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;
}

Bacula Plugins的更多相关文章

  1. bacula备份工具

    源码下载:http://www.bacula.org bacula适合数据业务量巨大,每天都在迅速增长,还需要以tar打包方式进行低级备份而且没有异地容灾策略.Bacula是一个完美的增量备份功能,同 ...

  2. CentOS7安装配置Bacula yum方法

    参考: https://www.baidu.com/link?url=o2QIy2YZWjsJPAFJuYFhrH3nPvtyRkSe-o5Q_FqFZ5E1EMOsIOmGeKm0HAonwHOw8 ...

  3. mybatis plugins实现项目【全局】读写分离

    在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...

  4. Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP

    回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...

  5. Lind.DDD.Plugins~插件模式的集成

    回到目录 对于Lind.DDD这个敏捷框架来说,插件也是其中的一个亮点,所有被认为是插件(Plugins)的模块都会继承自IPlugins这个标示接口,它在程序启动时会找到所有插件,并通过autofa ...

  6. 部署Qt程序时plugins相关问题

    部署qt程序时,经常涉及到Qt5.5.0\5.5\msvc2013\plugins目录下的一些动态链接库 例如数据库sqldrivers,操作系统类型platforms,读取各种图片imageform ...

  7. Maven Plugins常用配置

    官方文档:http://maven.apache.org/plugins/index.html# 这里主要介绍compiler插件的配置.http://maven.apache.org/plugins ...

  8. Linux No volume control GStreamer plugins and/or devices found

    案例环境:Oracle Linux Server release 5.7 进入Oracle Linux系统后,在右上角点击声音图标时,则会弹出如下报错窗口: The volume control di ...

  9. maven install Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1.1:war (default-war) on project web_nanchang

    maven打包成war时,报错:Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1.1:war (default- ...

随机推荐

  1. Houdini设置

    关闭网络编辑器动画 Animate network changes 平移时保持视口枢轴 Maintain Viewport Pivot when Panning 通知帮助提示将出现在Houdini的状 ...

  2. 7.Java基础_Java数据输入

    import java.util.Scanner; public class Output { public static void main(String[] args){ Scanner sc=n ...

  3. html学习之二(常用标签练习)

    <!DOCTYPE html><head> <meta charset="utf-8"> <title>锚点链接</title ...

  4. Pwnable-bof

    Download : http://pwnable.kr/bin/bof Download : http://pwnable.kr/bin/bof.c 下载之后,先看看c源码 #include < ...

  5. jTopo介绍(一)

    jTopo(Javascript Topology library)是一款完全基于HTML5 Canvas的关系.拓扑图形化界面开发工具包.jTopo关注于数据的图形展示,它是面向开发人员的,需要进行 ...

  6. 【声明式事务】Spring事务介绍(一)

    事务管理对于企业应用来说是至关重要的,当出现异常情况时,它也可以保证数据的一致性. Spring事务有两种管理方式:编程式事务和声明式事务 编程式事务使用TransactionTemplate或者直接 ...

  7. 01_javaSE面试题:自增变量

    开启刷面试题,都是根据视频进行刷面试题的 自增变量,只要记住两点 i++ 是先赋值后计算 ++i 是先计算后赋值 看下面的代码 int i = 1; i = i++; int j = i++; int ...

  8. 剑指Offer-5.用两个栈实现队列(C++/Java)

    题目: 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 分析: 栈的特点是先进后出,队列的特点则是先进先出. 题目要求我们用两个栈来实现一个队列,栈和队列都有入栈 ...

  9. java程序:调用百度api进行情感分析

    源码: package com.jeson.ceshi; import java.util.HashMap; import org.json.JSONObject; import com.baidu. ...

  10. Koa 中间件的执行顺序

    中间件工作原理 初始化koa实例后,我们会用use方法来加载中间件(middleware),会有一个数组来存储中间件,use调用顺序会决定中间件的执行顺序. 每个中间件都是一个函数(不是函数将报错), ...