2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2012 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation, which is
11 listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main program to test loading and running Bacula plugins.
30 * Destined to become Bacula pluginloader, ...
32 * Kern Sibbald, October 2007
38 extern DLL_IMP_EXP char *exepath;
39 extern DLL_IMP_EXP char *version;
40 extern DLL_IMP_EXP char *dist_name;
41 extern DLL_IMP_EXP int beef;
43 const int dbglvl = 150;
45 const char *plugin_type = "-fd.dll";
47 const char *plugin_type = "-fd.so";
50 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
51 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
53 /* Function pointers to be set here */
54 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
55 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
56 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
57 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
58 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
61 /* Forward referenced functions */
62 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
63 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
64 static bRC baculaRegisterEvents(bpContext *ctx, ...);
65 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
66 int type, utime_t mtime, const char *fmt, ...);
67 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
68 int level, const char *fmt, ...);
69 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
71 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
72 static bRC baculaAddExclude(bpContext *ctx, const char *file);
73 static bRC baculaAddInclude(bpContext *ctx, const char *file);
74 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
75 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
76 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
77 static bRC baculaNewOptions(bpContext *ctx);
78 static bRC baculaNewInclude(bpContext *ctx);
79 static bRC baculaNewPreInclude(bpContext *ctx);
80 static bool is_plugin_compatible(Plugin *plugin);
81 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
82 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
83 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
86 * These will be plugged into the global pointer structure for
89 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
90 static int my_plugin_bclose(BFILE *bfd);
91 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
92 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
93 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
97 static bInfo binfo = {
99 FD_PLUGIN_INTERFACE_VERSION
102 /* Bacula entry points */
103 static bFuncs bfuncs = {
105 FD_PLUGIN_INTERFACE_VERSION,
106 baculaRegisterEvents,
126 * Bacula private context
129 JCR *jcr; /* jcr for plugin */
130 bRC rc; /* last return code */
131 bool disabled; /* set if plugin disabled */
132 findINCEXE *exclude; /* pointer to exclude files */
133 findINCEXE *include; /* pointer to include/exclude files */
137 * Test if event is for this plugin
139 static bool for_this_plugin(Plugin *plugin, char *name, int len)
141 Dmsg4(dbglvl, "name=%s len=%d plugin=%s plen=%d\n", name, len, plugin->file, plugin->file_len);
142 if (!name) { /* if no plugin name, all plugins get it */
145 /* Return global VSS job metadata to all plugins */
146 if (strcmp("job", name) == 0) { /* old V4.0 name for VSS job metadata */
149 if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
152 /* Check if this is the correct plugin */
153 if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
160 bool is_plugin_disabled(bpContext *plugin_ctx)
167 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
171 return b_ctx->disabled;
174 bool is_plugin_disabled(JCR *jcr)
176 return is_plugin_disabled(jcr->plugin_ctx);
180 * Create a plugin event When receiving bEventCancelCommand, this function is
181 * called by an other thread.
183 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
185 bpContext *plugin_ctx;
191 bool call_if_canceled = false;
192 restore_object_pkt *rop;
195 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
196 return; /* Return if no plugins loaded */
200 * Some events are sent to only a particular plugin or must be
201 * called even if the job is canceled
204 case bEventPluginCommand:
205 case bEventOptionPlugin:
206 name = (char *)value;
207 if (!get_plugin_name(jcr, name, &len)) {
211 case bEventRestoreObject:
212 /* After all RestoreObject, we have it one more time with value=NULL */
214 /* Some RestoreObjects may not have a plugin name */
215 rop = (restore_object_pkt *)value;
216 if (*rop->plugin_name) {
217 name = rop->plugin_name;
218 get_plugin_name(jcr, name, &len);
223 case bEventEndBackupJob:
224 case bEventEndVerifyJob:
225 call_if_canceled = true; /* plugin *must* see this call */
227 case bEventStartRestoreJob:
228 foreach_alist_index(i, plugin, bplugin_list) {
229 plugin->restoreFileStarted = false;
230 plugin->createFileCalled = false;
233 case bEventEndRestoreJob:
234 call_if_canceled = true; /* plugin *must* see this call */
240 /* If call_if_canceled is set, we call the plugin anyway */
241 if (!call_if_canceled && jcr->is_job_canceled()) {
245 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
246 event.eventType = eventType;
248 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
251 * Pass event to every plugin (except if name is set). If name
252 * is set, we pass it only to the plugin with that name.
254 foreach_alist_index(i, plugin, bplugin_list) {
255 if (!for_this_plugin(plugin, name, len)) {
256 Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
257 name, name==NULL?1:0);
261 * Note, at this point do not change
262 * jcr->plugin or jcr->plugin_ctx
265 plugin_ctx = &plugin_ctx_list[i];
266 if (is_plugin_disabled(plugin_ctx)) {
269 if (eventType == bEventEndRestoreJob) {
270 Dmsg0(50, "eventType==bEventEndRestoreJob\n");
271 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
272 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
275 jcr->plugin->restoreFileStarted = false;
276 jcr->plugin->createFileCalled = false;
279 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
285 * Check if file was seen for accurate
287 bool plugin_check_file(JCR *jcr, char *fname)
294 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
295 return false; /* Return if no plugins loaded */
298 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
300 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
302 /* Pass event to every plugin */
303 foreach_alist_index(i, plugin, bplugin_list) {
304 jcr->plugin_ctx = &plugin_ctx_list[i];
305 jcr->plugin = plugin;
306 if (is_plugin_disabled(jcr)) {
309 if (plug_func(plugin)->checkFile == NULL) {
312 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
313 if (rc == bRC_Seen) {
320 jcr->plugin_ctx = NULL;
321 return rc == bRC_Seen;
324 /* Get the first part of the the plugin command
325 * systemstate:/@SYSTEMSTATE/
327 * => can use for_this_plugin(plug, cmd, ret);
329 * The plugin command can contain only the plugin name
333 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
338 if (!cmd || (*cmd == '\0')) {
341 /* Handle plugin command here backup */
342 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
343 if ((p = strchr(cmd, ':')) == NULL) {
344 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
347 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
350 } else { /* plugin:argument */
362 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
365 ff_pkt->no_read = sp->no_read;
366 ff_pkt->delta_seq = sp->delta_seq;
367 if (sp->flags & FO_DELTA) {
368 ff_pkt->flags |= FO_DELTA;
369 ff_pkt->delta_seq++; /* make new delta sequence number */
371 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
372 ff_pkt->delta_seq = 0;
375 if (sp->flags & FO_OFFSETS) {
376 ff_pkt->flags |= FO_OFFSETS;
378 ff_pkt->flags &= ~FO_OFFSETS;
380 /* Sparse code doesn't work with plugins
381 * that use FIFO or STDOUT/IN to communicate
383 if (sp->flags & FO_SPARSE) {
384 ff_pkt->flags |= FO_SPARSE;
386 ff_pkt->flags &= ~FO_SPARSE;
388 if (sp->flags & FO_PORTABLE_DATA) {
389 ff_pkt->flags |= FO_PORTABLE_DATA;
391 ff_pkt->flags &= ~FO_PORTABLE_DATA;
393 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
397 /* Ask to a Option Plugin what to do with the current file */
398 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
402 char *cmd = ff_pkt->plugin;
406 event.eventType = bEventHandleBackupFile;
409 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
410 memset(sp, 0, sizeof(struct save_pkt));
411 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
414 sp->link = ff_pkt->link;
415 sp->cmd = ff_pkt->plugin;
416 sp->statp = ff_pkt->statp;
417 sp->fname = ff_pkt->fname;
418 sp->delta_seq = ff_pkt->delta_seq;
419 sp->accurate_found = ff_pkt->accurate_found;
421 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
422 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
423 goto bail_out; /* Return if no plugins loaded */
426 if (!get_plugin_name(jcr, cmd, &len)) {
430 /* Note, we stop the loop on the first plugin that matches the name */
431 foreach_alist_index(i, plugin, bplugin_list) {
432 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
433 if (!for_this_plugin(plugin, cmd, len)) {
438 if (is_plugin_disabled(&plugin_ctx_list[i])) {
442 jcr->plugin_ctx = &plugin_ctx_list[i];
443 jcr->plugin = plugin;
445 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
448 /* TODO: would be better to set this in save_file() */
450 jcr->opt_plugin = true;
451 jcr->plugin = plugin;
452 jcr->plugin_sp = sp; /* Unset sp in save_file */
453 jcr->plugin_ctx = &plugin_ctx_list[i];
455 update_ff_pkt(ff_pkt, sp);
457 /* reset plugin in JCR if not used this time */
459 jcr->plugin_ctx = NULL;
464 } /* end foreach loop */
471 * Sequence of calls for a backup:
472 * 1. plugin_save() here is called with ff_pkt
473 * 2. we find the plugin requested on the command string
474 * 3. we generate a bEventBackupCommand event to the specified plugin
475 * and pass it the command string.
476 * 4. we make a startPluginBackup call to the plugin, which gives
477 * us the data we need in save_pkt
478 * 5. we call Bacula's save_file() subroutine to save the specified
479 * file. The plugin will be called at pluginIO() to supply the
482 * Sequence of calls for restore:
483 * See subroutine plugin_name_stream() below.
485 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
490 char *cmd = ff_pkt->top_fname;
493 POOL_MEM fname(PM_FNAME);
494 POOL_MEM link(PM_FNAME);
497 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
498 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
499 return 1; /* Return if no plugins loaded */
502 jcr->cmd_plugin = true;
503 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
504 event.eventType = bEventBackupCommand;
506 if (!get_plugin_name(jcr, cmd, &len)) {
510 /* Note, we stop the loop on the first plugin that matches the name */
511 foreach_alist_index(i, plugin, bplugin_list) {
512 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
513 if (!for_this_plugin(plugin, cmd, len)) {
517 * We put the current plugin pointer, and the plugin context
518 * into the jcr, because during save_file(), the plugin
519 * will be called many times and these values are needed.
522 jcr->plugin_ctx = &plugin_ctx_list[i];
523 jcr->plugin = plugin;
524 if (is_plugin_disabled(jcr)) {
528 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
529 /* Send the backup command to the right plugin*/
530 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
533 /* Loop getting filenames to backup then saving them */
534 while (!jcr->is_job_canceled()) {
535 memset(&sp, 0, sizeof(sp));
536 sp.pkt_size = sizeof(sp);
537 sp.pkt_end = sizeof(sp);
542 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
545 /* Get the file save parameters. I.e. the stat pkt ... */
546 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
550 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
554 jcr->plugin_sp = &sp;
557 * Copy fname and link because save_file() zaps them. This
558 * avoids zaping the plugin's strings.
560 ff_pkt->type = sp.type;
561 if (IS_FT_OBJECT(sp.type)) {
562 if (!sp.object_name) {
563 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
567 ff_pkt->fname = cmd; /* full plugin string */
568 ff_pkt->object_name = sp.object_name;
569 ff_pkt->object_index = sp.index; /* restore object index */
570 ff_pkt->object_compression = 0; /* no compression for now */
571 ff_pkt->object = sp.object;
572 ff_pkt->object_len = sp.object_len;
576 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
580 pm_strcpy(fname, sp.fname);
581 pm_strcpy(link, sp.link);
584 ff_pkt->fname = fname.c_str();
585 ff_pkt->link = link.c_str();
586 update_ff_pkt(ff_pkt, &sp);
589 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
590 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
592 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
594 /* Call Bacula core code to backup the plugin's file */
595 save_file(jcr, ff_pkt, true);
596 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
597 if (rc == bRC_More || rc == bRC_OK) {
598 accurate_mark_file_as_seen(jcr, fname.c_str());
601 if (rc == bRC_More) {
605 } /* end while loop */
607 } /* end loop over all plugins */
608 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
612 jcr->cmd_plugin = false;
614 jcr->plugin_ctx = NULL;
620 * Sequence of calls for a estimate:
621 * 1. plugin_estimate() here is called with ff_pkt
622 * 2. we find the plugin requested on the command string
623 * 3. we generate a bEventEstimateCommand event to the specified plugin
624 * and pass it the command string.
625 * 4. we make a startPluginBackup call to the plugin, which gives
626 * us the data we need in save_pkt
629 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
634 char *cmd = ff_pkt->top_fname;
637 POOL_MEM fname(PM_FNAME);
638 POOL_MEM link(PM_FNAME);
642 if (!bplugin_list || !jcr->plugin_ctx_list) {
643 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
644 return 1; /* Return if no plugins loaded */
647 jcr->cmd_plugin = true;
648 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
649 event.eventType = bEventEstimateCommand;
651 if (!get_plugin_name(jcr, cmd, &len)) {
655 /* Note, we stop the loop on the first plugin that matches the name */
656 foreach_alist_index(i, plugin, bplugin_list) {
657 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
658 if (!for_this_plugin(plugin, cmd, len)) {
662 * We put the current plugin pointer, and the plugin context
663 * into the jcr, because during save_file(), the plugin
664 * will be called many times and these values are needed.
667 jcr->plugin_ctx = &plugin_ctx_list[i];
668 jcr->plugin = plugin;
669 if (is_plugin_disabled(jcr)) {
673 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
674 /* Send the backup command to the right plugin*/
675 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
678 /* Loop getting filenames to backup then saving them */
679 while (!jcr->is_job_canceled()) {
681 memset(&sp, 0, sizeof(sp));
682 sp.pkt_size = sizeof(sp);
683 sp.pkt_end = sizeof(sp);
687 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
689 /* Get the file save parameters. I.e. the stat pkt ... */
690 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
694 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
699 if (!IS_FT_OBJECT(sp.type)) {
701 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
706 /* Count only files backed up */
716 jcr->JobFiles++; /* increment number of files backed up */
721 jcr->num_files_examined++;
723 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
724 if (sp.statp.st_size > 0) {
725 jcr->JobBytes += sp.statp.st_size;
730 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
732 attr.ofname = (POOLMEM *)sp.fname;
733 attr.olname = (POOLMEM *)sp.link;
734 print_ls_output(jcr, &attr);
738 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
740 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
742 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
743 if (rc == bRC_More || rc == bRC_OK) {
744 accurate_mark_file_as_seen(jcr, sp.fname);
747 if (rc == bRC_More) {
751 } /* end while loop */
753 } /* end loop over all plugins */
754 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
758 jcr->cmd_plugin = false;
760 jcr->plugin_ctx = NULL;
765 * Send plugin name start/end record to SD
767 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
770 int index = jcr->JobFiles;
771 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
775 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
778 if (jcr->is_job_canceled()) {
783 index++; /* JobFiles not incremented yet */
785 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
786 /* Send stream header */
788 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
789 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
793 Dmsg1(dbglvl, "send plugin name hdr: %s\n", sd->msg);
797 /* Send data -- not much */
798 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
800 /* Send end of data */
801 stat = sd->fsend("%ld 0", jcr->JobFiles);
805 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
809 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
810 sd->signal(BNET_EOD); /* indicate end of plugin name data */
816 * Plugin name stream found during restore. The record passed in
817 * argument name was generated in send_plugin_name() above.
819 * Returns: true if start of stream
820 * false if end of steam
822 bool plugin_name_stream(JCR *jcr, char *name)
830 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
833 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
834 skip_nonspaces(&p); /* skip over jcr->JobFiles */
838 /* Start of plugin data */
839 skip_nonspaces(&p); /* skip start/end flag */
841 // portable = *p == '1';
842 skip_nonspaces(&p); /* skip portable flag */
847 * End of plugin data, notify plugin, then clear flags
849 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
850 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
851 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
854 jcr->plugin->restoreFileStarted = false;
855 jcr->plugin->createFileCalled = false;
857 jcr->plugin_ctx = NULL;
862 if (!plugin_ctx_list) {
867 * After this point, we are dealing with a restore start
869 if (!get_plugin_name(jcr, cmd, &len)) {
874 * Search for correct plugin as specified on the command
877 foreach_alist_index(i, plugin, bplugin_list) {
879 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
880 if (!for_this_plugin(plugin, cmd, len)) {
884 jcr->plugin_ctx = &plugin_ctx_list[i];
885 jcr->plugin = plugin;
886 if (is_plugin_disabled(jcr)) {
887 Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
890 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
891 event.eventType = bEventRestoreCommand;
892 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
893 &event, cmd) != bRC_OK) {
894 Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
897 if (plugin->restoreFileStarted) {
898 Jmsg2(jcr, M_FATAL, 0, "Second call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
899 plugin->restoreFileStarted = false;
902 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
903 plugin->restoreFileStarted = true;
906 Dmsg1(dbglvl, "startRestoreFile failed. plugin=%s\n", cmd);
910 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
919 jcr->plugin_ctx = NULL;
924 * Tell the plugin to create the file. Return values are
925 * This is called only during Restore
928 * CF_SKIP -- skip processing this file
929 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
930 * CF_CREATED -- created, but no content to extract (typically directories)
933 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
935 bpContext *plugin_ctx = jcr->plugin_ctx;
936 Plugin *plugin = jcr->plugin;
937 struct restore_pkt rp;
942 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
946 rp.pkt_size = sizeof(rp);
947 rp.pkt_end = sizeof(rp);
948 rp.delta_seq = attr->delta_seq;
949 rp.stream = attr->stream;
950 rp.data_stream = attr->data_stream;
951 rp.type = attr->type;
952 rp.file_index = attr->file_index;
953 rp.LinkFI = attr->LinkFI;
955 rp.statp = attr->statp; /* structure assignment */
956 rp.attrEx = attr->attrEx;
957 rp.ofname = attr->ofname;
958 rp.olname = attr->olname;
959 rp.where = jcr->where;
960 rp.RegexWhere = jcr->RegexWhere;
961 rp.replace = jcr->replace;
962 rp.create_status = CF_ERROR;
963 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
964 rp.stream, rp.type, rp.LinkFI, rp.ofname);
966 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
969 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
970 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to createFile=%d %d\n",
971 plugin->createFileCalled, plugin->restoreFileStarted);
972 plugin->createFileCalled = false;
975 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
977 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
981 if (rp.create_status == CF_ERROR) {
982 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
987 if (rp.create_status == CF_SKIP) {
991 if (rp.create_status == CF_CORE) {
992 return CF_CORE; /* Let Bacula core handle the file creation */
995 /* Created link or directory? */
996 if (rp.create_status == CF_CREATED) {
997 return rp.create_status; /* yes, no need to bopen */
1002 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
1003 Dmsg0(dbglvl, "call bopen\n");
1004 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
1005 Dmsg1(dbglvl, "bopen status=%d\n", stat);
1008 be.set_errno(bfd->berrno);
1009 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
1010 attr->ofname, be.bstrerror());
1011 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
1015 if (!is_bopen(bfd)) {
1016 Dmsg0(000, "===== BFD is not open!!!!\n");
1023 * Reset the file attributes after all file I/O is done -- this allows
1024 * the previous access time/dates to be set properly, and it also allows
1025 * us to properly set directory permissions.
1026 * Not currently Implemented.
1028 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
1030 Plugin *plugin = (Plugin *)jcr->plugin;
1031 struct restore_pkt rp;
1033 Dmsg0(dbglvl, "plugin_set_attributes\n");
1035 if (!plugin || !jcr->plugin_ctx) {
1039 memset(&rp, 0, sizeof(rp));
1040 rp.pkt_size = sizeof(rp);
1041 rp.pkt_end = sizeof(rp);
1042 rp.stream = attr->stream;
1043 rp.data_stream = attr->data_stream;
1044 rp.type = attr->type;
1045 rp.file_index = attr->file_index;
1046 rp.LinkFI = attr->LinkFI;
1048 rp.statp = attr->statp; /* structure assignment */
1049 rp.attrEx = attr->attrEx;
1050 rp.ofname = attr->ofname;
1051 rp.olname = attr->olname;
1052 rp.where = jcr->where;
1053 rp.RegexWhere = jcr->RegexWhere;
1054 rp.replace = jcr->replace;
1055 rp.create_status = CF_ERROR;
1057 plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
1059 if (rp.create_status == CF_CORE) {
1060 set_attributes(jcr, attr, ofd);
1062 if (is_bopen(ofd)) {
1065 pm_strcpy(attr->ofname, "*none*");
1073 * Print to file the plugin info.
1075 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1080 pInfo *info = (pInfo *)plugin->pinfo;
1081 fprintf(fp, "\tversion=%d\n", info->version);
1082 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1083 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1084 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1085 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1086 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1087 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1091 * This entry point is called internally by Bacula to ensure
1092 * that the plugin IO calls come into this code.
1094 void load_fd_plugins(const char *plugin_dir)
1100 Dmsg0(dbglvl, "plugin dir is NULL\n");
1104 bplugin_list = New(alist(10, not_owned_by_alist));
1106 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1107 is_plugin_compatible)) {
1108 /* Either none found, or some error */
1109 if (bplugin_list->size() == 0) {
1110 delete bplugin_list;
1111 bplugin_list = NULL;
1112 Dmsg0(dbglvl, "No plugins loaded\n");
1117 /* Plug entry points called from findlib */
1118 plugin_bopen = my_plugin_bopen;
1119 plugin_bclose = my_plugin_bclose;
1120 plugin_bread = my_plugin_bread;
1121 plugin_bwrite = my_plugin_bwrite;
1122 plugin_blseek = my_plugin_blseek;
1126 * Verify that the plugin is acceptable, and print information
1129 foreach_alist_index(i, plugin, bplugin_list) {
1130 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1131 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1134 dbg_plugin_add_hook(dump_fd_plugin);
1139 * Check if a plugin is compatible. Called by the load_plugin function
1140 * to allow us to verify the plugin.
1142 static bool is_plugin_compatible(Plugin *plugin)
1144 pInfo *info = (pInfo *)plugin->pinfo;
1145 Dmsg0(dbglvl, "is_plugin_compatible called\n");
1147 if (debug_level >= 50) {
1148 dump_fd_plugin(plugin, stdin);
1150 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1151 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1152 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1153 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1154 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1158 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1159 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1160 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1161 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1162 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1165 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1166 strcmp(info->plugin_license, "AGPLv3") != 0) {
1167 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1168 plugin->file, info->plugin_license);
1169 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1170 plugin->file, info->plugin_license);
1173 if (info->size != sizeof(pInfo)) {
1174 Jmsg(NULL, M_ERROR, 0,
1175 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1176 plugin->file, sizeof(pInfo), info->size);
1186 * Create a new instance of each plugin for this Job
1187 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1188 * be NULL if no plugins were loaded.
1190 void new_plugins(JCR *jcr)
1196 if (!bplugin_list) {
1197 Dmsg0(dbglvl, "plugin list is NULL\n");
1200 if (jcr->is_job_canceled()) {
1204 int num = bplugin_list->size();
1207 Dmsg0(dbglvl, "No plugins loaded\n");
1211 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1213 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1214 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1215 foreach_alist_index(i, plugin, bplugin_list) {
1217 /* Start a new instance of each plugin */
1218 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1219 memset(b_ctx, 0, sizeof(bacula_ctx));
1221 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1222 plugin_ctx_list[i].pContext = NULL;
1223 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1224 b_ctx->disabled = true;
1228 Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1235 * Free the plugin instances for this Job
1237 void free_plugins(JCR *jcr)
1242 if (!bplugin_list || !jcr->plugin_ctx_list) {
1243 return; /* no plugins, nothing to do */
1247 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1248 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1249 foreach_alist_index(i, plugin, bplugin_list) {
1250 /* Free the plugin instance */
1251 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1252 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1256 free(plugin_ctx_list);
1257 jcr->plugin_ctx_list = NULL;
1260 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1262 JCR *jcr = bfd->jcr;
1263 Plugin *plugin = (Plugin *)jcr->plugin;
1266 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1268 if (!plugin || !jcr->plugin_ctx) {
1271 io.pkt_size = sizeof(io);
1272 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);
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;
1344 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1345 bfd->offset = io.offset;
1346 bfd->berrno = io.io_errno;
1348 errno = b_errno_win32;
1350 errno = io.io_errno;
1351 bfd->lerror = io.lerror;
1354 return (ssize_t)io.status;
1357 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1359 JCR *jcr = bfd->jcr;
1360 Plugin *plugin = (Plugin *)jcr->plugin;
1364 Dmsg0(dbglvl, "plugin_bwrite\n");
1365 if (!plugin || !jcr->plugin_ctx) {
1366 Dmsg0(0, "No plugin context\n");
1369 io.pkt_size = sizeof(io);
1370 io.pkt_end = sizeof(io);
1373 io.buf = (char *)buf;
1376 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1377 bfd->berrno = io.io_errno;
1379 errno = b_errno_win32;
1381 errno = io.io_errno;
1382 bfd->lerror = io.lerror;
1385 return (ssize_t)io.status;
1388 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1390 JCR *jcr = bfd->jcr;
1391 Plugin *plugin = (Plugin *)jcr->plugin;
1395 Dmsg0(dbglvl, "plugin_bseek\n");
1396 if (!plugin || !jcr->plugin_ctx) {
1399 io.pkt_size = sizeof(io);
1400 io.pkt_end = sizeof(io);
1406 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1407 bfd->berrno = io.io_errno;
1409 errno = b_errno_win32;
1411 errno = io.io_errno;
1412 bfd->lerror = io.lerror;
1415 return (boffset_t)io.offset;
1418 /* ==============================================================
1420 * Callbacks from the plugin
1422 * ==============================================================
1424 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1432 switch (var) { /* General variables, no need of ctx */
1434 *((char **)value) = my_name;
1436 case bVarWorkingDir:
1437 *(void **)value = me->working_directory;
1440 *(char **)value = exepath;
1443 *(char **)value = version;
1446 *(char **)value = dist_name;
1449 *((int *)value) = beef;
1455 if (!ctx) { /* Other variables need context */
1459 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1466 *((int *)value) = jcr->JobId;
1467 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1470 *((int *)value) = jcr->getJobLevel();
1471 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1474 *((int *)value) = jcr->getJobType();
1475 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1478 *((char **)value) = jcr->client_name;
1479 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1482 *((char **)value) = jcr->Job;
1483 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1485 case bVarPrevJobName:
1486 *((char **)value) = jcr->PrevJob;
1487 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1490 *((int *)value) = jcr->JobStatus;
1491 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1494 *((int *)value) = (int)jcr->mtime;
1495 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1498 *((int *)value) = (int)jcr->accurate;
1499 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1502 break; /* a write only variable, ignore read request */
1506 *(void **)value = g_pVSSClient->GetVssObject();
1511 case bVarVssDllHandle:
1514 *(void **)value = g_pVSSClient->GetVssDllHandle();
1520 *(char **)value = jcr->where;
1522 case bVarRegexWhere:
1523 *(char **)value = jcr->RegexWhere;
1525 case bVarPrefixLinks:
1526 *(int *)value = (int)jcr->prefix_links;
1528 case bVarFDName: /* get warning with g++ if we missed one */
1529 case bVarWorkingDir:
1540 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1544 if (!value || !ctx) {
1547 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1548 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1552 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1555 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
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, int 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 */