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;
}
Bacula Plugins的更多相关文章
- bacula备份工具
源码下载:http://www.bacula.org bacula适合数据业务量巨大,每天都在迅速增长,还需要以tar打包方式进行低级备份而且没有异地容灾策略.Bacula是一个完美的增量备份功能,同 ...
- CentOS7安装配置Bacula yum方法
参考: https://www.baidu.com/link?url=o2QIy2YZWjsJPAFJuYFhrH3nPvtyRkSe-o5Q_FqFZ5E1EMOsIOmGeKm0HAonwHOw8 ...
- mybatis plugins实现项目【全局】读写分离
在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...
- Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP
回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...
- Lind.DDD.Plugins~插件模式的集成
回到目录 对于Lind.DDD这个敏捷框架来说,插件也是其中的一个亮点,所有被认为是插件(Plugins)的模块都会继承自IPlugins这个标示接口,它在程序启动时会找到所有插件,并通过autofa ...
- 部署Qt程序时plugins相关问题
部署qt程序时,经常涉及到Qt5.5.0\5.5\msvc2013\plugins目录下的一些动态链接库 例如数据库sqldrivers,操作系统类型platforms,读取各种图片imageform ...
- Maven Plugins常用配置
官方文档:http://maven.apache.org/plugins/index.html# 这里主要介绍compiler插件的配置.http://maven.apache.org/plugins ...
- Linux No volume control GStreamer plugins and/or devices found
案例环境:Oracle Linux Server release 5.7 进入Oracle Linux系统后,在右上角点击声音图标时,则会弹出如下报错窗口: The volume control di ...
- 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- ...
随机推荐
- ctr预估论文梳理和个人理解
问题描述 ctr的全称是click through rate,就是预估用户的点击率,可以用于推荐系统的ranking阶段.ctr预估可以理解为给用户的特征.item的特征以及context的特征(比如 ...
- Windows 跟 Linux 文件共享:Samba 设置
用 Samba 服务器 https://my.oschina.net/u/3783115/blog/1919892?from=timeline https://blog.51cto.com/1372 ...
- postman---postman参数关联
我们做接口测试的时候都会遇到一个场景,就是参数关联,所谓的参数关联就是上一个参数的返回值用于下一个参数的请求中,通过python中requests我们知道如何请求,那么通过postman如何请求? 参 ...
- Verilog语言框架
一.常用关键字
- win10python安装iis
django部署iis https://www.cnblogs.com/guangang/articles/9268644.html python部署iis https://www.cnblogs.c ...
- 2019 蓝桥杯国赛 B 组模拟赛 题解
标签 ok #include<bits/stdc++.h> using namespace std; /* 求阶乘 去除尾部0 每次求阶乘时:结果去除尾0,并对 1e6取余 */ type ...
- luoguP4113 [HEOI2012]采花
经典颜色问题推荐博文 https://www.cnblogs.com/tyner/p/11519506.html https://www.cnblogs.com/tyner/p/11616770.ht ...
- c# 第41节 异常处理
本节内容: 1:捕获异常两种方式 2:处理异常的两种方式 3:实例处理异常 4:手动触发异常 5:预定义异常类有哪些 6:自定义异常 1:捕获异常 第一种 捕获:只能知道发生了异常 第二种捕获: 可以 ...
- python time和datetime常用写法格式
python 的time和datetime常用写法 import time from datetime import datetime from datetime import timedelta # ...
- jQuery的配置。
在python中有提前定义模板的功能,所以提前将jQuery的导入语句导入就可以直接使用jQuery语法: 一.下载jQuery包. 下载官网: https://jquery.com/ 可下载迷你版的 ...