2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Main program to test loading and running Bacula plugins.
21 * Destined to become Bacula pluginloader, ...
23 * Kern Sibbald, October 2007
29 extern DLL_IMP_EXP char *exepath;
30 extern DLL_IMP_EXP char *version;
31 extern DLL_IMP_EXP char *dist_name;
33 const int dbglvl = 150;
35 const char *plugin_type = "-fd.dll";
37 const char *plugin_type = "-fd.so";
40 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
41 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
43 /* Function pointers to be set here */
44 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
45 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
46 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
47 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
48 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
51 /* Forward referenced functions */
52 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
53 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
54 static bRC baculaRegisterEvents(bpContext *ctx, ...);
55 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
56 int type, utime_t mtime, const char *fmt, ...);
57 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
58 int level, const char *fmt, ...);
59 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
61 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
62 static bRC baculaAddExclude(bpContext *ctx, const char *file);
63 static bRC baculaAddInclude(bpContext *ctx, const char *file);
64 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
65 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
66 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
67 static bRC baculaNewOptions(bpContext *ctx);
68 static bRC baculaNewInclude(bpContext *ctx);
69 static bRC baculaNewPreInclude(bpContext *ctx);
70 static bool is_plugin_compatible(Plugin *plugin);
71 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
72 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
73 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
76 * These will be plugged into the global pointer structure for
79 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
80 static int my_plugin_bclose(BFILE *bfd);
81 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
82 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
83 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
87 static bInfo binfo = {
89 FD_PLUGIN_INTERFACE_VERSION
92 /* Bacula entry points */
93 static bFuncs bfuncs = {
95 FD_PLUGIN_INTERFACE_VERSION,
116 * Bacula private context
119 JCR *jcr; /* jcr for plugin */
120 bRC rc; /* last return code */
121 bool disabled; /* set if plugin disabled */
122 findINCEXE *exclude; /* pointer to exclude files */
123 findINCEXE *include; /* pointer to include/exclude files */
127 * Test if event is for this plugin
129 static bool for_this_plugin(Plugin *plugin, char *name, int len)
131 Dmsg4(dbglvl, "name=%s len=%d plugin=%s plen=%d\n", name, len, plugin->file, plugin->file_len);
132 if (!name) { /* if no plugin name, all plugins get it */
135 /* Return global VSS job metadata to all plugins */
136 if (strcmp("job", name) == 0) { /* old V4.0 name for VSS job metadata */
139 if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
142 /* Check if this is the correct plugin */
143 if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
150 bool is_plugin_disabled(bpContext *plugin_ctx)
157 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
161 return b_ctx->disabled;
164 bool is_plugin_disabled(JCR *jcr)
166 return is_plugin_disabled(jcr->plugin_ctx);
170 * Create a plugin event When receiving bEventCancelCommand, this function is
171 * called by an other thread.
173 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
175 bpContext *plugin_ctx;
181 bool call_if_canceled = false;
182 restore_object_pkt *rop;
185 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list) {
186 return; /* Return if no plugins loaded */
190 * Some events are sent to only a particular plugin or must be
191 * called even if the job is canceled
194 case bEventPluginCommand:
195 case bEventOptionPlugin:
196 name = (char *)value;
197 if (!get_plugin_name(jcr, name, &len)) {
201 case bEventRestoreObject:
202 /* After all RestoreObject, we have it one more time with value=NULL */
204 /* Some RestoreObjects may not have a plugin name */
205 rop = (restore_object_pkt *)value;
206 if (*rop->plugin_name) {
207 name = rop->plugin_name;
208 get_plugin_name(jcr, name, &len);
213 case bEventEndBackupJob:
214 case bEventEndVerifyJob:
215 call_if_canceled = true; /* plugin *must* see this call */
217 case bEventStartRestoreJob:
218 foreach_alist_index(i, plugin, b_plugin_list) {
219 plugin->restoreFileStarted = false;
220 plugin->createFileCalled = false;
223 case bEventEndRestoreJob:
224 call_if_canceled = true; /* plugin *must* see this call */
230 /* If call_if_canceled is set, we call the plugin anyway */
231 if (!call_if_canceled && jcr->is_job_canceled()) {
235 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
236 event.eventType = eventType;
238 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
241 * Pass event to every plugin (except if name is set). If name
242 * is set, we pass it only to the plugin with that name.
244 foreach_alist_index(i, plugin, b_plugin_list) {
245 if (!for_this_plugin(plugin, name, len)) {
246 Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
247 name, name==NULL?1:0);
251 * Note, at this point do not change
252 * jcr->plugin or jcr->plugin_ctx
255 plugin_ctx = &plugin_ctx_list[i];
256 if (is_plugin_disabled(plugin_ctx)) {
257 Dmsg1(50, "Plugin %s disabled\n", plugin->file);
260 if (eventType == bEventEndRestoreJob) {
261 Dmsg0(50, "eventType==bEventEndRestoreJob\n");
262 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
263 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
266 jcr->plugin->restoreFileStarted = false;
267 jcr->plugin->createFileCalled = false;
270 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
276 * Check if file was seen for accurate
278 bool plugin_check_file(JCR *jcr, char *fname)
285 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
286 return false; /* Return if no plugins loaded */
289 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
291 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
293 /* Pass event to every plugin */
294 foreach_alist_index(i, plugin, b_plugin_list) {
295 jcr->plugin_ctx = &plugin_ctx_list[i];
296 jcr->plugin = plugin;
297 if (is_plugin_disabled(jcr)) {
300 if (plug_func(plugin)->checkFile == NULL) {
303 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
304 if (rc == bRC_Seen) {
311 jcr->plugin_ctx = NULL;
312 return rc == bRC_Seen;
315 /* Get the first part of the the plugin command
316 * systemstate:/@SYSTEMSTATE/
318 * => can use for_this_plugin(plug, cmd, ret);
320 * The plugin command can contain only the plugin name
324 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
329 if (!cmd || (*cmd == '\0')) {
332 /* Handle plugin command here backup */
333 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
334 if ((p = strchr(cmd, ':')) == NULL) {
335 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
338 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
341 } else { /* plugin:argument */
353 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
356 ff_pkt->no_read = sp->no_read;
357 ff_pkt->delta_seq = sp->delta_seq;
358 if (sp->flags & FO_DELTA) {
359 ff_pkt->flags |= FO_DELTA;
360 ff_pkt->delta_seq++; /* make new delta sequence number */
362 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
363 ff_pkt->delta_seq = 0;
366 if (sp->flags & FO_OFFSETS) {
367 ff_pkt->flags |= FO_OFFSETS;
369 ff_pkt->flags &= ~FO_OFFSETS;
371 /* Sparse code doesn't work with plugins
372 * that use FIFO or STDOUT/IN to communicate
374 if (sp->flags & FO_SPARSE) {
375 ff_pkt->flags |= FO_SPARSE;
377 ff_pkt->flags &= ~FO_SPARSE;
379 if (sp->flags & FO_PORTABLE_DATA) {
380 ff_pkt->flags |= FO_PORTABLE_DATA;
382 ff_pkt->flags &= ~FO_PORTABLE_DATA;
384 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
388 /* Ask to a Option Plugin what to do with the current file */
389 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
394 char *cmd = ff_pkt->plugin;
398 event.eventType = bEventHandleBackupFile;
401 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
402 memset(sp, 0, sizeof(struct save_pkt));
403 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
406 sp->link = ff_pkt->link;
407 sp->cmd = ff_pkt->plugin;
408 sp->statp = ff_pkt->statp;
409 sp->fname = ff_pkt->fname;
410 sp->delta_seq = ff_pkt->delta_seq;
411 sp->accurate_found = ff_pkt->accurate_found;
413 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
414 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
415 goto bail_out; /* Return if no plugins loaded */
418 if (!get_plugin_name(jcr, cmd, &len)) {
422 /* Note, we stop the loop on the first plugin that matches the name */
423 foreach_alist_index(i, plugin, b_plugin_list) {
424 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
425 if (!for_this_plugin(plugin, cmd, len)) {
432 if (is_plugin_disabled(&plugin_ctx_list[i])) {
436 jcr->plugin_ctx = &plugin_ctx_list[i];
437 jcr->plugin = plugin;
439 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
442 /* TODO: would be better to set this in save_file() */
444 jcr->opt_plugin = true;
445 jcr->plugin = plugin;
446 jcr->plugin_sp = sp; /* Unset sp in save_file */
447 jcr->plugin_ctx = &plugin_ctx_list[i];
449 update_ff_pkt(ff_pkt, sp);
451 /* reset plugin in JCR if not used this time */
453 jcr->plugin_ctx = NULL;
458 } /* end foreach loop */
461 Jmsg1(jcr, M_FATAL, 0, "Options plugin \"%s\" not found.\n", cmd);
468 * Sequence of calls for a backup:
469 * 1. plugin_save() here is called with ff_pkt
470 * 2. we find the plugin requested on the command string
471 * 3. we generate a bEventBackupCommand event to the specified plugin
472 * and pass it the command string.
473 * 4. we make a startPluginBackup call to the plugin, which gives
474 * us the data we need in save_pkt
475 * 5. we call Bacula's save_file() subroutine to save the specified
476 * file. The plugin will be called at pluginIO() to supply the
479 * Sequence of calls for restore:
480 * See subroutine plugin_name_stream() below.
482 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
487 char *cmd = ff_pkt->top_fname;
490 POOL_MEM fname(PM_FNAME);
491 POOL_MEM link(PM_FNAME);
494 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
495 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
496 return 1; /* Return if no plugins loaded */
499 jcr->cmd_plugin = true;
500 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
501 event.eventType = bEventBackupCommand;
503 if (!get_plugin_name(jcr, cmd, &len)) {
507 /* Note, we stop the loop on the first plugin that matches the name */
508 foreach_alist_index(i, plugin, b_plugin_list) {
509 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
510 if (!for_this_plugin(plugin, cmd, len)) {
514 * We put the current plugin pointer, and the plugin context
515 * into the jcr, because during save_file(), the plugin
516 * will be called many times and these values are needed.
519 jcr->plugin_ctx = &plugin_ctx_list[i];
520 jcr->plugin = plugin;
521 if (is_plugin_disabled(jcr)) {
525 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
526 /* Send the backup command to the right plugin*/
527 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
530 /* Loop getting filenames to backup then saving them */
531 while (!jcr->is_job_canceled()) {
532 memset(&sp, 0, sizeof(sp));
533 sp.pkt_size = sizeof(sp);
534 sp.pkt_end = sizeof(sp);
539 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
542 /* Get the file save parameters. I.e. the stat pkt ... */
543 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
547 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
551 jcr->plugin_sp = &sp;
554 * Copy fname and link because save_file() zaps them. This
555 * avoids zaping the plugin's strings.
557 ff_pkt->type = sp.type;
558 if (IS_FT_OBJECT(sp.type)) {
559 if (!sp.object_name) {
560 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
564 ff_pkt->fname = cmd; /* full plugin string */
565 ff_pkt->object_name = sp.object_name;
566 ff_pkt->object_index = sp.index; /* restore object index */
567 ff_pkt->object_compression = 0; /* no compression for now */
568 ff_pkt->object = sp.object;
569 ff_pkt->object_len = sp.object_len;
573 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
577 pm_strcpy(fname, sp.fname);
578 pm_strcpy(link, sp.link);
581 ff_pkt->fname = fname.c_str();
582 ff_pkt->link = link.c_str();
583 update_ff_pkt(ff_pkt, &sp);
586 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
587 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
589 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
591 /* Call Bacula core code to backup the plugin's file */
592 save_file(jcr, ff_pkt, true);
593 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
594 if (rc == bRC_More || rc == bRC_OK) {
595 accurate_mark_file_as_seen(jcr, fname.c_str());
598 if (rc == bRC_More) {
602 } /* end while loop */
604 } /* end loop over all plugins */
605 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
609 jcr->cmd_plugin = false;
611 jcr->plugin_ctx = NULL;
617 * Sequence of calls for a estimate:
618 * 1. plugin_estimate() here is called with ff_pkt
619 * 2. we find the plugin requested on the command string
620 * 3. we generate a bEventEstimateCommand event to the specified plugin
621 * and pass it the command string.
622 * 4. we make a startPluginBackup call to the plugin, which gives
623 * us the data we need in save_pkt
626 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
631 char *cmd = ff_pkt->top_fname;
634 POOL_MEM fname(PM_FNAME);
635 POOL_MEM link(PM_FNAME);
639 if (!b_plugin_list || !jcr->plugin_ctx_list) {
640 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
641 return 1; /* Return if no plugins loaded */
644 jcr->cmd_plugin = true;
645 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
646 event.eventType = bEventEstimateCommand;
648 if (!get_plugin_name(jcr, cmd, &len)) {
652 /* Note, we stop the loop on the first plugin that matches the name */
653 foreach_alist_index(i, plugin, b_plugin_list) {
654 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
655 if (!for_this_plugin(plugin, cmd, len)) {
659 * We put the current plugin pointer, and the plugin context
660 * into the jcr, because during save_file(), the plugin
661 * will be called many times and these values are needed.
664 jcr->plugin_ctx = &plugin_ctx_list[i];
665 jcr->plugin = plugin;
666 if (is_plugin_disabled(jcr)) {
670 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
671 /* Send the backup command to the right plugin*/
672 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
675 /* Loop getting filenames to backup then saving them */
676 while (!jcr->is_job_canceled()) {
678 memset(&sp, 0, sizeof(sp));
679 sp.pkt_size = sizeof(sp);
680 sp.pkt_end = sizeof(sp);
684 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
686 /* Get the file save parameters. I.e. the stat pkt ... */
687 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
691 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
696 if (!IS_FT_OBJECT(sp.type)) {
698 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
703 /* Count only files backed up */
713 jcr->JobFiles++; /* increment number of files backed up */
718 jcr->num_files_examined++;
720 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
721 if (sp.statp.st_size > 0) {
722 jcr->JobBytes += sp.statp.st_size;
727 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
729 attr.ofname = (POOLMEM *)sp.fname;
730 attr.olname = (POOLMEM *)sp.link;
731 print_ls_output(jcr, &attr);
735 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
737 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
739 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
740 if (rc == bRC_More || rc == bRC_OK) {
741 accurate_mark_file_as_seen(jcr, sp.fname);
744 if (rc == bRC_More) {
748 } /* end while loop */
750 } /* end loop over all plugins */
751 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
755 jcr->cmd_plugin = false;
757 jcr->plugin_ctx = NULL;
762 * Send plugin name start/end record to SD
764 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
767 int index = jcr->JobFiles;
768 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
772 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
775 if (jcr->is_job_canceled()) {
780 index++; /* JobFiles not incremented yet */
782 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
783 /* Send stream header */
785 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
786 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
790 Dmsg1(dbglvl, "send plugin name hdr: %s\n", sd->msg);
794 /* Send data -- not much */
795 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
797 /* Send end of data */
798 stat = sd->fsend("%ld 0", jcr->JobFiles);
802 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
806 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
807 sd->signal(BNET_EOD); /* indicate end of plugin name data */
813 * Plugin name stream found during restore. The record passed in
814 * argument name was generated in send_plugin_name() above.
816 * Returns: true if start of stream
817 * false if end of steam
819 bool plugin_name_stream(JCR *jcr, char *name)
827 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
830 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
831 skip_nonspaces(&p); /* skip over jcr->JobFiles */
835 /* Start of plugin data */
836 skip_nonspaces(&p); /* skip start/end flag */
838 // portable = *p == '1';
839 skip_nonspaces(&p); /* skip portable flag */
844 * End of plugin data, notify plugin, then clear flags
846 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
847 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
848 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
851 jcr->plugin->restoreFileStarted = false;
852 jcr->plugin->createFileCalled = false;
854 jcr->plugin_ctx = NULL;
859 if (!plugin_ctx_list) {
864 * After this point, we are dealing with a restore start
866 if (!get_plugin_name(jcr, cmd, &len)) {
871 * Search for correct plugin as specified on the command
874 foreach_alist_index(i, plugin, b_plugin_list) {
876 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
877 if (!for_this_plugin(plugin, cmd, len)) {
881 jcr->plugin_ctx = &plugin_ctx_list[i];
882 jcr->plugin = plugin;
883 if (is_plugin_disabled(jcr)) {
884 Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
887 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
888 event.eventType = bEventRestoreCommand;
889 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
890 &event, cmd) != bRC_OK) {
891 Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
894 if (plugin->restoreFileStarted) {
895 Jmsg2(jcr, M_FATAL, 0, "Second call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
896 plugin->restoreFileStarted = false;
899 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
900 plugin->restoreFileStarted = true;
903 Dmsg1(dbglvl, "startRestoreFile failed. plugin=%s\n", cmd);
907 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
916 jcr->plugin_ctx = NULL;
921 * Tell the plugin to create the file. Return values are
922 * This is called only during Restore
925 * CF_SKIP -- skip processing this file
926 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
927 * CF_CREATED -- created, but no content to extract (typically directories)
930 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
932 bpContext *plugin_ctx = jcr->plugin_ctx;
933 Plugin *plugin = jcr->plugin;
934 struct restore_pkt rp;
939 if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
943 rp.pkt_size = sizeof(rp);
944 rp.pkt_end = sizeof(rp);
945 rp.delta_seq = attr->delta_seq;
946 rp.stream = attr->stream;
947 rp.data_stream = attr->data_stream;
948 rp.type = attr->type;
949 rp.file_index = attr->file_index;
950 rp.LinkFI = attr->LinkFI;
952 rp.statp = attr->statp; /* structure assignment */
953 rp.attrEx = attr->attrEx;
954 rp.ofname = attr->ofname;
955 rp.olname = attr->olname;
956 rp.where = jcr->where;
957 rp.RegexWhere = jcr->RegexWhere;
958 rp.replace = jcr->replace;
959 rp.create_status = CF_ERROR;
960 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
961 rp.stream, rp.type, rp.LinkFI, rp.ofname);
963 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
966 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
967 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to createFile=%d %d\n",
968 plugin->createFileCalled, plugin->restoreFileStarted);
969 plugin->createFileCalled = false;
972 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
974 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
978 if (rp.create_status == CF_ERROR) {
979 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
984 if (rp.create_status == CF_SKIP) {
988 if (rp.create_status == CF_CORE) {
989 return CF_CORE; /* Let Bacula core handle the file creation */
992 /* Use the bfile for plugin */
993 set_cmd_plugin(bfd, jcr);
995 /* Created link or directory? */
996 if (rp.create_status == CF_CREATED) {
997 return rp.create_status; /* yes, no need to bopen */
1002 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
1003 Dmsg0(dbglvl, "call bopen\n");
1004 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
1005 Dmsg1(dbglvl, "bopen status=%d\n", stat);
1008 be.set_errno(bfd->berrno);
1009 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
1010 attr->ofname, be.bstrerror());
1011 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
1015 if (!is_bopen(bfd)) {
1016 Dmsg0(000, "===== BFD is not open!!!!\n");
1023 * Reset the file attributes after all file I/O is done -- this allows
1024 * the previous access time/dates to be set properly, and it also allows
1025 * us to properly set directory permissions.
1026 * Not currently Implemented.
1028 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
1030 Plugin *plugin = (Plugin *)jcr->plugin;
1031 struct restore_pkt rp;
1033 Dmsg0(dbglvl, "plugin_set_attributes\n");
1035 if (!plugin || !jcr->plugin_ctx) {
1039 memset(&rp, 0, sizeof(rp));
1040 rp.pkt_size = sizeof(rp);
1041 rp.pkt_end = sizeof(rp);
1042 rp.stream = attr->stream;
1043 rp.data_stream = attr->data_stream;
1044 rp.type = attr->type;
1045 rp.file_index = attr->file_index;
1046 rp.LinkFI = attr->LinkFI;
1048 rp.statp = attr->statp; /* structure assignment */
1049 rp.attrEx = attr->attrEx;
1050 rp.ofname = attr->ofname;
1051 rp.olname = attr->olname;
1052 rp.where = jcr->where;
1053 rp.RegexWhere = jcr->RegexWhere;
1054 rp.replace = jcr->replace;
1055 rp.create_status = CF_ERROR;
1057 plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
1059 if (rp.create_status == CF_CORE) {
1060 set_attributes(jcr, attr, ofd);
1062 if (is_bopen(ofd)) {
1065 pm_strcpy(attr->ofname, "*none*");
1073 * The Plugin ACL data backup. We are using a new Plugin callback:
1074 * handleXACLdata() for that. The new callback get a pointer to
1075 * struct xacl_pkt as a main argument which consist of the following
1077 * xacl.func - could be the one of BACL_BACKUP, BACL_RESTORE,
1078 * BXATTR_BACKUP, BXATTR_RESTORE
1079 * xacl.count - the length of data at the content buffer
1080 * xacl.content - the buffer itself
1081 * The buffer (xacl.content) is supplied by Bacula during restore and has to
1082 * be supplied by a Plugin during backup.
1083 * The new callback should return bRC_OK on success and bRC_Error on
1087 * jcr - Job Control Record
1088 * ff_pkt - file save packet
1089 * data is a pointer to variable returned
1091 * data - the pointer to data buffer returned from plugin
1092 * 0 - Success, no more data to save
1093 * > 0 - Success and the number of bytes returned in **data buffer
1094 * -1 - Error, no acls data to backup
1096 int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data)
1098 struct xacl_pkt xacl;
1099 Plugin *plugin = (Plugin *)jcr->plugin;
1102 Dmsg0(dbglvl, "plugin_backup_acl\n");
1104 /* check of input variables */
1105 if (!plugin || !jcr->plugin_ctx || !data) {
1109 /* prepare the xacl packet */
1110 memset(&xacl, 0, sizeof(xacl));
1111 xacl.pkt_size = sizeof(xacl);
1112 xacl.pkt_end = sizeof(xacl);
1113 xacl.func = BACL_BACKUP;
1115 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1117 /* check out status */
1119 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1122 if (xacl.count > 0){
1123 /* we have something to save, so prepare return data */
1124 *data = xacl.content;
1132 * Called here when Bacula got ACL stream to restore but not every stream but
1133 * a specific one: STREAM_XACL_PLUGIN_ACL which means a plugin has to
1137 * jcr - Job Control Record
1138 * data - content to restore
1139 * length - the length of the content to restore
1141 * true - when successful
1142 * false - on any Error
1144 bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length)
1146 struct xacl_pkt xacl;
1147 Plugin *plugin = (Plugin *)jcr->plugin;
1150 Dmsg0(dbglvl, "plugin_restore_acl\n");
1152 /* check of input variables */
1153 if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1157 /* prepare the xacl packet */
1158 memset(&xacl, 0, sizeof(xacl));
1159 xacl.pkt_size = sizeof(xacl);
1160 xacl.pkt_end = sizeof(xacl);
1161 xacl.func = BACL_RESTORE;
1162 xacl.content = data;
1163 xacl.count = length;
1165 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1167 /* check out status */
1169 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1177 * The Plugin XATTR data backup. We are using a new Plugin callback:
1178 * handleXACLdata() for that. Check plugin_backup_acl for new callback
1182 * jcr - Job Control Record
1183 * ff_pkt - file save packet
1184 * data is a pointer to variable returned
1186 * data - the pointer to data buffer returned from plugin
1187 * 0 - Success, no more data to save
1188 * >0 - Success and the number of bytes returned in **data buffer
1191 int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data)
1194 struct xacl_pkt xacl;
1195 Plugin *plugin = (Plugin *)jcr->plugin;
1198 Dmsg0(dbglvl, "plugin_backup_xattr\n");
1200 /* check of input variables */
1201 if (!plugin || !jcr->plugin_ctx || !data) {
1205 /* prepare the xacl packet */
1206 memset(&xacl, 0, sizeof(xacl));
1207 xacl.pkt_size = sizeof(xacl);
1208 xacl.pkt_end = sizeof(xacl);
1209 xacl.func = BXATTR_BACKUP;
1211 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1213 /* check out status */
1215 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1218 if (xacl.count > 0){
1219 /* we have something to save, so prepare return data */
1220 *data = xacl.content;
1228 * Called here when Bacula got XATTR stream to restore but not every stream but
1229 * a specific one: STREAM_XACL_PLUGIN_XATTR which means a plugin has to
1233 * jcr - Job Control Record
1234 * data - content to restore
1235 * length - the length of the content to restore
1237 * true - when successful
1238 * false - on any Error
1240 bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length)
1242 struct xacl_pkt xacl;
1243 Plugin *plugin = (Plugin *)jcr->plugin;
1246 Dmsg0(dbglvl, "plugin_restore_xattr\n");
1248 /* check of input variables */
1249 if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1253 /* prepare the xacl packet */
1254 memset(&xacl, 0, sizeof(xacl));
1255 xacl.pkt_size = sizeof(xacl);
1256 xacl.pkt_end = sizeof(xacl);
1257 xacl.func = BXATTR_RESTORE;
1258 xacl.content = data;
1259 xacl.count = length;
1261 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1263 /* check out status */
1265 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1273 * Print to file the plugin info.
1275 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1280 pInfo *info = (pInfo *)plugin->pinfo;
1281 fprintf(fp, "\tversion=%d\n", info->version);
1282 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1283 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1284 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1285 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1286 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1287 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1291 * This entry point is called internally by Bacula to ensure
1292 * that the plugin IO calls come into this code.
1294 void load_fd_plugins(const char *plugin_dir)
1300 Dmsg0(dbglvl, "plugin dir is NULL\n");
1304 b_plugin_list = New(alist(10, not_owned_by_alist));
1306 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1307 is_plugin_compatible)) {
1308 /* Either none found, or some error */
1309 if (b_plugin_list->size() == 0) {
1310 delete b_plugin_list;
1311 b_plugin_list = NULL;
1312 Dmsg0(dbglvl, "No plugins loaded\n");
1317 /* Plug entry points called from findlib */
1318 plugin_bopen = my_plugin_bopen;
1319 plugin_bclose = my_plugin_bclose;
1320 plugin_bread = my_plugin_bread;
1321 plugin_bwrite = my_plugin_bwrite;
1322 plugin_blseek = my_plugin_blseek;
1326 * Verify that the plugin is acceptable, and print information
1329 foreach_alist_index(i, plugin, b_plugin_list) {
1330 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1331 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1334 dbg_plugin_add_hook(dump_fd_plugin);
1339 * Check if a plugin is compatible. Called by the load_plugin function
1340 * to allow us to verify the plugin.
1342 static bool is_plugin_compatible(Plugin *plugin)
1344 pInfo *info = (pInfo *)plugin->pinfo;
1345 Dmsg0(dbglvl, "is_plugin_compatible called\n");
1347 if (chk_dbglvl(50)) {
1348 dump_fd_plugin(plugin, stdin);
1350 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1351 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1352 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1353 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1354 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1358 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1359 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1360 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1361 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1362 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1365 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1366 strcmp(info->plugin_license, "AGPLv3") != 0 &&
1367 strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
1368 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1369 plugin->file, info->plugin_license);
1370 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1371 plugin->file, info->plugin_license);
1374 if (info->size != sizeof(pInfo)) {
1375 Jmsg(NULL, M_ERROR, 0,
1376 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1377 plugin->file, sizeof(pInfo), info->size);
1387 * Create a new instance of each plugin for this Job
1388 * Note, b_plugin_list can exist but jcr->plugin_ctx_list can
1389 * be NULL if no plugins were loaded.
1391 void new_plugins(JCR *jcr)
1397 if (!b_plugin_list) {
1398 Dmsg0(dbglvl, "plugin list is NULL\n");
1401 if (jcr->is_job_canceled()) {
1405 int num = b_plugin_list->size();
1408 Dmsg0(dbglvl, "No plugins loaded\n");
1412 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1414 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1415 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1416 foreach_alist_index(i, plugin, b_plugin_list) {
1418 /* Start a new instance of each plugin */
1419 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1420 memset(b_ctx, 0, sizeof(bacula_ctx));
1422 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1423 plugin_ctx_list[i].pContext = NULL;
1424 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1425 Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
1426 b_ctx->disabled = true;
1430 Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1437 * Free the plugin instances for this Job
1439 void free_plugins(JCR *jcr)
1444 if (!b_plugin_list || !jcr->plugin_ctx_list) {
1445 return; /* no plugins, nothing to do */
1449 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1450 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1451 foreach_alist_index(i, plugin, b_plugin_list) {
1452 /* Free the plugin instance */
1453 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1454 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1458 free(plugin_ctx_list);
1459 jcr->plugin_ctx_list = NULL;
1462 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
1464 JCR *jcr = bfd->jcr;
1465 Plugin *plugin = (Plugin *)jcr->plugin;
1468 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1470 if (!plugin || !jcr->plugin_ctx) {
1473 io.pkt_size = sizeof(io);
1474 io.pkt_end = sizeof(io);
1484 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1485 bfd->berrno = io.io_errno;
1487 errno = b_errno_win32;
1489 errno = io.io_errno;
1490 bfd->lerror = io.lerror;
1492 Dmsg1(dbglvl, "Return from plugin open status=%d\n", io.status);
1497 static int my_plugin_bclose(BFILE *bfd)
1499 JCR *jcr = bfd->jcr;
1500 Plugin *plugin = (Plugin *)jcr->plugin;
1504 Dmsg0(dbglvl, "===== plugin_bclose\n");
1505 if (!plugin || !jcr->plugin_ctx) {
1508 io.pkt_size = sizeof(io);
1509 io.pkt_end = sizeof(io);
1516 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1517 bfd->berrno = io.io_errno;
1519 errno = b_errno_win32;
1521 errno = io.io_errno;
1522 bfd->lerror = io.lerror;
1524 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1529 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1531 JCR *jcr = bfd->jcr;
1532 Plugin *plugin = (Plugin *)jcr->plugin;
1536 Dmsg0(dbglvl, "plugin_bread\n");
1537 if (!plugin || !jcr->plugin_ctx) {
1540 io.pkt_size = sizeof(io);
1541 io.pkt_end = sizeof(io);
1544 io.buf = (char *)buf;
1549 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1550 bfd->offset = io.offset;
1551 bfd->berrno = io.io_errno;
1553 errno = b_errno_win32;
1555 errno = io.io_errno;
1556 bfd->lerror = io.lerror;
1559 return (ssize_t)io.status;
1562 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1564 JCR *jcr = bfd->jcr;
1565 Plugin *plugin = (Plugin *)jcr->plugin;
1569 Dmsg0(dbglvl, "plugin_bwrite\n");
1570 if (!plugin || !jcr->plugin_ctx) {
1571 Dmsg0(0, "No plugin context\n");
1574 io.pkt_size = sizeof(io);
1575 io.pkt_end = sizeof(io);
1578 io.buf = (char *)buf;
1582 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1583 bfd->berrno = io.io_errno;
1585 errno = b_errno_win32;
1587 errno = io.io_errno;
1588 bfd->lerror = io.lerror;
1591 return (ssize_t)io.status;
1594 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1596 JCR *jcr = bfd->jcr;
1597 Plugin *plugin = (Plugin *)jcr->plugin;
1601 Dmsg0(dbglvl, "plugin_bseek\n");
1602 if (!plugin || !jcr->plugin_ctx) {
1605 io.pkt_size = sizeof(io);
1606 io.pkt_end = sizeof(io);
1612 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1613 bfd->berrno = io.io_errno;
1615 errno = b_errno_win32;
1617 errno = io.io_errno;
1618 bfd->lerror = io.lerror;
1621 return (boffset_t)io.offset;
1624 /* ==============================================================
1626 * Callbacks from the plugin
1628 * ==============================================================
1630 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1638 switch (var) { /* General variables, no need of ctx */
1640 *((char **)value) = my_name;
1642 case bVarWorkingDir:
1643 *(void **)value = me->working_directory;
1646 *(char **)value = exepath;
1649 *(char **)value = version;
1652 *(char **)value = dist_name;
1654 case bVarPrevJobName:
1656 case bVarPrefixLinks:
1664 if (!ctx) { /* Other variables need context */
1668 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1675 *((int *)value) = jcr->JobId;
1676 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1679 *((int *)value) = jcr->getJobLevel();
1680 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1683 *((int *)value) = jcr->getJobType();
1684 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1687 *((char **)value) = jcr->client_name;
1688 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1691 *((char **)value) = jcr->Job;
1692 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1694 case bVarPrevJobName:
1695 *((char **)value) = jcr->PrevJob;
1696 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1699 *((int *)value) = jcr->JobStatus;
1700 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1703 *((int *)value) = (int)jcr->mtime;
1704 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1707 *((int *)value) = (int)jcr->accurate;
1708 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1710 case bVarInteractiveSession:
1711 *(int *)value = (int)jcr->interactive_session;
1714 *(int *)value = (int)jcr->JobFiles;
1717 break; /* a write only variable, ignore read request */
1720 if (jcr->pVSSClient) {
1721 *(void **)value = jcr->pVSSClient->GetVssObject();
1726 case bVarVssDllHandle:
1728 *(void **)value = vsslib;
1733 *(char **)value = jcr->where;
1735 case bVarRegexWhere:
1736 *(char **)value = jcr->RegexWhere;
1738 case bVarPrefixLinks:
1739 *(int *)value = (int)jcr->prefix_links;
1742 *((int*)value) = jcr->replace;
1743 Dmsg1(dbglvl, "Bacula: return replace=%c\n", jcr->replace);
1745 case bVarFDName: /* get warning with g++ if we missed one */
1746 case bVarWorkingDir:
1757 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1761 if (!value || !ctx) {
1764 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1765 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1769 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1772 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1776 case bVarInteractiveSession:
1777 jcr->interactive_session = (((intptr_t) value) == 1);
1786 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1796 va_start(args, ctx);
1797 while ((event = va_arg(args, uint32_t))) {
1798 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1805 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1806 int type, utime_t mtime, const char *fmt, ...)
1814 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1819 va_start(arg_ptr, fmt);
1820 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1822 Jmsg(jcr, type, mtime, "%s", buf);
1827 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1828 int level, const char *fmt, ...)
1834 va_start(arg_ptr, fmt);
1835 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1837 d_msg(file, line, level, "%s", buf);
1842 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1846 return sm_malloc(file, line, size);
1848 return malloc(size);
1852 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1855 sm_free(file, line, mem);
1861 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1867 bctx = (bacula_ctx *)ctx->bContext;
1879 * Let the plugin define files/directories to be excluded
1880 * from the main backup.
1882 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1888 if (!is_ctx_good(ctx, jcr, bctx)) {
1895 /* Save the include context */
1896 old = get_incexe(jcr);
1898 /* Not right time to add exlude */
1903 if (!bctx->exclude) {
1904 bctx->exclude = new_exclude(jcr);
1907 /* Set the Exclude context */
1908 set_incexe(jcr, bctx->exclude);
1910 add_file_to_fileset(jcr, file, true);
1912 /* Restore the current context */
1913 set_incexe(jcr, old);
1915 Dmsg1(100, "Add exclude file=%s\n", file);
1921 * Let the plugin define files/directories to be excluded
1922 * from the main backup.
1924 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1931 if (!is_ctx_good(ctx, jcr, bctx)) {
1938 /* Save the include context */
1939 old = get_incexe(jcr);
1941 /* Not right time to add include */
1945 if (!bctx->include) {
1946 bctx->include = old;
1949 set_incexe(jcr, bctx->include);
1950 add_file_to_fileset(jcr, file, true);
1952 /* Restore the current context */
1953 set_incexe(jcr, old);
1955 Dmsg1(100, "Add include file=%s\n", file);
1960 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1965 if (!is_ctx_good(ctx, jcr, bctx)) {
1971 add_options_to_fileset(jcr, opts);
1973 Dmsg1(1000, "Add options=%s\n", opts);
1977 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1982 if (!is_ctx_good(ctx, jcr, bctx)) {
1988 add_regex_to_fileset(jcr, item, type);
1989 Dmsg1(100, "Add regex=%s\n", item);
1994 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1999 if (!is_ctx_good(ctx, jcr, bctx)) {
2005 add_wild_to_fileset(jcr, item, type);
2006 Dmsg1(100, "Add wild=%s\n", item);
2011 static bRC baculaNewOptions(bpContext *ctx)
2016 if (!is_ctx_good(ctx, jcr, bctx)) {
2019 (void)new_options(jcr, NULL);
2024 static bRC baculaNewInclude(bpContext *ctx)
2029 if (!is_ctx_good(ctx, jcr, bctx)) {
2032 (void)new_include(jcr);
2037 static bRC baculaNewPreInclude(bpContext *ctx)
2042 if (!is_ctx_good(ctx, jcr, bctx)) {
2046 bctx->include = new_preinclude(jcr);
2047 new_options(jcr, bctx->include);
2048 set_incexe(jcr, bctx->include);
2055 * Check if a file have to be backuped using Accurate code
2057 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
2062 bRC ret = bRC_Error;
2065 if (!is_ctx_good(ctx, jcr, bctx)) {
2074 * Copy fname and link because save_file() zaps them. This
2075 * avoids zaping the plugin's strings.
2077 ff_pkt->type = sp->type;
2079 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
2083 ff_pkt->fname = sp->fname;
2084 ff_pkt->link = sp->link;
2085 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
2087 if (check_changes(jcr, ff_pkt)) {
2093 /* check_changes() can update delta sequence number, return it to the
2096 sp->delta_seq = ff_pkt->delta_seq;
2097 sp->accurate_found = ff_pkt->accurate_found;
2101 Dmsg1(100, "checkChanges=%i\n", ret);
2106 * Check if a file would be saved using current Include/Exclude code
2108 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
2115 struct stat oldstat;
2116 bRC ret = bRC_Error;
2119 if (!is_ctx_good(ctx, jcr, bctx)) {
2128 /* Probably not needed, but keep a copy */
2129 old = ff_pkt->fname;
2130 oldstat = ff_pkt->statp;
2132 ff_pkt->fname = sp->fname;
2133 ff_pkt->statp = sp->statp;
2135 if (accept_file(ff_pkt)) {
2141 ff_pkt->fname = old;
2142 ff_pkt->statp = oldstat;
2150 int (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
2151 int (*plugin_bclose)(JCR *jcr) = NULL;
2152 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
2153 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
2154 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
2156 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
2161 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
2166 int main(int argc, char *argv[])
2168 char plugin_dir[1000];
2173 strcpy(my_name, "test-fd");
2175 getcwd(plugin_dir, sizeof(plugin_dir)-1);
2176 load_fd_plugins(plugin_dir);
2184 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
2185 generate_plugin_event(jcr1, bEventJobEnd);
2186 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
2188 generate_plugin_event(jcr2, bEventJobEnd);
2193 Dmsg0(dbglvl, "bacula: OK ...\n");
2194 close_memory_pool();
2195 sm_dump(false); /* unit test */
2199 #endif /* TEST_PROGRAM */