2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Main program to test loading and running Bacula plugins.
22 * Destined to become Bacula pluginloader, ...
24 * Kern Sibbald, October 2007
30 extern DLL_IMP_EXP char *exepath;
31 extern DLL_IMP_EXP char *version;
32 extern DLL_IMP_EXP char *dist_name;
34 const int dbglvl = 150;
35 const char *plugin_type = "-fd.so";
37 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
38 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
40 /* Function pointers to be set here */
41 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
42 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
43 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
44 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
45 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
48 /* Forward referenced functions */
49 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
50 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
51 static bRC baculaRegisterEvents(bpContext *ctx, ...);
52 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
53 int type, utime_t mtime, const char *fmt, ...);
54 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
55 int level, const char *fmt, ...);
56 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
58 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
59 static bRC baculaAddExclude(bpContext *ctx, const char *file);
60 static bRC baculaAddInclude(bpContext *ctx, const char *file);
61 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
62 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
63 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
64 static bRC baculaNewOptions(bpContext *ctx);
65 static bRC baculaNewInclude(bpContext *ctx);
66 static bRC baculaNewPreInclude(bpContext *ctx);
67 static bool is_plugin_compatible(Plugin *plugin);
68 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
69 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
70 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
73 * These will be plugged into the global pointer structure for
76 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
77 static int my_plugin_bclose(BFILE *bfd);
78 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
79 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
80 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
84 static bInfo binfo = {
86 FD_PLUGIN_INTERFACE_VERSION
89 /* Bacula entry points */
90 static bFuncs bfuncs = {
92 FD_PLUGIN_INTERFACE_VERSION,
113 * Bacula private context
116 JCR *jcr; /* jcr for plugin */
117 bRC rc; /* last return code */
118 bool disabled; /* set if plugin disabled */
119 findINCEXE *exclude; /* pointer to exclude files */
120 findINCEXE *include; /* pointer to include/exclude files */
124 * Test if event is for this plugin
126 static bool for_this_plugin(Plugin *plugin, char *name, int len)
128 Dmsg4(dbglvl, "name=%s len=%d plugin=%s plen=%d\n", name, len, plugin->file, plugin->file_len);
129 if (!name) { /* if no plugin name, all plugins get it */
132 /* Return global VSS job metadata to all plugins */
133 if (strcmp("job", name) == 0) { /* old V4.0 name for VSS job metadata */
136 if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
139 /* Check if this is the correct plugin */
140 if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
147 bool is_plugin_disabled(bpContext *plugin_ctx)
154 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
158 return b_ctx->disabled;
161 bool is_plugin_disabled(JCR *jcr)
163 return is_plugin_disabled(jcr->plugin_ctx);
167 * Create a plugin event When receiving bEventCancelCommand, this function is
168 * called by an other thread.
170 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
172 bpContext *plugin_ctx;
178 bool call_if_canceled = false;
179 restore_object_pkt *rop;
182 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list) {
183 return; /* Return if no plugins loaded */
187 * Some events are sent to only a particular plugin or must be
188 * called even if the job is canceled
191 case bEventPluginCommand:
192 case bEventOptionPlugin:
193 name = (char *)value;
194 if (!get_plugin_name(jcr, name, &len)) {
198 case bEventRestoreObject:
199 /* After all RestoreObject, we have it one more time with value=NULL */
201 /* Some RestoreObjects may not have a plugin name */
202 rop = (restore_object_pkt *)value;
203 if (*rop->plugin_name) {
204 name = rop->plugin_name;
205 get_plugin_name(jcr, name, &len);
210 case bEventEndBackupJob:
211 case bEventEndVerifyJob:
212 call_if_canceled = true; /* plugin *must* see this call */
214 case bEventStartRestoreJob:
215 foreach_alist_index(i, plugin, b_plugin_list) {
216 plugin->restoreFileStarted = false;
217 plugin->createFileCalled = false;
220 case bEventEndRestoreJob:
221 call_if_canceled = true; /* plugin *must* see this call */
227 /* If call_if_canceled is set, we call the plugin anyway */
228 if (!call_if_canceled && jcr->is_job_canceled()) {
232 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
233 event.eventType = eventType;
235 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
238 * Pass event to every plugin (except if name is set). If name
239 * is set, we pass it only to the plugin with that name.
241 foreach_alist_index(i, plugin, b_plugin_list) {
242 if (!for_this_plugin(plugin, name, len)) {
243 Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
244 name, name==NULL?1:0);
248 * Note, at this point do not change
249 * jcr->plugin or jcr->plugin_ctx
252 plugin_ctx = &plugin_ctx_list[i];
253 if (is_plugin_disabled(plugin_ctx)) {
254 Dmsg1(50, "Plugin %s disabled\n", plugin->file);
257 if (eventType == bEventEndRestoreJob) {
258 Dmsg0(50, "eventType==bEventEndRestoreJob\n");
259 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
260 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
263 jcr->plugin->restoreFileStarted = false;
264 jcr->plugin->createFileCalled = false;
267 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
273 * Check if file was seen for accurate
275 bool plugin_check_file(JCR *jcr, char *fname)
282 if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
283 return false; /* Return if no plugins loaded */
286 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
288 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
290 /* Pass event to every plugin */
291 foreach_alist_index(i, plugin, b_plugin_list) {
292 jcr->plugin_ctx = &plugin_ctx_list[i];
293 jcr->plugin = plugin;
294 if (is_plugin_disabled(jcr)) {
297 if (plug_func(plugin)->checkFile == NULL) {
300 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
301 if (rc == bRC_Seen) {
308 jcr->plugin_ctx = NULL;
309 return rc == bRC_Seen;
312 /* Get the first part of the the plugin command
313 * systemstate:/@SYSTEMSTATE/
315 * => can use for_this_plugin(plug, cmd, ret);
317 * The plugin command can contain only the plugin name
321 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
326 if (!cmd || (*cmd == '\0')) {
329 /* Handle plugin command here backup */
330 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
331 if ((p = strchr(cmd, ':')) == NULL) {
332 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
335 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
338 } else { /* plugin:argument */
350 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
353 ff_pkt->no_read = sp->no_read;
354 ff_pkt->delta_seq = sp->delta_seq;
355 if (sp->flags & FO_DELTA) {
356 ff_pkt->flags |= FO_DELTA;
357 ff_pkt->delta_seq++; /* make new delta sequence number */
359 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
360 ff_pkt->delta_seq = 0;
363 if (sp->flags & FO_OFFSETS) {
364 ff_pkt->flags |= FO_OFFSETS;
366 ff_pkt->flags &= ~FO_OFFSETS;
368 /* Sparse code doesn't work with plugins
369 * that use FIFO or STDOUT/IN to communicate
371 if (sp->flags & FO_SPARSE) {
372 ff_pkt->flags |= FO_SPARSE;
374 ff_pkt->flags &= ~FO_SPARSE;
376 if (sp->flags & FO_PORTABLE_DATA) {
377 ff_pkt->flags |= FO_PORTABLE_DATA;
379 ff_pkt->flags &= ~FO_PORTABLE_DATA;
381 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
385 /* Ask to a Option Plugin what to do with the current file */
386 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
391 char *cmd = ff_pkt->plugin;
395 event.eventType = bEventHandleBackupFile;
398 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
399 memset(sp, 0, sizeof(struct save_pkt));
400 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
403 sp->link = ff_pkt->link;
404 sp->cmd = ff_pkt->plugin;
405 sp->statp = ff_pkt->statp;
406 sp->fname = ff_pkt->fname;
407 sp->delta_seq = ff_pkt->delta_seq;
408 sp->accurate_found = ff_pkt->accurate_found;
410 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
411 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
412 goto bail_out; /* Return if no plugins loaded */
415 if (!get_plugin_name(jcr, cmd, &len)) {
419 /* Note, we stop the loop on the first plugin that matches the name */
420 foreach_alist_index(i, plugin, b_plugin_list) {
421 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
422 if (!for_this_plugin(plugin, cmd, len)) {
429 if (is_plugin_disabled(&plugin_ctx_list[i])) {
433 jcr->plugin_ctx = &plugin_ctx_list[i];
434 jcr->plugin = plugin;
436 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
439 /* TODO: would be better to set this in save_file() */
441 jcr->opt_plugin = true;
442 jcr->plugin = plugin;
443 jcr->plugin_sp = sp; /* Unset sp in save_file */
444 jcr->plugin_ctx = &plugin_ctx_list[i];
446 update_ff_pkt(ff_pkt, sp);
448 /* reset plugin in JCR if not used this time */
450 jcr->plugin_ctx = NULL;
455 } /* end foreach loop */
458 Jmsg1(jcr, M_FATAL, 0, "Options plugin \"%s\" not found.\n", cmd);
465 * Sequence of calls for a backup:
466 * 1. plugin_save() here is called with ff_pkt
467 * 2. we find the plugin requested on the command string
468 * 3. we generate a bEventBackupCommand event to the specified plugin
469 * and pass it the command string.
470 * 4. we make a startPluginBackup call to the plugin, which gives
471 * us the data we need in save_pkt
472 * 5. we call Bacula's save_file() subroutine to save the specified
473 * file. The plugin will be called at pluginIO() to supply the
476 * Sequence of calls for restore:
477 * See subroutine plugin_name_stream() below.
479 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
484 char *cmd = ff_pkt->top_fname;
487 POOL_MEM fname(PM_FNAME);
488 POOL_MEM link(PM_FNAME);
491 if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
492 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
493 return 1; /* Return if no plugins loaded */
496 jcr->cmd_plugin = true;
497 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
498 event.eventType = bEventBackupCommand;
500 if (!get_plugin_name(jcr, cmd, &len)) {
504 /* Note, we stop the loop on the first plugin that matches the name */
505 foreach_alist_index(i, plugin, b_plugin_list) {
506 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
507 if (!for_this_plugin(plugin, cmd, len)) {
511 * We put the current plugin pointer, and the plugin context
512 * into the jcr, because during save_file(), the plugin
513 * will be called many times and these values are needed.
516 jcr->plugin_ctx = &plugin_ctx_list[i];
517 jcr->plugin = plugin;
518 if (is_plugin_disabled(jcr)) {
522 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
523 /* Send the backup command to the right plugin*/
524 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
527 /* Loop getting filenames to backup then saving them */
528 while (!jcr->is_job_canceled()) {
529 memset(&sp, 0, sizeof(sp));
530 sp.pkt_size = sizeof(sp);
531 sp.pkt_end = sizeof(sp);
536 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
539 /* Get the file save parameters. I.e. the stat pkt ... */
540 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
544 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
548 jcr->plugin_sp = &sp;
551 * Copy fname and link because save_file() zaps them. This
552 * avoids zaping the plugin's strings.
554 ff_pkt->type = sp.type;
555 if (IS_FT_OBJECT(sp.type)) {
556 if (!sp.object_name) {
557 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
561 ff_pkt->fname = cmd; /* full plugin string */
562 ff_pkt->object_name = sp.object_name;
563 ff_pkt->object_index = sp.index; /* restore object index */
564 ff_pkt->object_compression = 0; /* no compression for now */
565 ff_pkt->object = sp.object;
566 ff_pkt->object_len = sp.object_len;
570 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
574 pm_strcpy(fname, sp.fname);
575 pm_strcpy(link, sp.link);
578 ff_pkt->fname = fname.c_str();
579 ff_pkt->link = link.c_str();
580 update_ff_pkt(ff_pkt, &sp);
583 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
584 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
586 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
588 /* Call Bacula core code to backup the plugin's file */
589 save_file(jcr, ff_pkt, true);
590 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
591 if (rc == bRC_More || rc == bRC_OK) {
592 accurate_mark_file_as_seen(jcr, fname.c_str());
595 if (rc == bRC_More) {
599 } /* end while loop */
601 } /* end loop over all plugins */
602 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
606 jcr->cmd_plugin = false;
608 jcr->plugin_ctx = NULL;
614 * Sequence of calls for a estimate:
615 * 1. plugin_estimate() here is called with ff_pkt
616 * 2. we find the plugin requested on the command string
617 * 3. we generate a bEventEstimateCommand event to the specified plugin
618 * and pass it the command string.
619 * 4. we make a startPluginBackup call to the plugin, which gives
620 * us the data we need in save_pkt
623 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
628 char *cmd = ff_pkt->top_fname;
631 POOL_MEM fname(PM_FNAME);
632 POOL_MEM link(PM_FNAME);
636 if (!b_plugin_list || !jcr->plugin_ctx_list) {
637 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
638 return 1; /* Return if no plugins loaded */
641 jcr->cmd_plugin = true;
642 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
643 event.eventType = bEventEstimateCommand;
645 if (!get_plugin_name(jcr, cmd, &len)) {
649 /* Note, we stop the loop on the first plugin that matches the name */
650 foreach_alist_index(i, plugin, b_plugin_list) {
651 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
652 if (!for_this_plugin(plugin, cmd, len)) {
656 * We put the current plugin pointer, and the plugin context
657 * into the jcr, because during save_file(), the plugin
658 * will be called many times and these values are needed.
661 jcr->plugin_ctx = &plugin_ctx_list[i];
662 jcr->plugin = plugin;
663 if (is_plugin_disabled(jcr)) {
667 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
668 /* Send the backup command to the right plugin*/
669 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
672 /* Loop getting filenames to backup then saving them */
673 while (!jcr->is_job_canceled()) {
675 memset(&sp, 0, sizeof(sp));
676 sp.pkt_size = sizeof(sp);
677 sp.pkt_end = sizeof(sp);
681 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
683 /* Get the file save parameters. I.e. the stat pkt ... */
684 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
688 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
693 if (!IS_FT_OBJECT(sp.type)) {
695 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
700 /* Count only files backed up */
710 jcr->JobFiles++; /* increment number of files backed up */
715 jcr->num_files_examined++;
717 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
718 if (sp.statp.st_size > 0) {
719 jcr->JobBytes += sp.statp.st_size;
724 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
726 attr.ofname = (POOLMEM *)sp.fname;
727 attr.olname = (POOLMEM *)sp.link;
728 print_ls_output(jcr, &attr);
732 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
734 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
736 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
737 if (rc == bRC_More || rc == bRC_OK) {
738 accurate_mark_file_as_seen(jcr, sp.fname);
741 if (rc == bRC_More) {
745 } /* end while loop */
747 } /* end loop over all plugins */
748 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
752 jcr->cmd_plugin = false;
754 jcr->plugin_ctx = NULL;
759 * Send plugin name start/end record to SD
761 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
764 int index = jcr->JobFiles;
765 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
769 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
772 if (jcr->is_job_canceled()) {
777 index++; /* JobFiles not incremented yet */
779 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
780 /* Send stream header */
782 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
783 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
787 Dmsg1(dbglvl, "send plugin name hdr: %s\n", sd->msg);
791 /* Send data -- not much */
792 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
794 /* Send end of data */
795 stat = sd->fsend("%ld 0", jcr->JobFiles);
799 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
803 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
804 sd->signal(BNET_EOD); /* indicate end of plugin name data */
810 * Plugin name stream found during restore. The record passed in
811 * argument name was generated in send_plugin_name() above.
813 * Returns: true if start of stream
814 * false if end of steam
816 bool plugin_name_stream(JCR *jcr, char *name)
824 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
827 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
828 skip_nonspaces(&p); /* skip over jcr->JobFiles */
832 /* Start of plugin data */
833 skip_nonspaces(&p); /* skip start/end flag */
835 // portable = *p == '1';
836 skip_nonspaces(&p); /* skip portable flag */
841 * End of plugin data, notify plugin, then clear flags
843 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
844 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
845 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
848 jcr->plugin->restoreFileStarted = false;
849 jcr->plugin->createFileCalled = false;
851 jcr->plugin_ctx = NULL;
856 if (!plugin_ctx_list) {
861 * After this point, we are dealing with a restore start
863 if (!get_plugin_name(jcr, cmd, &len)) {
868 * Search for correct plugin as specified on the command
871 foreach_alist_index(i, plugin, b_plugin_list) {
873 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
874 if (!for_this_plugin(plugin, cmd, len)) {
878 jcr->plugin_ctx = &plugin_ctx_list[i];
879 jcr->plugin = plugin;
880 if (is_plugin_disabled(jcr)) {
881 Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
884 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
885 event.eventType = bEventRestoreCommand;
886 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
887 &event, cmd) != bRC_OK) {
888 Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
891 if (plugin->restoreFileStarted) {
892 Jmsg2(jcr, M_FATAL, 0, "Second call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
893 plugin->restoreFileStarted = false;
896 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
897 plugin->restoreFileStarted = true;
900 Dmsg1(dbglvl, "startRestoreFile failed. plugin=%s\n", cmd);
904 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
913 jcr->plugin_ctx = NULL;
918 * Tell the plugin to create the file. Return values are
919 * This is called only during Restore
922 * CF_SKIP -- skip processing this file
923 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
924 * CF_CREATED -- created, but no content to extract (typically directories)
927 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
929 bpContext *plugin_ctx = jcr->plugin_ctx;
930 Plugin *plugin = jcr->plugin;
931 struct restore_pkt rp;
936 if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
940 rp.pkt_size = sizeof(rp);
941 rp.pkt_end = sizeof(rp);
942 rp.delta_seq = attr->delta_seq;
943 rp.stream = attr->stream;
944 rp.data_stream = attr->data_stream;
945 rp.type = attr->type;
946 rp.file_index = attr->file_index;
947 rp.LinkFI = attr->LinkFI;
949 rp.statp = attr->statp; /* structure assignment */
950 rp.attrEx = attr->attrEx;
951 rp.ofname = attr->ofname;
952 rp.olname = attr->olname;
953 rp.where = jcr->where;
954 rp.RegexWhere = jcr->RegexWhere;
955 rp.replace = jcr->replace;
956 rp.create_status = CF_ERROR;
957 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
958 rp.stream, rp.type, rp.LinkFI, rp.ofname);
960 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
963 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
964 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to createFile=%d %d\n",
965 plugin->createFileCalled, plugin->restoreFileStarted);
966 plugin->createFileCalled = false;
969 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
971 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
975 if (rp.create_status == CF_ERROR) {
976 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
981 if (rp.create_status == CF_SKIP) {
985 if (rp.create_status == CF_CORE) {
986 return CF_CORE; /* Let Bacula core handle the file creation */
989 /* Use the bfile for plugin */
990 set_cmd_plugin(bfd, jcr);
992 /* Created link or directory? */
993 if (rp.create_status == CF_CREATED) {
994 return rp.create_status; /* yes, no need to bopen */
999 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
1000 Dmsg0(dbglvl, "call bopen\n");
1001 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
1002 Dmsg1(dbglvl, "bopen status=%d\n", stat);
1005 be.set_errno(bfd->berrno);
1006 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
1007 attr->ofname, be.bstrerror());
1008 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
1012 if (!is_bopen(bfd)) {
1013 Dmsg0(000, "===== BFD is not open!!!!\n");
1020 * Reset the file attributes after all file I/O is done -- this allows
1021 * the previous access time/dates to be set properly, and it also allows
1022 * us to properly set directory permissions.
1023 * Not currently Implemented.
1025 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
1027 Plugin *plugin = (Plugin *)jcr->plugin;
1028 struct restore_pkt rp;
1030 Dmsg0(dbglvl, "plugin_set_attributes\n");
1032 if (!plugin || !jcr->plugin_ctx) {
1036 memset(&rp, 0, sizeof(rp));
1037 rp.pkt_size = sizeof(rp);
1038 rp.pkt_end = sizeof(rp);
1039 rp.stream = attr->stream;
1040 rp.data_stream = attr->data_stream;
1041 rp.type = attr->type;
1042 rp.file_index = attr->file_index;
1043 rp.LinkFI = attr->LinkFI;
1045 rp.statp = attr->statp; /* structure assignment */
1046 rp.attrEx = attr->attrEx;
1047 rp.ofname = attr->ofname;
1048 rp.olname = attr->olname;
1049 rp.where = jcr->where;
1050 rp.RegexWhere = jcr->RegexWhere;
1051 rp.replace = jcr->replace;
1052 rp.create_status = CF_ERROR;
1054 plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
1056 if (rp.create_status == CF_CORE) {
1057 set_attributes(jcr, attr, ofd);
1059 if (is_bopen(ofd)) {
1062 pm_strcpy(attr->ofname, "*none*");
1070 * Print to file the plugin info.
1072 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1077 pInfo *info = (pInfo *)plugin->pinfo;
1078 fprintf(fp, "\tversion=%d\n", info->version);
1079 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1080 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1081 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1082 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1083 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1084 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1088 * This entry point is called internally by Bacula to ensure
1089 * that the plugin IO calls come into this code.
1091 void load_fd_plugins(const char *plugin_dir)
1097 Dmsg0(dbglvl, "plugin dir is NULL\n");
1101 b_plugin_list = New(alist(10, not_owned_by_alist));
1103 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1104 is_plugin_compatible)) {
1105 /* Either none found, or some error */
1106 if (b_plugin_list->size() == 0) {
1107 delete b_plugin_list;
1108 b_plugin_list = NULL;
1109 Dmsg0(dbglvl, "No plugins loaded\n");
1114 /* Plug entry points called from findlib */
1115 plugin_bopen = my_plugin_bopen;
1116 plugin_bclose = my_plugin_bclose;
1117 plugin_bread = my_plugin_bread;
1118 plugin_bwrite = my_plugin_bwrite;
1119 plugin_blseek = my_plugin_blseek;
1123 * Verify that the plugin is acceptable, and print information
1126 foreach_alist_index(i, plugin, b_plugin_list) {
1127 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1128 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1131 dbg_plugin_add_hook(dump_fd_plugin);
1136 * Check if a plugin is compatible. Called by the load_plugin function
1137 * to allow us to verify the plugin.
1139 static bool is_plugin_compatible(Plugin *plugin)
1141 pInfo *info = (pInfo *)plugin->pinfo;
1142 Dmsg0(dbglvl, "is_plugin_compatible called\n");
1144 if (chk_dbglvl(50)) {
1145 dump_fd_plugin(plugin, stdin);
1147 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1148 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1149 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1150 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1151 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1155 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1156 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1157 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1158 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1159 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1162 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1163 strcmp(info->plugin_license, "AGPLv3") != 0 &&
1164 strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
1165 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1166 plugin->file, info->plugin_license);
1167 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1168 plugin->file, info->plugin_license);
1171 if (info->size != sizeof(pInfo)) {
1172 Jmsg(NULL, M_ERROR, 0,
1173 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1174 plugin->file, sizeof(pInfo), info->size);
1184 * Create a new instance of each plugin for this Job
1185 * Note, b_plugin_list can exist but jcr->plugin_ctx_list can
1186 * be NULL if no plugins were loaded.
1188 void new_plugins(JCR *jcr)
1194 if (!b_plugin_list) {
1195 Dmsg0(dbglvl, "plugin list is NULL\n");
1198 if (jcr->is_job_canceled()) {
1202 int num = b_plugin_list->size();
1205 Dmsg0(dbglvl, "No plugins loaded\n");
1209 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1211 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1212 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1213 foreach_alist_index(i, plugin, b_plugin_list) {
1215 /* Start a new instance of each plugin */
1216 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1217 memset(b_ctx, 0, sizeof(bacula_ctx));
1219 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1220 plugin_ctx_list[i].pContext = NULL;
1221 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1222 Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
1223 b_ctx->disabled = true;
1227 Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1234 * Free the plugin instances for this Job
1236 void free_plugins(JCR *jcr)
1241 if (!b_plugin_list || !jcr->plugin_ctx_list) {
1242 return; /* no plugins, nothing to do */
1246 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1247 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1248 foreach_alist_index(i, plugin, b_plugin_list) {
1249 /* Free the plugin instance */
1250 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1251 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1255 free(plugin_ctx_list);
1256 jcr->plugin_ctx_list = NULL;
1259 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
1261 JCR *jcr = bfd->jcr;
1262 Plugin *plugin = (Plugin *)jcr->plugin;
1265 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1267 if (!plugin || !jcr->plugin_ctx) {
1270 io.pkt_size = sizeof(io);
1271 io.pkt_end = sizeof(io);
1281 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1282 bfd->berrno = io.io_errno;
1284 errno = b_errno_win32;
1286 errno = io.io_errno;
1287 bfd->lerror = io.lerror;
1289 Dmsg1(dbglvl, "Return from plugin open status=%d\n", io.status);
1294 static int my_plugin_bclose(BFILE *bfd)
1296 JCR *jcr = bfd->jcr;
1297 Plugin *plugin = (Plugin *)jcr->plugin;
1301 Dmsg0(dbglvl, "===== plugin_bclose\n");
1302 if (!plugin || !jcr->plugin_ctx) {
1305 io.pkt_size = sizeof(io);
1306 io.pkt_end = sizeof(io);
1313 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1314 bfd->berrno = io.io_errno;
1316 errno = b_errno_win32;
1318 errno = io.io_errno;
1319 bfd->lerror = io.lerror;
1321 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1326 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1328 JCR *jcr = bfd->jcr;
1329 Plugin *plugin = (Plugin *)jcr->plugin;
1333 Dmsg0(dbglvl, "plugin_bread\n");
1334 if (!plugin || !jcr->plugin_ctx) {
1337 io.pkt_size = sizeof(io);
1338 io.pkt_end = sizeof(io);
1341 io.buf = (char *)buf;
1346 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1347 bfd->offset = io.offset;
1348 bfd->berrno = io.io_errno;
1350 errno = b_errno_win32;
1352 errno = io.io_errno;
1353 bfd->lerror = io.lerror;
1356 return (ssize_t)io.status;
1359 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1361 JCR *jcr = bfd->jcr;
1362 Plugin *plugin = (Plugin *)jcr->plugin;
1366 Dmsg0(dbglvl, "plugin_bwrite\n");
1367 if (!plugin || !jcr->plugin_ctx) {
1368 Dmsg0(0, "No plugin context\n");
1371 io.pkt_size = sizeof(io);
1372 io.pkt_end = sizeof(io);
1375 io.buf = (char *)buf;
1379 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1380 bfd->berrno = io.io_errno;
1382 errno = b_errno_win32;
1384 errno = io.io_errno;
1385 bfd->lerror = io.lerror;
1388 return (ssize_t)io.status;
1391 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1393 JCR *jcr = bfd->jcr;
1394 Plugin *plugin = (Plugin *)jcr->plugin;
1398 Dmsg0(dbglvl, "plugin_bseek\n");
1399 if (!plugin || !jcr->plugin_ctx) {
1402 io.pkt_size = sizeof(io);
1403 io.pkt_end = sizeof(io);
1409 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1410 bfd->berrno = io.io_errno;
1412 errno = b_errno_win32;
1414 errno = io.io_errno;
1415 bfd->lerror = io.lerror;
1418 return (boffset_t)io.offset;
1421 /* ==============================================================
1423 * Callbacks from the plugin
1425 * ==============================================================
1427 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1435 switch (var) { /* General variables, no need of ctx */
1437 *((char **)value) = my_name;
1439 case bVarWorkingDir:
1440 *(void **)value = me->working_directory;
1443 *(char **)value = exepath;
1446 *(char **)value = version;
1449 *(char **)value = dist_name;
1451 case bVarPrevJobName:
1453 case bVarPrefixLinks:
1461 if (!ctx) { /* Other variables need context */
1465 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1472 *((int *)value) = jcr->JobId;
1473 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1476 *((int *)value) = jcr->getJobLevel();
1477 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1480 *((int *)value) = jcr->getJobType();
1481 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1484 *((char **)value) = jcr->client_name;
1485 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1488 *((char **)value) = jcr->Job;
1489 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1491 case bVarPrevJobName:
1492 *((char **)value) = jcr->PrevJob;
1493 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1496 *((int *)value) = jcr->JobStatus;
1497 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1500 *((int *)value) = (int)jcr->mtime;
1501 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1504 *((int *)value) = (int)jcr->accurate;
1505 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1507 case bVarInteractiveSession:
1508 *(int *)value = (int)jcr->interactive_session;
1511 break; /* a write only variable, ignore read request */
1514 case bVarVssDllHandle:
1517 *(char **)value = jcr->where;
1519 case bVarRegexWhere:
1520 *(char **)value = jcr->RegexWhere;
1522 case bVarPrefixLinks:
1523 *(int *)value = (int)jcr->prefix_links;
1525 case bVarFDName: /* get warning with g++ if we missed one */
1526 case bVarWorkingDir:
1537 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1541 if (!value || !ctx) {
1544 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1545 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1549 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1552 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1556 case bVarInteractiveSession:
1557 jcr->interactive_session = (((intptr_t) value) == 1);
1566 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1576 va_start(args, ctx);
1577 while ((event = va_arg(args, uint32_t))) {
1578 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1585 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1586 int type, utime_t mtime, const char *fmt, ...)
1594 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1599 va_start(arg_ptr, fmt);
1600 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1602 Jmsg(jcr, type, mtime, "%s", buf);
1607 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1608 int level, const char *fmt, ...)
1614 va_start(arg_ptr, fmt);
1615 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1617 d_msg(file, line, level, "%s", buf);
1622 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1626 return sm_malloc(file, line, size);
1628 return malloc(size);
1632 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1635 sm_free(file, line, mem);
1641 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1647 bctx = (bacula_ctx *)ctx->bContext;
1659 * Let the plugin define files/directories to be excluded
1660 * from the main backup.
1662 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1668 if (!is_ctx_good(ctx, jcr, bctx)) {
1675 /* Save the include context */
1676 old = get_incexe(jcr);
1678 /* Not right time to add exlude */
1683 if (!bctx->exclude) {
1684 bctx->exclude = new_exclude(jcr);
1687 /* Set the Exclude context */
1688 set_incexe(jcr, bctx->exclude);
1690 add_file_to_fileset(jcr, file, true);
1692 /* Restore the current context */
1693 set_incexe(jcr, old);
1695 Dmsg1(100, "Add exclude file=%s\n", file);
1701 * Let the plugin define files/directories to be excluded
1702 * from the main backup.
1704 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1711 if (!is_ctx_good(ctx, jcr, bctx)) {
1718 /* Save the include context */
1719 old = get_incexe(jcr);
1721 /* Not right time to add include */
1725 if (!bctx->include) {
1726 bctx->include = old;
1729 set_incexe(jcr, bctx->include);
1730 add_file_to_fileset(jcr, file, true);
1732 /* Restore the current context */
1733 set_incexe(jcr, old);
1735 Dmsg1(100, "Add include file=%s\n", file);
1740 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1745 if (!is_ctx_good(ctx, jcr, bctx)) {
1751 add_options_to_fileset(jcr, opts);
1753 Dmsg1(1000, "Add options=%s\n", opts);
1757 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1762 if (!is_ctx_good(ctx, jcr, bctx)) {
1768 add_regex_to_fileset(jcr, item, type);
1769 Dmsg1(100, "Add regex=%s\n", item);
1774 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1779 if (!is_ctx_good(ctx, jcr, bctx)) {
1785 add_wild_to_fileset(jcr, item, type);
1786 Dmsg1(100, "Add wild=%s\n", item);
1791 static bRC baculaNewOptions(bpContext *ctx)
1796 if (!is_ctx_good(ctx, jcr, bctx)) {
1799 (void)new_options(jcr, NULL);
1804 static bRC baculaNewInclude(bpContext *ctx)
1809 if (!is_ctx_good(ctx, jcr, bctx)) {
1812 (void)new_include(jcr);
1817 static bRC baculaNewPreInclude(bpContext *ctx)
1822 if (!is_ctx_good(ctx, jcr, bctx)) {
1826 bctx->include = new_preinclude(jcr);
1827 new_options(jcr, bctx->include);
1828 set_incexe(jcr, bctx->include);
1835 * Check if a file have to be backuped using Accurate code
1837 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1842 bRC ret = bRC_Error;
1845 if (!is_ctx_good(ctx, jcr, bctx)) {
1854 * Copy fname and link because save_file() zaps them. This
1855 * avoids zaping the plugin's strings.
1857 ff_pkt->type = sp->type;
1859 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1863 ff_pkt->fname = sp->fname;
1864 ff_pkt->link = sp->link;
1865 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1867 if (check_changes(jcr, ff_pkt)) {
1873 /* check_changes() can update delta sequence number, return it to the
1876 sp->delta_seq = ff_pkt->delta_seq;
1877 sp->accurate_found = ff_pkt->accurate_found;
1881 Dmsg1(100, "checkChanges=%i\n", ret);
1886 * Check if a file would be saved using current Include/Exclude code
1888 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
1895 struct stat oldstat;
1896 bRC ret = bRC_Error;
1899 if (!is_ctx_good(ctx, jcr, bctx)) {
1908 /* Probably not needed, but keep a copy */
1909 old = ff_pkt->fname;
1910 oldstat = ff_pkt->statp;
1912 ff_pkt->fname = sp->fname;
1913 ff_pkt->statp = sp->statp;
1915 if (accept_file(ff_pkt)) {
1921 ff_pkt->fname = old;
1922 ff_pkt->statp = oldstat;
1930 int (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
1931 int (*plugin_bclose)(JCR *jcr) = NULL;
1932 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1933 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1934 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1936 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1941 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1946 int main(int argc, char *argv[])
1948 char plugin_dir[1000];
1953 strcpy(my_name, "test-fd");
1955 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1956 load_fd_plugins(plugin_dir);
1964 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1965 generate_plugin_event(jcr1, bEventJobEnd);
1966 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1968 generate_plugin_event(jcr2, bEventJobEnd);
1973 Dmsg0(dbglvl, "bacula: OK ...\n");
1974 close_memory_pool();
1975 sm_dump(false); /* unit test */
1979 #endif /* TEST_PROGRAM */