bacula的插件里面有以下几个函数
newPlugin(bpContext *ctx)bool checkFile(bpContext *ctx, char *fname)
同样的,它有几个入口点
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)
这些都是在Bpluginfo.c里面定义的。
插件是以动态共享对象程序或者dll(win32)来实现的,在bacula-fd.conf里面会有相关配置,当fd启动时,它会加载你配置的目录里面的-fd.so或者-fd.dll(win32)
一般情况下,插件可以通过3种方式被调用:第一种,当一个特定的事件在bacula里面被探知到,它将会轮流的将事件传递给已经加载的插件,这和脚本运行很相似,事件也很相似。一旦插件获得控制权,它就会和通过获取以及设置bacula变量和bacula进行交互。这样,他就和脚本很相似了。目前定义的bacula变量很少,但是在需要的时候就会被执行,很具有扩展性。
可以通过注册来使插件接收一般情况下接收不到的事件,比如说检查每个文件来进行备份还原。但是目前还没实现。
第二种插件是很有用并且在我们当前版本已经实现了,它叫命令插件。和别的插件一样,他会被统治像上面描述的哪些重要事件,除此之外这种类型的插件可以接受命令行——
Plugin =
这个命令会定义一个文件集目录,和“file= ”的功能很像(ps:这一段感觉没翻译好),当在bacula备份的时候遇到这条指令,它将会把这条命令里面的command传递给插件。这样插件就可以备份系统上面的任何文件或者文件夹。也可以在目录里面创建包含被恢复的数据的虚拟文件夹,并且没必要对应于系统上面的实际文件夹———ps:不太懂啊!
第三种是选择插件,这种插件对于实现数据的自定义过滤很有效。比如,你可以通过一个很简单的方法来实现一个压缩算法。bacula会为每个在fileset(according to Wild/Regex/Exclude/Include rules)被选择的文件来调用插件。和所有的插件一样,它会像上面一样被通知事件,除此之外,这种插件可以被放在一个选择组里面,像下面这样:
FileSet {
Name = TestFS
Include {
Options {
Compression = GZIP1
Signature = MD5
Wild = "*.txt"
Plugin =
}
File = /
}
}
Loading Plugins
当fd加载plugins的时候,它会向os ask两个入口点(oadPlugin and unloadPlugin),然后调用loadPlugin的两个入口点
bacula通过这个调用来传递信息给插件,也能通过这个方式来从插件获取信息用来使用插件。然后bacula将会调用loadPlugin接口定义的特定的函数。
当bacula要退出plugin的时候(也就是说bacula不需要使用plugin),会调用unloadPlugin。
这两个函数是
bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs)和bRC unloadPlugin()
它们虽然是C定义的,但是可以使用任何语言来编写插件,当然,最好是C或者C++
bRC和参数是在src/filed/fd-plugins.h里面定义的,再加上src/lib/plugins.h,基本上定义了所有的插件接口。在这个头文件里面,它包含了如下文件:
#include
#include "config.h"
#include "bc_types.h"
#include "lib/plugins.h"
#include
除了bc types.h and con fig.h插件定义是用了最小量的代码
bc_types.h是为了确保数据类型定义和bacula核心代码一致。
返回码定义如下:
typedef enum {
bRC_OK = 0, /* OK */
bRC_Stop = 1, /* Stop calling other plugins */
bRC_Error = 2, /* Some kind of error */
bRC_More = 3, /* More files to backup */
bRC_Term = 4, /* Unload me */
bRC_Seen = 5, /* Return code from checkFiles */
bRC_Core = 6, /* Let Bacula core handles this file */
bRC_Skip = 7, /* Skip the proposed file */
} bRC;
我们希望把bacula libbac.a整成一个共享对象以此来让插件使用更多的bacula的东西。但是for this first cut,我们尽量尝试着让它少一些的对于bacula的依赖
loadPlugin
loadPlugin在bacula的fd服务第一次启动并加载插件之后立即被调用。这个入口点在fd执行过程中只被调用一次。前两个参数是bacula传递给插件的信息,后两个参数是插件自身的需要传递给bacula的信息。原型如下:
BRC loadPlugin(bInfo * lbinfo,bFuncs * lbfuncs,PINFO ** PINFO,pFuncs ** pfuncs)
参数如下:
lbinfo
一般而言,这是关于bacula的信息,目前来说,只有bacula插件的接口版本的值在binfo结构体里面定义了,现在定义为1,size是结构体的字节大小,下面是binfo的定义
typedef struct s_baculaInfo {
uint32_t size;
uint32_t version;
} bInfo;
lbfuncs
bFuncs结构体在把bacula里面定义了回调函数入口点,插件可以使用register events, get Bacula values, set Bacula values, and send messages to the Job output or debug output,定义如下:
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);
} bFuncs;
pInfo
当loadPlugin被调用之后,插件必须初始化一个关于插件的信息结构体并且返回这个结构体的指针给bacula
typedef struct s_pluginInfo {
uint32_t size;
uint32_t version;
const char *plugin_magic;
const char *plugin_license;
const char *plugin_author;
const char *plugin_date;
const char *plugin_version;
const char *plugin_description;
} pInfo;
version 是当前版本定义的插件接口的版本,目前是1,当接口版本和当前的bacula版本不一样的时候,插件将不会工作(还没实现)
plugin_magic 是一个指向文本字符串“FDPluginData”的指针,这是一种健全性的检查,当值没有指定的时候,插件将不会运行
plugin_license 指向一个描述插件license的文本字符串的指针,bacula只接受相容的许可(没有实现)】
plugin_author 一个指向text名字的指针
plugin_version 指向包含插件版本的文本字符串的指针
plugin_description 指向插件描述的字符串的指针
pInfo结构体必须定义在静态存储区(因为bacula并没有复制它,并且在插件被加载之后可能随时参考它的值)。这里面所有的值都得有(还没实现),文本字符串得是ASCII或者UTF-8格式的。
pFuncs
loadPlugin被调用之后,插件必须初始化一个关于插件的入口点结构体,并且返回这个结构体的指针给bacula。这个结构体包含了插件必须给bacula提供的每个入口点的指针。当bacula正常运行插件之后,将会在特定的时间调用定义好的入口点。所有的入口点都得定义。
pFuncs结构体必须定义在静态存储区(如上)
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调用loadPlugin的时候这些子程序的指针将会在pFuncs结构体里面传递给bacula
newPlugin(bpContext *ctx)
创建一个新的插件instance的时候bacula会调用,一般在Job开始的时候调用,当10个Jobs同时运行的时候,插件里将会有10个instance。bpContext结构体会被传递给插件。如果插件需要有与该插件的特定实例相关联的私人工作存储区,应该从堆(malloc内存)来创建并存储一个指向其私有工作存储区的指针在pContext。注意:有余bacula是多进程程序,你不能让任何变量的数据存放在你的插件,除非它确实是为了全局用于整个插件。此外,你必须意识到,除了第一个和最后一个调用插件(loadPlugin和unloadPlugin)的所有其他线程调用将由对应于一个Bacula Job。bpContext将会保持不变,因此你可以保存你的私人Job的特殊数据在里面
typedef struct s_bpContext {
void *pContext; /* Plugin private context */
void *bContext; /* Bacula private context */
} bpContext;
这个context指针将作为第一个参数传递给所有Bacula调用插件内的入口点。不用说,插件不应改变bContext变量,这是Bacula的私人上下文指针这个插件的实例(工作)。
freePlugin(bpContext *ctx)
当instance不再需要的时候调用此接口,同时,插件需要释放这个instance分配的所有内存,这个unloadPlugin不一样,别的instance将会继续运行,如果其他的Jobs开始的时候newplugin也会被被再次调用。
getPluginValue(bpContext *ctx, pVariable var, void *value)
bacula会调用这个函数才获取插件的变量,目前还没被调用
setPluginValue(bpContext *ctx, pVariable var, void *value)
设置插件里的变量,目前没被调用
handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
当bacula遇到下述情况的时候调用此函数:实际上,功能是,当bacula运行时插件获取控制权,并且知道Job里面发生了什么。它可以被比作RunScript功能,调用外部程序和脚本,并非常类似于Bacula Python接口。插件被调用时,bacula把bEvent结构体的指针传给插件,这个结构体如下所示:
typedef struct s_bEvent {
uint32_t eventType;
} bEvent;
定义引起的事件,对于每一个event,Bacula将传递一个与该event相关联的指针值。如果没有与此event相关联的值,bacula传一个NULL指针,因此插件必须在解除相关性之前经常小心的检查变量指针。
目前events的列表如下:
typedef enum {
bEventJobStart = 1,
bEventJobEnd = 2,
bEventStartBackupJob = 3,
bEventEndBackupJob = 4,
bEventStartRestoreJob = 5,
bEventEndRestoreJob = 6,
bEventStartVerifyJob = 7,
bEventEndVerifyJob = 8,
bEventBackupCommand = 9,
bEventRestoreCommand = 10,
bEventLevel = 11,
bEventSince = 12,
bEventCancelCommand = 13, /* Executed by another thread */
/* Just before bEventVssPrepareSnapshot */
bEventVssBackupAddComponents = 14,
bEventVssRestoreLoadComponentMetadata = 15,
bEventVssRestoreSetComponentsSelected = 16,
bEventRestoreObject = 17,
bEventEndFileSet = 18,
bEventPluginCommand = 19,
bEventVssBeforeCloseRestore = 21,
/* Add drives to VSS snapshot
* argument: char[27] drivelist
* You need to add them without duplicates,
* see fd_common.h add_drive() copy_drives() to get help
*/
bEventVssPrepareSnapshot = 22,
bEventOptionPlugin = 23,
bEventHandleBackupFile = 24 /* Used with Options Plugin */
} bEventType;
bEventJobStart Job开始的时候调用,包含Jobid=nnn Job=job-name
bEventJobEnd Job End
bEventStartBackupJob backup开始
bEventEndBackupJob backup结束
bEventStartRestoreJob restore开始
bEventEndRestoreJob restore结束
bEventStartVerifyJob 验证工作开始
bEventEndVerifyJob 验证工作结束
bEventBackupCommand 在bEventStartBackupJob之前调用,传递command(也就是Plugin= 之后的)给插件。注意:如果你想备份文件,这是重要的第一点,复制命令字符串传递到你的pContext区域,这样你就会知道,执行备份时,如何备份和用户想要叫它什么
bEventRestoreCommand 在bEventStartRestoreJob之前调用,其余如上所述
bEventLevel 当level因为新的Job被设置时被调用,是一个32位整数,显示Job的level码
bEventSince 当since time因为新的Job被设置时被调用,是最后一个Job运行时的time_t time
bEventCancelCommand 正在运行的Job被取消的时候被调用。这个是由一个不同的线程发送的
bEventVssBackupAddComponents
bEventPluginCommand 在当前的Fileset调用,event只能传递给command里面指定的插件,参数是PluginCommand
bEventHandleBackupFile 当使用选择性插件的时候被文件集里面的每一个文件调用,如果插件返回CF_OK,它将用于备份,如果返回CF_SKIP,文件将被忽略。别的东西将会使用bacula核心功能来备份文件
startBackupFile(bpContext *ctx, struct save_pkt *sp)
只有你的插件是命令插件并且在遇到“Plugin = ”命令的时候才调用,在备份的时候调用。bacula提供了一个指向save_pkt结构体的指针,你必须填写这个文件的“属性”数据。
struct save_pkt {
int32_t pkt_size; /* size of this packet */
char *fname; /* Full path and filename */
char *link; /* Link name if any */
struct stat statp; /* System stat() packet for file */
int32_t type; /* FT_xx for this file */
uint32_t flags; /* Bacula internal flags */
bool portable; /* set if data format is portable */
char *cmd; /* command */
uint32_t delta_seq; /* Delta sequence number */
char *object_name; /* Object name to create */
char *object; /* restore object data to save */
int32_t object_len; /* restore object length */
int32_t index; /* restore object index */
int32_t pkt_end; /* end packet sentinel */
};
第二个参数是一个指向save_pkt结构体的指针。插件负责填写save_pkt所有字段。如果你备份一个真正的文件,那么一般来说,在进行一个stat系统调用的时候就能填写完statp结构体。
如果你要备份一个像数据库一样复杂的东西的时候,你就得创建一个虚拟的文件,这个文件不存在,但是它能呈现你所备份的东西。这样的话,你就得确保你传回的fname字符串是独一无二的,才不会和系统上面的实际的文件冲突,而且你得在statp里面自己创建。bpipe-fd.c等示例程序展示了如何设置这些字段。你必须注意不要存储指针到指针字段的stack区,比如说fname和link(ps:这一段不太通顺)。因为当你从函数返回,你堆栈条目将被摧毁。在这种情况下的解决方案是malloc()和返回malloc之后的指针。为了没有内存泄漏,你应该把所有分配的内存的指针存储在pContext结构,以便在后续调用或终止的时候,你可以释放它。
一旦备份开始,bacula将会调用pluginIO来读数据进行备份。参考bpipe-fd.c,如下
struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
time_t now = time(NULL);
sp->fname = p_ctx->fname;
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;
注意:需要创建的filename已经在之前传给插件的command里面创建了,在插件的p_ctx->fname里面是malloc出来的。
一般来说,处理从bacula到插件的“Plugin = ”命令的顺序是:
生成一个对应于指定插件的bEventBackupCommand事件并将command字符串给这个插件。
向插件发一个startPluginBackup的调用命令,which fills in the data needed in save_pkt to save as the file attributes and to put on the Volume and in the catalog。
调用bacula里面的save_file()子程序来保存指定的文件。然后插件的PluginIO将会被调用,然后开始read数据。如果是对虚拟文件进行的操作,这个open就只是插件内部的行为,没必要非得打开一个磁盘上的真实的文件。 在bpipe-fd.c的例子中,初始化一个管道,最终插件给bacula发一个信号说都读完了,然后bacula就会调用“close”pluginIO的函数。
endBackupFile(bpContext *ctx)
如果备份完成,得返回bRC_OK。如果插件想要创建一些别的文件并备份,就得返回bRc_More(还没有实现)。这是个释放你回传filenames用到的malloc的内存的比较好的时间???????????不懂!!!
startRestoreFile(bpContext *ctx, const char *cmd)
createFile(bpContext *ctx, struct restore_pkt *rp)
setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
endRestoreFile(bpContext *ctx)
pluginIO(bpContext *ctx, struct io_pkt *io)
命令插件用来在备份和还原的时候进行输入输出数据的。这些都是模仿unix的read,write,open,close,lseek一类的接口,参数都在数据包里面。另外对于Win32系统插件必须返回两个额外的值:
enum {
IO_OPEN = 1,
IO_READ = 2,
IO_WRITE = 3,
IO_CLOSE = 4,
IO_SEEK = 5
};
struct io_pkt {
int32_t pkt_size; /* Size of this packet */
int32_t func; /* Function code */
int32_t count; /* read/write count */
mode_t mode; /* permissions for created files */
int32_t flags; /* Open flags */
char *buf; /* read/write buffer */
const char *fname; /* open filename */
int32_t status; /* return status */
int32_t io_errno; /* errno code */
int32_t lerror; /* Win32 error code */
int32_t whence; /* lseek argument */
boffset_t offset; /* lseek argument */
bool win32; /* Win32 GetLastError returned */
int32_t pkt_end; /* end packet sentinel */
};
bool checkFile(bpContext *ctx, char *fname)
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)