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\n", 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\n");

   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\n");

   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\n", 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\n", (char *)value);
      break;
   case bEventJobStart:
        bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: bEventJobStart=%s\n", (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\n", (char *)value);
      p_ctx->cmd = strdup((char *)value);
      bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: cmd=%c\n", *p_ctx->cmd);
      p = strchr(p_ctx->cmd, ':');
      if (!p) {
         bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Plugin terminator not found: %s\n", (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\n", *p_ctx->fname);
      p = strchr(p, ':');
      if (!p) {
         bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "File terminator not found: %s\n", (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\n", *p_ctx->reader);
      p = strchr(p, ':');
      if (!p) {
         bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Reader terminator not found: %s\n", (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\n", *p_ctx->writer);
      break;

   default:
    bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: unknown event\n");
//  bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "bpipe-fd: unknown event%s\n");      //一点小小的语法错误,运行才发现出来
      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\n", 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\n");
   /*
    * 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\n", 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\n", 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\n",
             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\n", 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\n", 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\n", 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 \n", io->count);
      if (!p_ctx->fd) {
         bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL read FD\n");
         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\n", 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\n", 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\n");
         return bRC_Error;
      }
    printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d\n", 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\n");
         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\n");
         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)\n",
                            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\n", io->status);
//         bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing stream for pseudo file %s: exit %d\n", 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.\n");
   }
   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;
}
posted @ 2015-05-22 11:30  thomas_blog  阅读(153)  评论(0编辑  收藏  举报