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