• Bacula Plugins


    1. loadPlugin

    插件通过加载动态库loadPlugin函数开始,此函数包括bacula的回调和Plugin的注册

    bacula的回调

    typedef struct s_baculaFuncs {
       uint32_t size;
       uint32_t version;
       bRC (*registerBaculaEvents)(bpContext *ctx, ...);
       bRC (*getBaculaValue)(bpContext *ctx, bVariable var, void *value);
       bRC (*setBaculaValue)(bpContext *ctx, bVariable var, void *value);
       bRC (*JobMessage)(bpContext *ctx, const char *file, int line,
           int type, utime_t mtime, const char *fmt, ...);
       bRC (*DebugMessage)(bpContext *ctx, const char *file, int line,
           int level, const char *fmt, ...);
       void *(*baculaMalloc)(bpContext *ctx, const char *file, int line,
           size_t size);
       void (*baculaFree)(bpContext *ctx, const char *file, int line, void *mem);
       bRC (*AddExclude)(bpContext *ctx, const char *file);
       bRC (*AddInclude)(bpContext *ctx, const char *file);
       bRC (*AddOptions)(bpContext *ctx, const char *opts);
       bRC (*AddRegex)(bpContext *ctx, const char *item, int type);
       bRC (*AddWild)(bpContext *ctx, const char *item, int type);
       bRC (*NewOptions)(bpContext *ctx);
       bRC (*NewInclude)(bpContext *ctx);
       bRC (*NewPreInclude)(bpContext *ctx);
       bRC (*checkChanges)(bpContext *ctx, struct save_pkt *sp);
       bRC (*AcceptFile)(bpContext *ctx, struct save_pkt *sp); /* Need fname and statp */
    } bFuncs;
    
    bfuncs->getBaculaValue(ctx, bVarJobId, (void *)&JobId);     //获取相应插件的一些信息
    bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: JobId %d
    ", JobId);  //打印到窗口

    详细参考:手册或官网

    fd Plugin

    sd Plugin或dir Plugin各不同

    typedef struct s_pluginFuncs {
       uint32_t size;
       uint32_t version;
       bRC (*newPlugin)(bpContext *ctx);
       bRC (*freePlugin)(bpContext *ctx);
       bRC (*getPluginValue)(bpContext *ctx, pVariable var, void *value);
       bRC (*setPluginValue)(bpContext *ctx, pVariable var, void *value);
       bRC (*handlePluginEvent)(bpContext *ctx, bEvent *event, void *value);
       bRC (*startBackupFile)(bpContext *ctx, struct save_pkt *sp);
       bRC (*endBackupFile)(bpContext *ctx);
       bRC (*startRestoreFile)(bpContext *ctx, const char *cmd);
       bRC (*endRestoreFile)(bpContext *ctx);
       bRC (*pluginIO)(bpContext *ctx, struct io_pkt *io);
       bRC (*createFile)(bpContext *ctx, struct restore_pkt *rp);
       bRC (*setFileAttributes)(bpContext *ctx, struct restore_pkt *rp);
       bRC (*checkFile)(bpContext *ctx, char *fname);
    } pFuncs;

    注册到bacula之后,bacula会按着不同的需求去回调

    2. 插件配置

    bpipe-dir.so

    以下这种方式,bacula会自动加载bpipe-dir.so

    //默认加载路径配置
    # bacula-fd.conf
    FileDaemon {
        Plugin Directory = /usr/local/lib/
    }  
    //bpipe配置
    # vi bacula-dir.conf
    FileSet {
        Name = "Full Set"
        Include {
            Options {
                signature = MD5    
                Plugin = "bpipe:..."
            }
            File = /home
        } 
    }

    注:

    • Plugin如果写到Include,则备份失败
    • bpipe不能写成bpipe-fd

    此时,bacula会默认先去备份/home目录下的所有内容

    bpipe-fd.so

    FileSet {
        Name = "Full Set"
        Include {
            Options {
                signature = MD5    
            }
            Plugin = "bpipe:fuse.c:ls:cat >"
        } 
    }

    bpipe:插件名字,也就是bpipe-fd.so动态库
    fuse.c:备份时,存储的名字。恢复时候用到
    ls:备份时,会被打开。可以使管道、文件等,只要是流即可
    cat:还原时,会被打开
    注:

    • Plugin如果写到Options,则不会备份任何数据
    • 不管是重新编译.so,还是修改了.conf,都需要重新启动服务

    此时,fuse.c就是需要备份的文件。reader是pipe-readr,writer是pipe-writer

    3. 备份流程

    //handlePluginEvent()
    
    typedef enum {
      bEventJobStart                        = 1,
      bEventJobEnd                          = 2,
      bEventStartBackupJob                  = 3,
      bEventEndBackupJob                    = 4,
      bEventStartRestoreJob                 = 5,
      bEventEndRestoreJob                   = 6,
      bEventStartVerifyJob                  = 7,
      bEventEndVerifyJob                    = 8,
      bEventBackupCommand                   = 9,
      bEventRestoreCommand                  = 10,
      bEventEstimateCommand                 = 11,
      bEventLevel                           = 12,
      bEventSince                           = 13,
      bEventCancelCommand                   = 14, /* Executed by another thread */
      bEventVssBackupAddComponents          = 15, /* Just before bEventVssPrepareSnapshot */
      bEventVssRestoreLoadComponentMetadata = 16,
      bEventVssRestoreSetComponentsSelected = 17,
      bEventRestoreObject                   = 18,
      bEventEndFileSet                      = 19,
      bEventPluginCommand                   = 20, /* Sent during FileSet creation */
      bEventVssBeforeCloseRestore           = 21,
      bEventVssPrepareSnapshot              = 22,
      bEventOptionPlugin                    = 23,
      bEventHandleBackupFile                = 24, /* Used with Options Plugin */
      bEventComponentInfo                   = 25  /* Plugin component */
    } bEventType;

    如果配置的是上述bpipe-fd.so插件

    • 则bacula回调流程handlePluginEvent()
      1 –> 12 –> 20 –> 19 –> 3 –> 9 –> 4 –> 2
    • bEventBackupCommand()很重要
      它会读取Plugin的参数信息bpipe:之后的内容
    • 一切都配置完成后,在3 –> 4之间,也会有I/O读写操作
    //pluginIO()
    
    enum {
       IO_OPEN = 1,
       IO_READ = 2,
       IO_WRITE = 3,
       IO_CLOSE = 4,
       IO_SEEK = 5
    };

    备份的话,流程就是 1 –> 2 –> 4,如果一次读不完,则有多次2

    详细参考:main手册

    4. 实例

    bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs)
    {
       bfuncs = lbfuncs;                  /* set Bacula funct pointers */
       binfo  = lbinfo;
       *pinfo  = &pluginInfo;             /* return pointer to our info */
       *pfuncs = &pluginFuncs;            /* return pointer to our functions */
    
       return bRC_OK;
    }
    
    bRC unloadPlugin()
    {
       return bRC_OK;
    }
    
    static bRC newPlugin(bpContext *ctx)
    {
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: newPlugin
    ");
    
       struct plugin_ctx *p_ctx = (struct plugin_ctx *)malloc(sizeof(struct plugin_ctx));
       if (!p_ctx) {
          return bRC_Error;
       }
       memset(p_ctx, 0, sizeof(struct plugin_ctx));
       ctx->pContext = (void *)p_ctx;        /* set our context pointer */
       return bRC_OK;
    }
    
    static bRC freePlugin(bpContext *ctx)
    {
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: freePlugin
    ");
    
       struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
       if (!p_ctx) {
          return bRC_Error;
       }
       if (p_ctx->cmd) {
          free(p_ctx->cmd);                  /* free any allocated command string */
       }
       free(p_ctx);                          /* free our private context */
       p_ctx = NULL;
       return bRC_OK;
    }
    
    static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
    {
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: handlePluginEvent %d
    ", event->eventType);
    
       struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
       if (!p_ctx) {
          return bRC_Error;
       }
    
       switch (event->eventType) {
       case bEventPluginCommand:
          bfuncs->DebugMessage(ctx, fi, li, dbglvl,
                               "bpipe-fd: PluginCommand=%s
    ", (char *)value);
          break;
       case bEventJobStart:
            bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: bEventJobStart=%s
    ", (char *)value); 
            break;
       case bEventJobEnd:
          break;
       case bEventStartBackupJob:
          break;
       case bEventEndBackupJob:
          break;
       case bEventLevel:
          break;
       case bEventSince:
          break;
       case bEventStartRestoreJob:
          break;
       case bEventEndRestoreJob:
          break;
       case bEventRestoreCommand:
       case bEventEstimateCommand:
       case bEventBackupCommand:
          char *p;
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: value=%s
    ", (char *)value);
          p_ctx->cmd = strdup((char *)value);
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: cmd=%c
    ", *p_ctx->cmd);
          p = strchr(p_ctx->cmd, ':');
          if (!p) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Plugin terminator not found: %s
    ", (char *)value);
             return bRC_Error;
          }
          *p++ = 0;           /* terminate plugin */
          p_ctx->fname = p;
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: fname=%c
    ", *p_ctx->fname);
          p = strchr(p, ':');
          if (!p) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "File terminator not found: %s
    ", (char *)value);
             return bRC_Error;
          }
          *p++ = 0;           /* terminate file */
          p_ctx->reader = p;
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: reader=%c
    ", *p_ctx->reader);
          p = strchr(p, ':');
          if (!p) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Reader terminator not found: %s
    ", (char *)value);
             return bRC_Error;
          }
          *p++ = 0;           /* terminate reader string */
          p_ctx->writer = p;
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: writer=%c
    ", *p_ctx->writer);
          break;
    
       default:
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: unknown event
    ");
    //  bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: unknown event%s
    ");      //一点小小的语法错误,运行才发现出来
          break;
       }
       return bRC_OK;
    }
    
    static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
    {
       struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
       if (!p_ctx) {
          return bRC_Error;
       }
    
       bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: startBackupFile%s
    ", p_ctx->fname);
    
       time_t now = time(NULL);
       sp->fname = p_ctx->fname;
       sp->type = FT_REG;
       sp->statp.st_mode = 0700 | S_IFREG;
       sp->statp.st_ctime = now;
       sp->statp.st_mtime = now;
       sp->statp.st_atime = now;
       sp->statp.st_size = -1;
       sp->statp.st_blksize = 4096;
       sp->statp.st_blocks = 1;
       p_ctx->backup = true;
    
       return bRC_OK;
    }
    
    static bRC endBackupFile(bpContext *ctx)
    {
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: endBackupFile
    ");
       /*
        * We would return bRC_More if we wanted startBackupFile to be
        * called again to backup another file
        */
       return bRC_OK;
    }
    
    static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
    {
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: pluginIO %d
    ", io->func);
    
       struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
       if (!p_ctx) {
          return bRC_Error;
       }
    
       io->status = 0;
       io->io_errno = 0;
       switch(io->func) {
       case IO_OPEN:        //需要的是fname 其他为空
          bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: fname %s
    ", io->fname);
          if (io->flags & (O_CREAT | O_WRONLY)) {
             char *writer_codes = apply_rp_codes(p_ctx);
             p_ctx->fd = popen(writer_codes, "w");
             bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%d writer=%s
    ",
                 p_ctx->fd, writer_codes);
             if (!p_ctx->fd) {
                io->io_errno = errno;
                bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
                   "Open pipe writer=%s failed: ERR=%s
    ", writer_codes, strerror(errno));
                if (writer_codes) {
                   free(writer_codes);
                }
                return bRC_Error;
             }
             if (writer_codes) {
                free(writer_codes);
             }
          } else {
             p_ctx->fd = popen(p_ctx->reader, "r");
             bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: IO_OPEN fd=%p reader=%s
    ", p_ctx->fd, p_ctx->reader);
             if (!p_ctx->fd) {
                io->io_errno = errno;
                bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
                   "Open pipe reader=%s failed: ERR=%s
    ", p_ctx->reader, strerror(errno));
                return bRC_Error;
             }
          }
          sleep(1);                 /* let pipe connect */
          break;
    
       case IO_READ:    //需要的是count 其他为空
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: count %d 
    ", io->count);
          if (!p_ctx->fd) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL read FD
    ");
             return bRC_Error;
          }
          io->status = fread(io->buf, 1, io->count, p_ctx->fd);
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: IO_READ buf=%p len=%d
    ", io->buf, io->status);
          if (io->status == 0 && ferror(p_ctx->fd)) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
                "Pipe read error: ERR=%s
    ", strerror(errno));
             return bRC_Error;
          }
          break;
    
       case IO_WRITE:
          if (!p_ctx->fd) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL write FD
    ");
             return bRC_Error;
          }
        printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d
    ", p_ctx->fd, io->buf, io->count);
          io->status = fwrite(io->buf, 1, io->count, p_ctx->fd);
          if (io->status == 0 && ferror(p_ctx->fd)) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
                "Pipe write error
    ");
             return bRC_Error;
          }
          break;
    
       case IO_CLOSE:       //什么都不需要,直接关闭
          if (!p_ctx->fd) {
             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL FD on bpipe close
    ");
             return bRC_Error;
          }
          io->status = pclose(p_ctx->fd);
    
          /* Problem during execution */
          if (io->status < 0) {
             io->io_errno = errno;
             bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing stream for pseudo file %s: %d (%s)
    ",
                                p_ctx->fname, io->status, strerror(errno));
    
          /* Problem inside the subprogram */
          } else if (io->status > 0) {
             int status=1;
             if (WIFEXITED(io->status)) {    /* process exit()ed */
                status = WEXITSTATUS(io->status);
    
             } else if (WIFSIGNALED(io->status)) {  /* process died */
    #ifndef HAVE_WIN32
                status = WTERMSIG(io->status);
    #endif
             }
             bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: exit=%d
    ", io->status);
    //         bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing stream for pseudo file %s: exit %d
    ", p_ctx->fname, status);
          }
          break;
    
       case IO_SEEK:
          io->offset = p_ctx->offset;
          break;
       }
       return bRC_OK;
    }
    
    static bRC startRestoreFile(bpContext *ctx, const char *cmd)
    {
       return bRC_OK;
    }
    
    static bRC endRestoreFile(bpContext *ctx)
    {
       return bRC_OK;
    }
    
    /*
     * This is called during restore to create the file (if necessary)
     * We must return in rp->create_status:
     *
     *  CF_ERROR    -- error
     *  CF_SKIP     -- skip processing this file
     *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
     *  CF_CREATED  -- created, but no content to extract (typically directories)
     *
     */
    static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
    {
       if (strlen(rp->where) > 512) {
          printf("Restore target dir too long. Restricting to first 512 bytes.
    ");
       }
       strncpy(((struct plugin_ctx *)ctx->pContext)->where, rp->where, 513);
       ((struct plugin_ctx *)ctx->pContext)->replace = rp->replace;
       rp->create_status = CF_EXTRACT;
       return bRC_OK;
    }
    
    /*
     * We will get here if the File is a directory after everything
     * is written in the directory.
     */
    static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
    {
       return bRC_OK;
    }
    
    /* When using Incremental dump, all previous dumps are necessary */
    static bRC checkFile(bpContext *ctx, char *fname)
    {
       return bRC_OK;
    }
    
  • 相关阅读:
    Linux程序分析工具介绍—ldd,nm
    Makefile学习(三)[第二版]
    Linux下的tree命令 --Linux下目录树查看
    Makefile学习(二)[第二版]
    Makefile学习(一)[第二版]
    Linux下top命令详解
    Shell编程入门(第二版)(下)
    mysql用命令行导入sql文件
    javascript的onbeforeunload函数在IOS上运行
    mysql如何利用Navicat 导出和导入数据库
  • 原文地址:https://www.cnblogs.com/zhangxuechao/p/11709967.html
Copyright © 2020-2023  润新知