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 * Print to file the plugin info.
1071 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1076 pInfo *info = (pInfo *)plugin->pinfo;
1077 fprintf(fp, "\tversion=%d\n", info->version);
1078 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1079 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1080 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1081 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1082 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1083 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1087 * This entry point is called internally by Bacula to ensure
1088 * that the plugin IO calls come into this code.
1090 void load_fd_plugins(const char *plugin_dir)
1096 Dmsg0(dbglvl, "plugin dir is NULL\n");
1100 b_plugin_list = New(alist(10, not_owned_by_alist));
1102 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1103 is_plugin_compatible)) {
1104 /* Either none found, or some error */
1105 if (b_plugin_list->size() == 0) {
1106 delete b_plugin_list;
1107 b_plugin_list = NULL;
1108 Dmsg0(dbglvl, "No plugins loaded\n");
1113 /* Plug entry points called from findlib */
1114 plugin_bopen = my_plugin_bopen;
1115 plugin_bclose = my_plugin_bclose;
1116 plugin_bread = my_plugin_bread;
1117 plugin_bwrite = my_plugin_bwrite;
1118 plugin_blseek = my_plugin_blseek;
1122 * Verify that the plugin is acceptable, and print information
1125 foreach_alist_index(i, plugin, b_plugin_list) {
1126 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1127 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1130 dbg_plugin_add_hook(dump_fd_plugin);
1135 * Check if a plugin is compatible. Called by the load_plugin function
1136 * to allow us to verify the plugin.
1138 static bool is_plugin_compatible(Plugin *plugin)
1140 pInfo *info = (pInfo *)plugin->pinfo;
1141 Dmsg0(dbglvl, "is_plugin_compatible called\n");
1143 if (chk_dbglvl(50)) {
1144 dump_fd_plugin(plugin, stdin);
1146 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1147 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1148 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1149 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1150 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1154 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1155 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1156 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1157 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1158 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1161 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1162 strcmp(info->plugin_license, "AGPLv3") != 0 &&
1163 strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
1164 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1165 plugin->file, info->plugin_license);
1166 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1167 plugin->file, info->plugin_license);
1170 if (info->size != sizeof(pInfo)) {
1171 Jmsg(NULL, M_ERROR, 0,
1172 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1173 plugin->file, sizeof(pInfo), info->size);
1183 * Create a new instance of each plugin for this Job
1184 * Note, b_plugin_list can exist but jcr->plugin_ctx_list can
1185 * be NULL if no plugins were loaded.
1187 void new_plugins(JCR *jcr)
1193 if (!b_plugin_list) {
1194 Dmsg0(dbglvl, "plugin list is NULL\n");
1197 if (jcr->is_job_canceled()) {
1201 int num = b_plugin_list->size();
1204 Dmsg0(dbglvl, "No plugins loaded\n");
1208 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1210 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1211 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1212 foreach_alist_index(i, plugin, b_plugin_list) {
1214 /* Start a new instance of each plugin */
1215 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1216 memset(b_ctx, 0, sizeof(bacula_ctx));
1218 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1219 plugin_ctx_list[i].pContext = NULL;
1220 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1221 Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
1222 b_ctx->disabled = true;
1226 Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1233 * Free the plugin instances for this Job
1235 void free_plugins(JCR *jcr)
1240 if (!b_plugin_list || !jcr->plugin_ctx_list) {
1241 return; /* no plugins, nothing to do */
1245 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1246 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1247 foreach_alist_index(i, plugin, b_plugin_list) {
1248 /* Free the plugin instance */
1249 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1250 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1254 free(plugin_ctx_list);
1255 jcr->plugin_ctx_list = NULL;
1258 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
1260 JCR *jcr = bfd->jcr;
1261 Plugin *plugin = (Plugin *)jcr->plugin;
1264 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1266 if (!plugin || !jcr->plugin_ctx) {
1269 io.pkt_size = sizeof(io);
1270 io.pkt_end = sizeof(io);
1280 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1281 bfd->berrno = io.io_errno;
1283 errno = b_errno_win32;
1285 errno = io.io_errno;
1286 bfd->lerror = io.lerror;
1288 Dmsg1(dbglvl, "Return from plugin open status=%d\n", io.status);
1293 static int my_plugin_bclose(BFILE *bfd)
1295 JCR *jcr = bfd->jcr;
1296 Plugin *plugin = (Plugin *)jcr->plugin;
1300 Dmsg0(dbglvl, "===== plugin_bclose\n");
1301 if (!plugin || !jcr->plugin_ctx) {
1304 io.pkt_size = sizeof(io);
1305 io.pkt_end = sizeof(io);
1312 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1313 bfd->berrno = io.io_errno;
1315 errno = b_errno_win32;
1317 errno = io.io_errno;
1318 bfd->lerror = io.lerror;
1320 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1325 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1327 JCR *jcr = bfd->jcr;
1328 Plugin *plugin = (Plugin *)jcr->plugin;
1332 Dmsg0(dbglvl, "plugin_bread\n");
1333 if (!plugin || !jcr->plugin_ctx) {
1336 io.pkt_size = sizeof(io);
1337 io.pkt_end = sizeof(io);
1340 io.buf = (char *)buf;
1345 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1346 bfd->offset = io.offset;
1347 bfd->berrno = io.io_errno;
1349 errno = b_errno_win32;
1351 errno = io.io_errno;
1352 bfd->lerror = io.lerror;
1355 return (ssize_t)io.status;
1358 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1360 JCR *jcr = bfd->jcr;
1361 Plugin *plugin = (Plugin *)jcr->plugin;
1365 Dmsg0(dbglvl, "plugin_bwrite\n");
1366 if (!plugin || !jcr->plugin_ctx) {
1367 Dmsg0(0, "No plugin context\n");
1370 io.pkt_size = sizeof(io);
1371 io.pkt_end = sizeof(io);
1374 io.buf = (char *)buf;
1378 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1379 bfd->berrno = io.io_errno;
1381 errno = b_errno_win32;
1383 errno = io.io_errno;
1384 bfd->lerror = io.lerror;
1387 return (ssize_t)io.status;
1390 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1392 JCR *jcr = bfd->jcr;
1393 Plugin *plugin = (Plugin *)jcr->plugin;
1397 Dmsg0(dbglvl, "plugin_bseek\n");
1398 if (!plugin || !jcr->plugin_ctx) {
1401 io.pkt_size = sizeof(io);
1402 io.pkt_end = sizeof(io);
1408 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1409 bfd->berrno = io.io_errno;
1411 errno = b_errno_win32;
1413 errno = io.io_errno;
1414 bfd->lerror = io.lerror;
1417 return (boffset_t)io.offset;
1420 /* ==============================================================
1422 * Callbacks from the plugin
1424 * ==============================================================
1426 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1434 switch (var) { /* General variables, no need of ctx */
1436 *((char **)value) = my_name;
1438 case bVarWorkingDir:
1439 *(void **)value = me->working_directory;
1442 *(char **)value = exepath;
1445 *(char **)value = version;
1448 *(char **)value = dist_name;
1450 case bVarPrevJobName:
1452 case bVarPrefixLinks:
1460 if (!ctx) { /* Other variables need context */
1464 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1471 *((int *)value) = jcr->JobId;
1472 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1475 *((int *)value) = jcr->getJobLevel();
1476 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1479 *((int *)value) = jcr->getJobType();
1480 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1483 *((char **)value) = jcr->client_name;
1484 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1487 *((char **)value) = jcr->Job;
1488 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1490 case bVarPrevJobName:
1491 *((char **)value) = jcr->PrevJob;
1492 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1495 *((int *)value) = jcr->JobStatus;
1496 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1499 *((int *)value) = (int)jcr->mtime;
1500 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1503 *((int *)value) = (int)jcr->accurate;
1504 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1506 case bVarInteractiveSession:
1507 *(int *)value = (int)jcr->interactive_session;
1510 break; /* a write only variable, ignore read request */
1513 case bVarVssDllHandle:
1516 *(char **)value = jcr->where;
1518 case bVarRegexWhere:
1519 *(char **)value = jcr->RegexWhere;
1521 case bVarPrefixLinks:
1522 *(int *)value = (int)jcr->prefix_links;
1524 case bVarFDName: /* get warning with g++ if we missed one */
1525 case bVarWorkingDir:
1536 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1540 if (!value || !ctx) {
1543 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1544 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1548 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1551 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1555 case bVarInteractiveSession:
1556 jcr->interactive_session = (((intptr_t) value) == 1);
1565 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1575 va_start(args, ctx);
1576 while ((event = va_arg(args, uint32_t))) {
1577 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1584 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1585 int type, utime_t mtime, const char *fmt, ...)
1593 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1598 va_start(arg_ptr, fmt);
1599 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1601 Jmsg(jcr, type, mtime, "%s", buf);
1606 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1607 int level, const char *fmt, ...)
1613 va_start(arg_ptr, fmt);
1614 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1616 d_msg(file, line, level, "%s", buf);
1621 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1625 return sm_malloc(file, line, size);
1627 return malloc(size);
1631 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1634 sm_free(file, line, mem);
1640 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1646 bctx = (bacula_ctx *)ctx->bContext;
1658 * Let the plugin define files/directories to be excluded
1659 * from the main backup.
1661 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1667 if (!is_ctx_good(ctx, jcr, bctx)) {
1674 /* Save the include context */
1675 old = get_incexe(jcr);
1677 /* Not right time to add exlude */
1682 if (!bctx->exclude) {
1683 bctx->exclude = new_exclude(jcr);
1686 /* Set the Exclude context */
1687 set_incexe(jcr, bctx->exclude);
1689 add_file_to_fileset(jcr, file, true);
1691 /* Restore the current context */
1692 set_incexe(jcr, old);
1694 Dmsg1(100, "Add exclude file=%s\n", file);
1700 * Let the plugin define files/directories to be excluded
1701 * from the main backup.
1703 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1710 if (!is_ctx_good(ctx, jcr, bctx)) {
1717 /* Save the include context */
1718 old = get_incexe(jcr);
1720 /* Not right time to add include */
1724 if (!bctx->include) {
1725 bctx->include = old;
1728 set_incexe(jcr, bctx->include);
1729 add_file_to_fileset(jcr, file, true);
1731 /* Restore the current context */
1732 set_incexe(jcr, old);
1734 Dmsg1(100, "Add include file=%s\n", file);
1739 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1744 if (!is_ctx_good(ctx, jcr, bctx)) {
1750 add_options_to_fileset(jcr, opts);
1752 Dmsg1(1000, "Add options=%s\n", opts);
1756 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1761 if (!is_ctx_good(ctx, jcr, bctx)) {
1767 add_regex_to_fileset(jcr, item, type);
1768 Dmsg1(100, "Add regex=%s\n", item);
1773 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1778 if (!is_ctx_good(ctx, jcr, bctx)) {
1784 add_wild_to_fileset(jcr, item, type);
1785 Dmsg1(100, "Add wild=%s\n", item);
1790 static bRC baculaNewOptions(bpContext *ctx)
1795 if (!is_ctx_good(ctx, jcr, bctx)) {
1798 (void)new_options(jcr, NULL);
1803 static bRC baculaNewInclude(bpContext *ctx)
1808 if (!is_ctx_good(ctx, jcr, bctx)) {
1811 (void)new_include(jcr);
1816 static bRC baculaNewPreInclude(bpContext *ctx)
1821 if (!is_ctx_good(ctx, jcr, bctx)) {
1825 bctx->include = new_preinclude(jcr);
1826 new_options(jcr, bctx->include);
1827 set_incexe(jcr, bctx->include);
1834 * Check if a file have to be backuped using Accurate code
1836 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1841 bRC ret = bRC_Error;
1844 if (!is_ctx_good(ctx, jcr, bctx)) {
1853 * Copy fname and link because save_file() zaps them. This
1854 * avoids zaping the plugin's strings.
1856 ff_pkt->type = sp->type;
1858 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1862 ff_pkt->fname = sp->fname;
1863 ff_pkt->link = sp->link;
1864 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1866 if (check_changes(jcr, ff_pkt)) {
1872 /* check_changes() can update delta sequence number, return it to the
1875 sp->delta_seq = ff_pkt->delta_seq;
1876 sp->accurate_found = ff_pkt->accurate_found;
1880 Dmsg1(100, "checkChanges=%i\n", ret);
1885 * Check if a file would be saved using current Include/Exclude code
1887 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
1894 struct stat oldstat;
1895 bRC ret = bRC_Error;
1898 if (!is_ctx_good(ctx, jcr, bctx)) {
1907 /* Probably not needed, but keep a copy */
1908 old = ff_pkt->fname;
1909 oldstat = ff_pkt->statp;
1911 ff_pkt->fname = sp->fname;
1912 ff_pkt->statp = sp->statp;
1914 if (accept_file(ff_pkt)) {
1920 ff_pkt->fname = old;
1921 ff_pkt->statp = oldstat;
1929 int (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
1930 int (*plugin_bclose)(JCR *jcr) = NULL;
1931 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1932 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1933 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1935 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1940 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1945 int main(int argc, char *argv[])
1947 char plugin_dir[1000];
1952 strcpy(my_name, "test-fd");
1954 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1955 load_fd_plugins(plugin_dir);
1963 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1964 generate_plugin_event(jcr1, bEventJobEnd);
1965 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1967 generate_plugin_event(jcr2, bEventJobEnd);
1972 Dmsg0(dbglvl, "bacula: OK ...\n");
1973 close_memory_pool();
1974 sm_dump(false); /* unit test */
1978 #endif /* TEST_PROGRAM */