2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation, which is
11 listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main program to test loading and running Bacula plugins.
30 * Destined to become Bacula pluginloader, ...
32 * Kern Sibbald, October 2007
37 const int dbglvl = 50;
39 const char *plugin_type = "-fd.dll";
41 const char *plugin_type = "-fd.so";
44 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
46 /* Function pointers to be set here */
47 extern DLL_IMP_EXP int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode);
48 extern DLL_IMP_EXP int (*plugin_bclose)(JCR *jcr);
49 extern DLL_IMP_EXP ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count);
50 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count);
51 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence);
54 /* Forward referenced functions */
55 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
56 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
57 static bRC baculaRegisterEvents(bpContext *ctx, ...);
58 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
59 int type, time_t mtime, const char *fmt, ...);
60 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
61 int level, const char *fmt, ...);
63 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode);
64 static int my_plugin_bclose(JCR *jcr);
65 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count);
66 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count);
67 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence);
71 static bInfo binfo = {
73 FD_PLUGIN_INTERFACE_VERSION
76 /* Bacula entry points */
77 static bFuncs bfuncs = {
79 FD_PLUGIN_INTERFACE_VERSION,
89 * Create a plugin event
91 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
97 if (!plugin_list || !jcr->plugin_ctx_list) {
98 return; /* Return if no plugins loaded */
101 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
102 event.eventType = eventType;
104 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
106 /* Pass event to every plugin */
107 foreach_alist(plugin, plugin_list) {
109 rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
119 * Sequence of calls for a backup:
120 * 1. plugin_save() here is called with ff_pkt
121 * 2. we find the plugin requested on the command string
122 * 3. we generate a bEventBackupCommand event to the specified plugin
123 * and pass it the command string.
124 * 4. we make a startPluginBackup call to the plugin, which gives
125 * us the data we need in save_pkt
126 * 5. we call Bacula's save_file() subroutine to save the specified
127 * file. The plugin will be called at pluginIO() to supply the
130 * Sequence of calls for restore:
131 * See subroutine plugin_name_stream() below.
133 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
139 char *cmd = ff_pkt->top_fname;
143 if (!plugin_list || !jcr->plugin_ctx_list) {
144 return 1; /* Return if no plugins loaded */
147 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
148 event.eventType = bEventBackupCommand;
150 /* Handle plugin command here backup */
151 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
152 if (!(p = strchr(cmd, ':'))) {
153 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
161 foreach_alist(plugin, plugin_list) {
162 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
163 if (strncmp(plugin->file, cmd, len) != 0) {
167 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
168 /* Send the backup command */
169 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, cmd) != bRC_OK) {
172 /* Loop getting filenames to backup then saving them */
173 while (!job_canceled(jcr)) {
174 memset(&sp, 0, sizeof(sp));
178 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
180 /* Get the file save parameters */
181 if (plug_func(plugin)->startBackupFile(&plugin_ctx_list[i], &sp) != bRC_OK) {
184 jcr->plugin_ctx = &plugin_ctx_list[i];
185 jcr->plugin = plugin;
186 jcr->plugin_sp = &sp;
188 ff_pkt->fname = sp.fname;
189 ff_pkt->type = sp.type;
190 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
191 Dmsg1(dbglvl, "Save_file: file=%s\n", ff_pkt->fname);
192 save_file(jcr, ff_pkt, true);
193 if (plug_func(plugin)->endBackupFile(&plugin_ctx_list[i]) != bRC_More) {
198 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
205 * Send plugin name start/end record to SD
207 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
210 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
212 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
213 /* Send stream header */
214 if (!sd->fsend("%ld %d 0", jcr->JobFiles+1, STREAM_PLUGIN_NAME)) {
215 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
219 Dmsg1(000, "send: %s\n", sd->msg);
222 /* Send data -- not much */
223 stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
225 /* Send end of data */
226 stat = sd->fsend("0 0");
229 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
233 Dmsg1(dbglvl, "send: %s\n", sd->msg);
234 sd->signal(BNET_EOD); /* indicate end of plugin name data */
239 * Plugin name stream found during restore. The record passed in
240 * argument name was generated in send_plugin_name() above.
242 void plugin_name_stream(JCR *jcr, char *name)
246 bool start, portable;
250 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
251 if (!plugin_ctx_list) {
255 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
256 skip_nonspaces(&p); /* skip over jcr->JobFiles */
260 /* Start of plugin data */
261 skip_nonspaces(&p); /* skip start/end flag */
263 portable = *p == '1';
264 skip_nonspaces(&p); /* skip portable flag */
269 * End of plugin data, notify plugin, then clear flags
271 plugin = (Plugin *)jcr->plugin;
272 plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
273 jcr->plugin_ctx = NULL;
279 * After this point, we are dealing with a restore start
282 Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
283 if (!(p = strchr(cmd, ':'))) {
284 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
293 * Search for correct plugin as specified on the command
295 foreach_alist(plugin, plugin_list) {
297 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
298 if (strncmp(plugin->file, cmd, len) != 0) {
302 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
303 event.eventType = bEventRestoreCommand;
304 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
305 &event, cmd) != bRC_OK) {
308 jcr->plugin_ctx = &plugin_ctx_list[i];
309 jcr->plugin = plugin;
317 * Tell the plugin to create the file. Return values are
320 * CF_SKIP -- skip processing this file
321 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
322 * CF_CREATED -- created, but no content to extract (typically directories)
325 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
327 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
328 Plugin *plugin = (Plugin *)jcr->plugin;
329 struct restore_pkt rp;
332 if (!set_cmd_plugin(bfd, jcr)) {
335 rp.stream = attr->stream;
336 rp.data_stream = attr->data_stream;
337 rp.type = attr->type;
338 rp.file_index = attr->file_index;
339 rp.LinkFI = attr->LinkFI;
341 rp.statp = attr->statp; /* structure assignment */
342 rp.attrEx = attr->attrEx;
343 rp.ofname = attr->ofname;
344 rp.olname = attr->olname;
345 rp.where = jcr->where;
346 rp.RegexWhere = jcr->RegexWhere;
347 rp.replace = jcr->replace;
348 if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
354 io.mode = 0777 & attr->statp.st_mode;
356 if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
363 * Reset the file attributes after all file I/O is done -- this allows
364 * the previous access time/dates to be set properly, and it also allows
365 * us to properly set directory permissions.
367 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
373 * This entry point is called internally by Bacula to ensure
374 * that the plugin IO calls come into this code.
376 void load_fd_plugins(const char *plugin_dir)
381 Dmsg0(dbglvl, "plugin dir is NULL\n");
385 plugin_list = New(alist(10, not_owned_by_alist));
386 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
387 /* Either none found, or some error */
388 if (plugin_list->size() == 0) {
391 Dmsg0(dbglvl, "No plugins loaded\n");
396 /* Plug entry points called from findlib */
397 plugin_bopen = my_plugin_bopen;
398 plugin_bclose = my_plugin_bclose;
399 plugin_bread = my_plugin_bread;
400 plugin_bwrite = my_plugin_bwrite;
401 plugin_blseek = my_plugin_blseek;
402 foreach_alist(plugin, plugin_list) {
403 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
404 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
411 * Create a new instance of each plugin for this Job
412 * Note, plugin_list can exist but jcr->plugin_ctx_list can
413 * be NULL if no plugins were loaded.
415 void new_plugins(JCR *jcr)
421 Dmsg0(dbglvl, "plugin list is NULL\n");
425 int num = plugin_list->size();
428 Dmsg0(dbglvl, "No plugins loaded\n");
432 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
434 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
435 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
436 foreach_alist(plugin, plugin_list) {
437 /* Start a new instance of each plugin */
438 plugin_ctx_list[i].bContext = (void *)jcr;
439 plugin_ctx_list[i].pContext = NULL;
440 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
445 * Free the plugin instances for this Job
447 void free_plugins(JCR *jcr)
452 if (!plugin_list || !jcr->plugin_ctx_list) {
453 return; /* no plugins, nothing to do */
456 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
457 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
458 foreach_alist(plugin, plugin_list) {
459 /* Free the plugin instance */
460 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
462 free(plugin_ctx_list);
463 jcr->plugin_ctx_list = NULL;
466 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
468 Plugin *plugin = (Plugin *)jcr->plugin;
469 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
471 Dmsg0(dbglvl, "plugin_bopen\n");
477 plug_func(plugin)->pluginIO(plugin_ctx, &io);
481 static int my_plugin_bclose(JCR *jcr)
483 Plugin *plugin = (Plugin *)jcr->plugin;
484 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
486 Dmsg0(dbglvl, "plugin_bclose\n");
490 plug_func(plugin)->pluginIO(plugin_ctx, &io);
494 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
496 Plugin *plugin = (Plugin *)jcr->plugin;
497 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
499 Dmsg0(dbglvl, "plugin_bread\n");
502 io.buf = (char *)buf;
503 plug_func(plugin)->pluginIO(plugin_ctx, &io);
504 return (ssize_t)io.status;
507 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
509 Plugin *plugin = (Plugin *)jcr->plugin;
510 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
512 Dmsg0(dbglvl, "plugin_bwrite\n");
515 io.buf = (char *)buf;
516 plug_func(plugin)->pluginIO(plugin_ctx, &io);
517 return (ssize_t)io.status;
520 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
522 Plugin *plugin = (Plugin *)jcr->plugin;
523 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
525 Dmsg0(dbglvl, "plugin_bseek\n");
529 plug_func(plugin)->pluginIO(plugin_ctx, &io);
530 return (boffset_t)io.offset;
533 /* ==============================================================
535 * Callbacks from the plugin
537 * ==============================================================
539 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
541 JCR *jcr = (JCR *)(ctx->bContext);
542 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
546 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
549 *((int *)value) = jcr->JobId;
550 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
553 *((char **)value) = my_name;
554 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
567 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
569 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
573 static bRC baculaRegisterEvents(bpContext *ctx, ...)
579 while ((event = va_arg(args, uint32_t))) {
580 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
586 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
587 int type, time_t mtime, const char *fmt, ...)
591 JCR *jcr = (JCR *)(ctx->bContext);
593 va_start(arg_ptr, fmt);
594 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
596 Jmsg(jcr, type, mtime, "%s", buf);
600 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
601 int level, const char *fmt, ...)
606 va_start(arg_ptr, fmt);
607 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
609 d_msg(file, line, level, "%s", buf);
615 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
616 int (*plugin_bclose)(JCR *jcr) = NULL;
617 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
618 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
619 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
621 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
626 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
631 int main(int argc, char *argv[])
633 char plugin_dir[1000];
638 strcpy(my_name, "test-fd");
640 getcwd(plugin_dir, sizeof(plugin_dir)-1);
641 load_fd_plugins(plugin_dir);
649 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
650 generate_plugin_event(jcr1, bEventJobEnd);
651 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
653 generate_plugin_event(jcr2, bEventJobEnd);
658 Dmsg0(dbglvl, "bacula: OK ...\n");
664 #endif /* TEST_PROGRAM */