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;
34 const char *plugin_type = "-fd.so";
36 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
37 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
39 /* Function pointers to be set here */
40 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
41 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
42 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
43 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
44 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
47 /* Forward referenced functions */
48 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
49 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
50 static bRC baculaRegisterEvents(bpContext *ctx, ...);
51 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
52 int type, utime_t mtime, const char *fmt, ...);
53 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
54 int level, const char *fmt, ...);
55 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
57 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
58 static bRC baculaAddExclude(bpContext *ctx, const char *file);
59 static bRC baculaAddInclude(bpContext *ctx, const char *file);
60 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
61 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
62 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
63 static bRC baculaNewOptions(bpContext *ctx);
64 static bRC baculaNewInclude(bpContext *ctx);
65 static bRC baculaNewPreInclude(bpContext *ctx);
66 static bool is_plugin_compatible(Plugin *plugin);
67 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
68 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
69 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
72 * These will be plugged into the global pointer structure for
75 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
76 static int my_plugin_bclose(BFILE *bfd);
77 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
78 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
79 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
83 static bInfo binfo = {
85 FD_PLUGIN_INTERFACE_VERSION
88 /* Bacula entry points */
89 static bFuncs bfuncs = {
91 FD_PLUGIN_INTERFACE_VERSION,
112 * Bacula private context
115 JCR *jcr; /* jcr for plugin */
116 bRC rc; /* last return code */
117 bool disabled; /* set if plugin disabled */
118 findINCEXE *exclude; /* pointer to exclude files */
119 findINCEXE *include; /* pointer to include/exclude files */
123 * Test if event is for this plugin
125 static bool for_this_plugin(Plugin *plugin, char *name, int len)
127 Dmsg4(dbglvl, "name=%s len=%d plugin=%s plen=%d\n", name, len, plugin->file, plugin->file_len);
128 if (!name) { /* if no plugin name, all plugins get it */
131 /* Return global VSS job metadata to all plugins */
132 if (strcmp("job", name) == 0) { /* old V4.0 name for VSS job metadata */
135 if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
138 /* Check if this is the correct plugin */
139 if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
146 bool is_plugin_disabled(bpContext *plugin_ctx)
153 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
157 return b_ctx->disabled;
160 bool is_plugin_disabled(JCR *jcr)
162 return is_plugin_disabled(jcr->plugin_ctx);
166 * Create a plugin event When receiving bEventCancelCommand, this function is
167 * called by an other thread.
169 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
171 bpContext *plugin_ctx;
177 bool call_if_canceled = false;
178 restore_object_pkt *rop;
181 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list) {
182 return; /* Return if no plugins loaded */
186 * Some events are sent to only a particular plugin or must be
187 * called even if the job is canceled
190 case bEventPluginCommand:
191 case bEventOptionPlugin:
192 name = (char *)value;
193 if (!get_plugin_name(jcr, name, &len)) {
197 case bEventRestoreObject:
198 /* After all RestoreObject, we have it one more time with value=NULL */
200 /* Some RestoreObjects may not have a plugin name */
201 rop = (restore_object_pkt *)value;
202 if (*rop->plugin_name) {
203 name = rop->plugin_name;
204 get_plugin_name(jcr, name, &len);
209 case bEventEndBackupJob:
210 case bEventEndVerifyJob:
211 call_if_canceled = true; /* plugin *must* see this call */
213 case bEventStartRestoreJob:
214 foreach_alist_index(i, plugin, b_plugin_list) {
215 plugin->restoreFileStarted = false;
216 plugin->createFileCalled = false;
219 case bEventEndRestoreJob:
220 call_if_canceled = true; /* plugin *must* see this call */
226 /* If call_if_canceled is set, we call the plugin anyway */
227 if (!call_if_canceled && jcr->is_job_canceled()) {
231 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
232 event.eventType = eventType;
234 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
237 * Pass event to every plugin (except if name is set). If name
238 * is set, we pass it only to the plugin with that name.
240 foreach_alist_index(i, plugin, b_plugin_list) {
241 if (!for_this_plugin(plugin, name, len)) {
242 Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
243 name, name==NULL?1:0);
247 * Note, at this point do not change
248 * jcr->plugin or jcr->plugin_ctx
251 plugin_ctx = &plugin_ctx_list[i];
252 if (is_plugin_disabled(plugin_ctx)) {
253 Dmsg1(50, "Plugin %s disabled\n", plugin->file);
256 if (eventType == bEventEndRestoreJob) {
257 Dmsg0(50, "eventType==bEventEndRestoreJob\n");
258 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
259 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
262 jcr->plugin->restoreFileStarted = false;
263 jcr->plugin->createFileCalled = false;
266 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
272 * Check if file was seen for accurate
274 bool plugin_check_file(JCR *jcr, char *fname)
281 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
282 return false; /* Return if no plugins loaded */
285 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
287 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
289 /* Pass event to every plugin */
290 foreach_alist_index(i, plugin, b_plugin_list) {
291 jcr->plugin_ctx = &plugin_ctx_list[i];
292 jcr->plugin = plugin;
293 if (is_plugin_disabled(jcr)) {
296 if (plug_func(plugin)->checkFile == NULL) {
299 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
300 if (rc == bRC_Seen) {
307 jcr->plugin_ctx = NULL;
308 return rc == bRC_Seen;
311 /* Get the first part of the the plugin command
312 * systemstate:/@SYSTEMSTATE/
314 * => can use for_this_plugin(plug, cmd, ret);
316 * The plugin command can contain only the plugin name
320 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
325 if (!cmd || (*cmd == '\0')) {
328 /* Handle plugin command here backup */
329 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
330 if ((p = strchr(cmd, ':')) == NULL) {
331 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
334 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
337 } else { /* plugin:argument */
349 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
352 ff_pkt->no_read = sp->no_read;
353 ff_pkt->delta_seq = sp->delta_seq;
354 if (sp->flags & FO_DELTA) {
355 ff_pkt->flags |= FO_DELTA;
356 ff_pkt->delta_seq++; /* make new delta sequence number */
358 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
359 ff_pkt->delta_seq = 0;
362 if (sp->flags & FO_OFFSETS) {
363 ff_pkt->flags |= FO_OFFSETS;
365 ff_pkt->flags &= ~FO_OFFSETS;
367 /* Sparse code doesn't work with plugins
368 * that use FIFO or STDOUT/IN to communicate
370 if (sp->flags & FO_SPARSE) {
371 ff_pkt->flags |= FO_SPARSE;
373 ff_pkt->flags &= ~FO_SPARSE;
375 if (sp->flags & FO_PORTABLE_DATA) {
376 ff_pkt->flags |= FO_PORTABLE_DATA;
378 ff_pkt->flags &= ~FO_PORTABLE_DATA;
380 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
384 /* Ask to a Option Plugin what to do with the current file */
385 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
390 char *cmd = ff_pkt->plugin;
394 event.eventType = bEventHandleBackupFile;
397 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
398 memset(sp, 0, sizeof(struct save_pkt));
399 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
402 sp->link = ff_pkt->link;
403 sp->cmd = ff_pkt->plugin;
404 sp->statp = ff_pkt->statp;
405 sp->fname = ff_pkt->fname;
406 sp->delta_seq = ff_pkt->delta_seq;
407 sp->accurate_found = ff_pkt->accurate_found;
409 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
410 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
411 goto bail_out; /* Return if no plugins loaded */
414 if (!get_plugin_name(jcr, cmd, &len)) {
418 /* Note, we stop the loop on the first plugin that matches the name */
419 foreach_alist_index(i, plugin, b_plugin_list) {
420 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
421 if (!for_this_plugin(plugin, cmd, len)) {
428 if (is_plugin_disabled(&plugin_ctx_list[i])) {
432 jcr->plugin_ctx = &plugin_ctx_list[i];
433 jcr->plugin = plugin;
435 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
438 /* TODO: would be better to set this in save_file() */
440 jcr->opt_plugin = true;
441 jcr->plugin = plugin;
442 jcr->plugin_sp = sp; /* Unset sp in save_file */
443 jcr->plugin_ctx = &plugin_ctx_list[i];
445 update_ff_pkt(ff_pkt, sp);
447 /* reset plugin in JCR if not used this time */
449 jcr->plugin_ctx = NULL;
454 } /* end foreach loop */
457 Jmsg1(jcr, M_FATAL, 0, "Options plugin \"%s\" not found.\n", cmd);
464 * Sequence of calls for a backup:
465 * 1. plugin_save() here is called with ff_pkt
466 * 2. we find the plugin requested on the command string
467 * 3. we generate a bEventBackupCommand event to the specified plugin
468 * and pass it the command string.
469 * 4. we make a startPluginBackup call to the plugin, which gives
470 * us the data we need in save_pkt
471 * 5. we call Bacula's save_file() subroutine to save the specified
472 * file. The plugin will be called at pluginIO() to supply the
475 * Sequence of calls for restore:
476 * See subroutine plugin_name_stream() below.
478 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
483 char *cmd = ff_pkt->top_fname;
486 POOL_MEM fname(PM_FNAME);
487 POOL_MEM link(PM_FNAME);
490 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
491 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
492 return 1; /* Return if no plugins loaded */
495 jcr->cmd_plugin = true;
496 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
497 event.eventType = bEventBackupCommand;
499 if (!get_plugin_name(jcr, cmd, &len)) {
503 /* Note, we stop the loop on the first plugin that matches the name */
504 foreach_alist_index(i, plugin, b_plugin_list) {
505 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
506 if (!for_this_plugin(plugin, cmd, len)) {
510 * We put the current plugin pointer, and the plugin context
511 * into the jcr, because during save_file(), the plugin
512 * will be called many times and these values are needed.
515 jcr->plugin_ctx = &plugin_ctx_list[i];
516 jcr->plugin = plugin;
517 if (is_plugin_disabled(jcr)) {
521 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
522 /* Send the backup command to the right plugin*/
523 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
526 /* Loop getting filenames to backup then saving them */
527 while (!jcr->is_job_canceled()) {
528 memset(&sp, 0, sizeof(sp));
529 sp.pkt_size = sizeof(sp);
530 sp.pkt_end = sizeof(sp);
535 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
538 /* Get the file save parameters. I.e. the stat pkt ... */
539 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
543 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
547 jcr->plugin_sp = &sp;
550 * Copy fname and link because save_file() zaps them. This
551 * avoids zaping the plugin's strings.
553 ff_pkt->type = sp.type;
554 if (IS_FT_OBJECT(sp.type)) {
555 if (!sp.object_name) {
556 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
560 ff_pkt->fname = cmd; /* full plugin string */
561 ff_pkt->object_name = sp.object_name;
562 ff_pkt->object_index = sp.index; /* restore object index */
563 ff_pkt->object_compression = 0; /* no compression for now */
564 ff_pkt->object = sp.object;
565 ff_pkt->object_len = sp.object_len;
569 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
573 pm_strcpy(fname, sp.fname);
574 pm_strcpy(link, sp.link);
577 ff_pkt->fname = fname.c_str();
578 ff_pkt->link = link.c_str();
579 update_ff_pkt(ff_pkt, &sp);
582 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
583 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
585 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
587 /* Call Bacula core code to backup the plugin's file */
588 save_file(jcr, ff_pkt, true);
589 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
590 if (rc == bRC_More || rc == bRC_OK) {
591 accurate_mark_file_as_seen(jcr, fname.c_str());
594 if (rc == bRC_More) {
598 } /* end while loop */
600 } /* end loop over all plugins */
601 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
605 jcr->cmd_plugin = false;
607 jcr->plugin_ctx = NULL;
613 * Sequence of calls for a estimate:
614 * 1. plugin_estimate() here is called with ff_pkt
615 * 2. we find the plugin requested on the command string
616 * 3. we generate a bEventEstimateCommand event to the specified plugin
617 * and pass it the command string.
618 * 4. we make a startPluginBackup call to the plugin, which gives
619 * us the data we need in save_pkt
622 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
627 char *cmd = ff_pkt->top_fname;
630 POOL_MEM fname(PM_FNAME);
631 POOL_MEM link(PM_FNAME);
635 if (!b_plugin_list || !jcr->plugin_ctx_list) {
636 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
637 return 1; /* Return if no plugins loaded */
640 jcr->cmd_plugin = true;
641 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
642 event.eventType = bEventEstimateCommand;
644 if (!get_plugin_name(jcr, cmd, &len)) {
648 /* Note, we stop the loop on the first plugin that matches the name */
649 foreach_alist_index(i, plugin, b_plugin_list) {
650 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
651 if (!for_this_plugin(plugin, cmd, len)) {
655 * We put the current plugin pointer, and the plugin context
656 * into the jcr, because during save_file(), the plugin
657 * will be called many times and these values are needed.
660 jcr->plugin_ctx = &plugin_ctx_list[i];
661 jcr->plugin = plugin;
662 if (is_plugin_disabled(jcr)) {
666 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
667 /* Send the backup command to the right plugin*/
668 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
671 /* Loop getting filenames to backup then saving them */
672 while (!jcr->is_job_canceled()) {
674 memset(&sp, 0, sizeof(sp));
675 sp.pkt_size = sizeof(sp);
676 sp.pkt_end = sizeof(sp);
680 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
682 /* Get the file save parameters. I.e. the stat pkt ... */
683 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
687 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
692 if (!IS_FT_OBJECT(sp.type)) {
694 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
699 /* Count only files backed up */
709 jcr->JobFiles++; /* increment number of files backed up */
714 jcr->num_files_examined++;
716 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
717 if (sp.statp.st_size > 0) {
718 jcr->JobBytes += sp.statp.st_size;
723 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
725 attr.ofname = (POOLMEM *)sp.fname;
726 attr.olname = (POOLMEM *)sp.link;
727 print_ls_output(jcr, &attr);
731 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
733 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
735 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
736 if (rc == bRC_More || rc == bRC_OK) {
737 accurate_mark_file_as_seen(jcr, sp.fname);
740 if (rc == bRC_More) {
744 } /* end while loop */
746 } /* end loop over all plugins */
747 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
751 jcr->cmd_plugin = false;
753 jcr->plugin_ctx = NULL;
758 * Send plugin name start/end record to SD
760 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
763 int index = jcr->JobFiles;
764 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
768 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
771 if (jcr->is_job_canceled()) {
776 index++; /* JobFiles not incremented yet */
778 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
779 /* Send stream header */
781 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
782 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
786 Dmsg1(dbglvl, "send plugin name hdr: %s\n", sd->msg);
790 /* Send data -- not much */
791 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
793 /* Send end of data */
794 stat = sd->fsend("%ld 0", jcr->JobFiles);
798 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
802 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
803 sd->signal(BNET_EOD); /* indicate end of plugin name data */
809 * Plugin name stream found during restore. The record passed in
810 * argument name was generated in send_plugin_name() above.
812 * Returns: true if start of stream
813 * false if end of steam
815 bool plugin_name_stream(JCR *jcr, char *name)
823 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
826 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
827 skip_nonspaces(&p); /* skip over jcr->JobFiles */
831 /* Start of plugin data */
832 skip_nonspaces(&p); /* skip start/end flag */
834 // portable = *p == '1';
835 skip_nonspaces(&p); /* skip portable flag */
840 * End of plugin data, notify plugin, then clear flags
842 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
843 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
844 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
847 jcr->plugin->restoreFileStarted = false;
848 jcr->plugin->createFileCalled = false;
850 jcr->plugin_ctx = NULL;
855 if (!plugin_ctx_list) {
860 * After this point, we are dealing with a restore start
862 if (!get_plugin_name(jcr, cmd, &len)) {
867 * Search for correct plugin as specified on the command
870 foreach_alist_index(i, plugin, b_plugin_list) {
872 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
873 if (!for_this_plugin(plugin, cmd, len)) {
877 jcr->plugin_ctx = &plugin_ctx_list[i];
878 jcr->plugin = plugin;
879 if (is_plugin_disabled(jcr)) {
880 Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
883 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
884 event.eventType = bEventRestoreCommand;
885 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
886 &event, cmd) != bRC_OK) {
887 Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
890 if (plugin->restoreFileStarted) {
891 Jmsg2(jcr, M_FATAL, 0, "Second call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
892 plugin->restoreFileStarted = false;
895 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
896 plugin->restoreFileStarted = true;
899 Dmsg1(dbglvl, "startRestoreFile failed. plugin=%s\n", cmd);
903 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
912 jcr->plugin_ctx = NULL;
917 * Tell the plugin to create the file. Return values are
918 * This is called only during Restore
921 * CF_SKIP -- skip processing this file
922 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
923 * CF_CREATED -- created, but no content to extract (typically directories)
926 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
928 bpContext *plugin_ctx = jcr->plugin_ctx;
929 Plugin *plugin = jcr->plugin;
930 struct restore_pkt rp;
935 if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
939 rp.pkt_size = sizeof(rp);
940 rp.pkt_end = sizeof(rp);
941 rp.delta_seq = attr->delta_seq;
942 rp.stream = attr->stream;
943 rp.data_stream = attr->data_stream;
944 rp.type = attr->type;
945 rp.file_index = attr->file_index;
946 rp.LinkFI = attr->LinkFI;
948 rp.statp = attr->statp; /* structure assignment */
949 rp.attrEx = attr->attrEx;
950 rp.ofname = attr->ofname;
951 rp.olname = attr->olname;
952 rp.where = jcr->where;
953 rp.RegexWhere = jcr->RegexWhere;
954 rp.replace = jcr->replace;
955 rp.create_status = CF_ERROR;
956 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
957 rp.stream, rp.type, rp.LinkFI, rp.ofname);
959 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
962 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
963 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to createFile=%d %d\n",
964 plugin->createFileCalled, plugin->restoreFileStarted);
965 plugin->createFileCalled = false;
968 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
970 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
974 if (rp.create_status == CF_ERROR) {
975 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
980 if (rp.create_status == CF_SKIP) {
984 if (rp.create_status == CF_CORE) {
985 return CF_CORE; /* Let Bacula core handle the file creation */
988 /* Use the bfile for plugin */
989 set_cmd_plugin(bfd, jcr);
991 /* Created link or directory? */
992 if (rp.create_status == CF_CREATED) {
993 return rp.create_status; /* yes, no need to bopen */
998 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
999 Dmsg0(dbglvl, "call bopen\n");
1000 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
1001 Dmsg1(dbglvl, "bopen status=%d\n", stat);
1004 be.set_errno(bfd->berrno);
1005 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
1006 attr->ofname, be.bstrerror());
1007 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
1011 if (!is_bopen(bfd)) {
1012 Dmsg0(000, "===== BFD is not open!!!!\n");
1019 * Reset the file attributes after all file I/O is done -- this allows
1020 * the previous access time/dates to be set properly, and it also allows
1021 * us to properly set directory permissions.
1022 * Not currently Implemented.
1024 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
1026 Plugin *plugin = (Plugin *)jcr->plugin;
1027 struct restore_pkt rp;
1029 Dmsg0(dbglvl, "plugin_set_attributes\n");
1031 if (!plugin || !jcr->plugin_ctx) {
1035 memset(&rp, 0, sizeof(rp));
1036 rp.pkt_size = sizeof(rp);
1037 rp.pkt_end = sizeof(rp);
1038 rp.stream = attr->stream;
1039 rp.data_stream = attr->data_stream;
1040 rp.type = attr->type;
1041 rp.file_index = attr->file_index;
1042 rp.LinkFI = attr->LinkFI;
1044 rp.statp = attr->statp; /* structure assignment */
1045 rp.attrEx = attr->attrEx;
1046 rp.ofname = attr->ofname;
1047 rp.olname = attr->olname;
1048 rp.where = jcr->where;
1049 rp.RegexWhere = jcr->RegexWhere;
1050 rp.replace = jcr->replace;
1051 rp.create_status = CF_ERROR;
1053 plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
1055 if (rp.create_status == CF_CORE) {
1056 set_attributes(jcr, attr, ofd);
1058 if (is_bopen(ofd)) {
1061 pm_strcpy(attr->ofname, "*none*");
1069 * The Plugin ACL data backup. We are using a new Plugin callback:
1070 * handleXACLdata() for that. The new callback get a pointer to
1071 * struct xacl_pkt as a main argument which consist of the following
1073 * xacl.func - could be the one of BACL_BACKUP, BACL_RESTORE,
1074 * BXATTR_BACKUP, BXATTR_RESTORE
1075 * xacl.count - the length of data at the content buffer
1076 * xacl.content - the buffer itself
1077 * The buffer (xacl.content) is supplied by Bacula during restore and has to
1078 * be supplied by a Plugin during backup.
1079 * The new callback should return bRC_OK on success and bRC_Error on
1083 * jcr - Job Control Record
1084 * ff_pkt - file save packet
1085 * data is a pointer to variable returned
1087 * data - the pointer to data buffer returned from plugin
1088 * 0 - Success, no more data to save
1089 * > 0 - Success and the number of bytes returned in **data buffer
1090 * -1 - Error, no acls data to backup
1092 int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data)
1094 struct xacl_pkt xacl;
1095 Plugin *plugin = (Plugin *)jcr->plugin;
1098 Dmsg0(dbglvl, "plugin_backup_acl\n");
1100 /* check of input variables */
1101 if (!plugin || !jcr->plugin_ctx || !data) {
1105 /* prepare the xacl packet */
1106 memset(&xacl, 0, sizeof(xacl));
1107 xacl.pkt_size = sizeof(xacl);
1108 xacl.pkt_end = sizeof(xacl);
1109 xacl.func = BACL_BACKUP;
1111 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1113 /* check out status */
1115 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1118 if (xacl.count > 0){
1119 /* we have something to save, so prepare return data */
1120 *data = xacl.content;
1128 * Called here when Bacula got ACL stream to restore but not every stream but
1129 * a specific one: STREAM_XACL_PLUGIN_ACL which means a plugin has to
1133 * jcr - Job Control Record
1134 * data - content to restore
1135 * length - the length of the content to restore
1137 * true - when successful
1138 * false - on any Error
1140 bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length)
1142 struct xacl_pkt xacl;
1143 Plugin *plugin = (Plugin *)jcr->plugin;
1146 Dmsg0(dbglvl, "plugin_restore_acl\n");
1148 /* check of input variables */
1149 if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1153 /* prepare the xacl packet */
1154 memset(&xacl, 0, sizeof(xacl));
1155 xacl.pkt_size = sizeof(xacl);
1156 xacl.pkt_end = sizeof(xacl);
1157 xacl.func = BACL_RESTORE;
1158 xacl.content = data;
1159 xacl.count = length;
1161 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1163 /* check out status */
1165 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1173 * The Plugin XATTR data backup. We are using a new Plugin callback:
1174 * handleXACLdata() for that. Check plugin_backup_acl for new callback
1178 * jcr - Job Control Record
1179 * ff_pkt - file save packet
1180 * data is a pointer to variable returned
1182 * data - the pointer to data buffer returned from plugin
1183 * 0 - Success, no more data to save
1184 * >0 - Success and the number of bytes returned in **data buffer
1187 int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data)
1190 struct xacl_pkt xacl;
1191 Plugin *plugin = (Plugin *)jcr->plugin;
1194 Dmsg0(dbglvl, "plugin_backup_xattr\n");
1196 /* check of input variables */
1197 if (!plugin || !jcr->plugin_ctx || !data) {
1201 /* prepare the xacl packet */
1202 memset(&xacl, 0, sizeof(xacl));
1203 xacl.pkt_size = sizeof(xacl);
1204 xacl.pkt_end = sizeof(xacl);
1205 xacl.func = BXATTR_BACKUP;
1207 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1209 /* check out status */
1211 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1214 if (xacl.count > 0){
1215 /* we have something to save, so prepare return data */
1216 *data = xacl.content;
1224 * Called here when Bacula got XATTR stream to restore but not every stream but
1225 * a specific one: STREAM_XACL_PLUGIN_XATTR which means a plugin has to
1229 * jcr - Job Control Record
1230 * data - content to restore
1231 * length - the length of the content to restore
1233 * true - when successful
1234 * false - on any Error
1236 bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length)
1238 struct xacl_pkt xacl;
1239 Plugin *plugin = (Plugin *)jcr->plugin;
1242 Dmsg0(dbglvl, "plugin_restore_xattr\n");
1244 /* check of input variables */
1245 if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1249 /* prepare the xacl packet */
1250 memset(&xacl, 0, sizeof(xacl));
1251 xacl.pkt_size = sizeof(xacl);
1252 xacl.pkt_end = sizeof(xacl);
1253 xacl.func = BXATTR_RESTORE;
1254 xacl.content = data;
1255 xacl.count = length;
1257 rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1259 /* check out status */
1261 Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1269 * Print to file the plugin info.
1271 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1276 pInfo *info = (pInfo *)plugin->pinfo;
1277 fprintf(fp, "\tversion=%d\n", info->version);
1278 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1279 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1280 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1281 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1282 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1283 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1287 * This entry point is called internally by Bacula to ensure
1288 * that the plugin IO calls come into this code.
1290 void load_fd_plugins(const char *plugin_dir)
1296 Dmsg0(dbglvl, "plugin dir is NULL\n");
1300 b_plugin_list = New(alist(10, not_owned_by_alist));
1302 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1303 is_plugin_compatible)) {
1304 /* Either none found, or some error */
1305 if (b_plugin_list->size() == 0) {
1306 delete b_plugin_list;
1307 b_plugin_list = NULL;
1308 Dmsg0(dbglvl, "No plugins loaded\n");
1313 /* Plug entry points called from findlib */
1314 plugin_bopen = my_plugin_bopen;
1315 plugin_bclose = my_plugin_bclose;
1316 plugin_bread = my_plugin_bread;
1317 plugin_bwrite = my_plugin_bwrite;
1318 plugin_blseek = my_plugin_blseek;
1322 * Verify that the plugin is acceptable, and print information
1325 foreach_alist_index(i, plugin, b_plugin_list) {
1326 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1327 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1330 dbg_plugin_add_hook(dump_fd_plugin);
1335 * Check if a plugin is compatible. Called by the load_plugin function
1336 * to allow us to verify the plugin.
1338 static bool is_plugin_compatible(Plugin *plugin)
1340 pInfo *info = (pInfo *)plugin->pinfo;
1341 Dmsg0(dbglvl, "is_plugin_compatible called\n");
1343 if (chk_dbglvl(50)) {
1344 dump_fd_plugin(plugin, stdin);
1346 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1347 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1348 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1349 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1350 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1354 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1355 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1356 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1357 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1358 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1361 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1362 strcmp(info->plugin_license, "AGPLv3") != 0 &&
1363 strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
1364 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1365 plugin->file, info->plugin_license);
1366 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1367 plugin->file, info->plugin_license);
1370 if (info->size != sizeof(pInfo)) {
1371 Jmsg(NULL, M_ERROR, 0,
1372 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1373 plugin->file, sizeof(pInfo), info->size);
1383 * Create a new instance of each plugin for this Job
1384 * Note, b_plugin_list can exist but jcr->plugin_ctx_list can
1385 * be NULL if no plugins were loaded.
1387 void new_plugins(JCR *jcr)
1393 if (!b_plugin_list) {
1394 Dmsg0(dbglvl, "plugin list is NULL\n");
1397 if (jcr->is_job_canceled()) {
1401 int num = b_plugin_list->size();
1404 Dmsg0(dbglvl, "No plugins loaded\n");
1408 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1410 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1411 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1412 foreach_alist_index(i, plugin, b_plugin_list) {
1414 /* Start a new instance of each plugin */
1415 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1416 memset(b_ctx, 0, sizeof(bacula_ctx));
1418 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1419 plugin_ctx_list[i].pContext = NULL;
1420 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1421 Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
1422 b_ctx->disabled = true;
1426 Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1433 * Free the plugin instances for this Job
1435 void free_plugins(JCR *jcr)
1440 if (!b_plugin_list || !jcr->plugin_ctx_list) {
1441 return; /* no plugins, nothing to do */
1445 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1446 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1447 foreach_alist_index(i, plugin, b_plugin_list) {
1448 /* Free the plugin instance */
1449 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1450 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1454 free(plugin_ctx_list);
1455 jcr->plugin_ctx_list = NULL;
1458 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
1460 JCR *jcr = bfd->jcr;
1461 Plugin *plugin = (Plugin *)jcr->plugin;
1464 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1466 if (!plugin || !jcr->plugin_ctx) {
1469 io.pkt_size = sizeof(io);
1470 io.pkt_end = sizeof(io);
1480 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1481 bfd->berrno = io.io_errno;
1483 errno = b_errno_win32;
1485 errno = io.io_errno;
1486 bfd->lerror = io.lerror;
1488 Dmsg1(dbglvl, "Return from plugin open status=%d\n", io.status);
1493 static int my_plugin_bclose(BFILE *bfd)
1495 JCR *jcr = bfd->jcr;
1496 Plugin *plugin = (Plugin *)jcr->plugin;
1500 Dmsg0(dbglvl, "===== plugin_bclose\n");
1501 if (!plugin || !jcr->plugin_ctx) {
1504 io.pkt_size = sizeof(io);
1505 io.pkt_end = sizeof(io);
1512 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1513 bfd->berrno = io.io_errno;
1515 errno = b_errno_win32;
1517 errno = io.io_errno;
1518 bfd->lerror = io.lerror;
1520 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1525 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1527 JCR *jcr = bfd->jcr;
1528 Plugin *plugin = (Plugin *)jcr->plugin;
1532 Dmsg0(dbglvl, "plugin_bread\n");
1533 if (!plugin || !jcr->plugin_ctx) {
1536 io.pkt_size = sizeof(io);
1537 io.pkt_end = sizeof(io);
1540 io.buf = (char *)buf;
1545 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1546 bfd->offset = io.offset;
1547 bfd->berrno = io.io_errno;
1549 errno = b_errno_win32;
1551 errno = io.io_errno;
1552 bfd->lerror = io.lerror;
1555 return (ssize_t)io.status;
1558 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1560 JCR *jcr = bfd->jcr;
1561 Plugin *plugin = (Plugin *)jcr->plugin;
1565 Dmsg0(dbglvl, "plugin_bwrite\n");
1566 if (!plugin || !jcr->plugin_ctx) {
1567 Dmsg0(0, "No plugin context\n");
1570 io.pkt_size = sizeof(io);
1571 io.pkt_end = sizeof(io);
1574 io.buf = (char *)buf;
1578 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1579 bfd->berrno = io.io_errno;
1581 errno = b_errno_win32;
1583 errno = io.io_errno;
1584 bfd->lerror = io.lerror;
1587 return (ssize_t)io.status;
1590 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1592 JCR *jcr = bfd->jcr;
1593 Plugin *plugin = (Plugin *)jcr->plugin;
1597 Dmsg0(dbglvl, "plugin_bseek\n");
1598 if (!plugin || !jcr->plugin_ctx) {
1601 io.pkt_size = sizeof(io);
1602 io.pkt_end = sizeof(io);
1608 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1609 bfd->berrno = io.io_errno;
1611 errno = b_errno_win32;
1613 errno = io.io_errno;
1614 bfd->lerror = io.lerror;
1617 return (boffset_t)io.offset;
1620 /* ==============================================================
1622 * Callbacks from the plugin
1624 * ==============================================================
1626 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1634 switch (var) { /* General variables, no need of ctx */
1636 *((char **)value) = my_name;
1638 case bVarWorkingDir:
1639 *(void **)value = me->working_directory;
1642 *(char **)value = exepath;
1645 *(char **)value = version;
1648 *(char **)value = dist_name;
1650 case bVarPrevJobName:
1652 case bVarPrefixLinks:
1660 if (!ctx) { /* Other variables need context */
1664 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1671 *((int *)value) = jcr->JobId;
1672 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1675 *((int *)value) = jcr->getJobLevel();
1676 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1679 *((int *)value) = jcr->getJobType();
1680 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1683 *((char **)value) = jcr->client_name;
1684 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1687 *((char **)value) = jcr->Job;
1688 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1690 case bVarPrevJobName:
1691 *((char **)value) = jcr->PrevJob;
1692 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1695 *((int *)value) = jcr->JobStatus;
1696 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1699 *((int *)value) = (int)jcr->mtime;
1700 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1703 *((int *)value) = (int)jcr->accurate;
1704 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1706 case bVarInteractiveSession:
1707 *(int *)value = (int)jcr->interactive_session;
1710 break; /* a write only variable, ignore read request */
1713 case bVarVssDllHandle:
1716 *(char **)value = jcr->where;
1718 case bVarRegexWhere:
1719 *(char **)value = jcr->RegexWhere;
1721 case bVarPrefixLinks:
1722 *(int *)value = (int)jcr->prefix_links;
1725 *((int*)value) = jcr->replace;
1726 Dmsg1(dbglvl, "Bacula: return replace=%c\n", jcr->replace);
1728 case bVarFDName: /* get warning with g++ if we missed one */
1729 case bVarWorkingDir:
1740 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1744 if (!value || !ctx) {
1747 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1748 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1752 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1755 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1759 case bVarInteractiveSession:
1760 jcr->interactive_session = (((intptr_t) value) == 1);
1769 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1779 va_start(args, ctx);
1780 while ((event = va_arg(args, uint32_t))) {
1781 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1788 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1789 int type, utime_t mtime, const char *fmt, ...)
1797 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1802 va_start(arg_ptr, fmt);
1803 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1805 Jmsg(jcr, type, mtime, "%s", buf);
1810 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1811 int level, const char *fmt, ...)
1817 va_start(arg_ptr, fmt);
1818 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1820 d_msg(file, line, level, "%s", buf);
1825 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1829 return sm_malloc(file, line, size);
1831 return malloc(size);
1835 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1838 sm_free(file, line, mem);
1844 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1850 bctx = (bacula_ctx *)ctx->bContext;
1862 * Let the plugin define files/directories to be excluded
1863 * from the main backup.
1865 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1871 if (!is_ctx_good(ctx, jcr, bctx)) {
1878 /* Save the include context */
1879 old = get_incexe(jcr);
1881 /* Not right time to add exlude */
1886 if (!bctx->exclude) {
1887 bctx->exclude = new_exclude(jcr);
1890 /* Set the Exclude context */
1891 set_incexe(jcr, bctx->exclude);
1893 add_file_to_fileset(jcr, file, true);
1895 /* Restore the current context */
1896 set_incexe(jcr, old);
1898 Dmsg1(100, "Add exclude file=%s\n", file);
1904 * Let the plugin define files/directories to be excluded
1905 * from the main backup.
1907 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1914 if (!is_ctx_good(ctx, jcr, bctx)) {
1921 /* Save the include context */
1922 old = get_incexe(jcr);
1924 /* Not right time to add include */
1928 if (!bctx->include) {
1929 bctx->include = old;
1932 set_incexe(jcr, bctx->include);
1933 add_file_to_fileset(jcr, file, true);
1935 /* Restore the current context */
1936 set_incexe(jcr, old);
1938 Dmsg1(100, "Add include file=%s\n", file);
1943 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1948 if (!is_ctx_good(ctx, jcr, bctx)) {
1954 add_options_to_fileset(jcr, opts);
1956 Dmsg1(1000, "Add options=%s\n", opts);
1960 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1965 if (!is_ctx_good(ctx, jcr, bctx)) {
1971 add_regex_to_fileset(jcr, item, type);
1972 Dmsg1(100, "Add regex=%s\n", item);
1977 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1982 if (!is_ctx_good(ctx, jcr, bctx)) {
1988 add_wild_to_fileset(jcr, item, type);
1989 Dmsg1(100, "Add wild=%s\n", item);
1994 static bRC baculaNewOptions(bpContext *ctx)
1999 if (!is_ctx_good(ctx, jcr, bctx)) {
2002 (void)new_options(jcr, NULL);
2007 static bRC baculaNewInclude(bpContext *ctx)
2012 if (!is_ctx_good(ctx, jcr, bctx)) {
2015 (void)new_include(jcr);
2020 static bRC baculaNewPreInclude(bpContext *ctx)
2025 if (!is_ctx_good(ctx, jcr, bctx)) {
2029 bctx->include = new_preinclude(jcr);
2030 new_options(jcr, bctx->include);
2031 set_incexe(jcr, bctx->include);
2038 * Check if a file have to be backuped using Accurate code
2040 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
2045 bRC ret = bRC_Error;
2048 if (!is_ctx_good(ctx, jcr, bctx)) {
2057 * Copy fname and link because save_file() zaps them. This
2058 * avoids zaping the plugin's strings.
2060 ff_pkt->type = sp->type;
2062 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
2066 ff_pkt->fname = sp->fname;
2067 ff_pkt->link = sp->link;
2068 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
2070 if (check_changes(jcr, ff_pkt)) {
2076 /* check_changes() can update delta sequence number, return it to the
2079 sp->delta_seq = ff_pkt->delta_seq;
2080 sp->accurate_found = ff_pkt->accurate_found;
2084 Dmsg1(100, "checkChanges=%i\n", ret);
2089 * Check if a file would be saved using current Include/Exclude code
2091 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
2098 struct stat oldstat;
2099 bRC ret = bRC_Error;
2102 if (!is_ctx_good(ctx, jcr, bctx)) {
2111 /* Probably not needed, but keep a copy */
2112 old = ff_pkt->fname;
2113 oldstat = ff_pkt->statp;
2115 ff_pkt->fname = sp->fname;
2116 ff_pkt->statp = sp->statp;
2118 if (accept_file(ff_pkt)) {
2124 ff_pkt->fname = old;
2125 ff_pkt->statp = oldstat;
2133 int (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
2134 int (*plugin_bclose)(JCR *jcr) = NULL;
2135 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
2136 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
2137 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
2139 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
2144 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
2149 int main(int argc, char *argv[])
2151 char plugin_dir[1000];
2156 strcpy(my_name, "test-fd");
2158 getcwd(plugin_dir, sizeof(plugin_dir)-1);
2159 load_fd_plugins(plugin_dir);
2167 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
2168 generate_plugin_event(jcr1, bEventJobEnd);
2169 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
2171 generate_plugin_event(jcr2, bEventJobEnd);
2176 Dmsg0(dbglvl, "bacula: OK ...\n");
2177 close_memory_pool();
2178 sm_dump(false); /* unit test */
2182 #endif /* TEST_PROGRAM */