2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2010 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;
40 const int dbglvl = 150;
42 const char *plugin_type = "-fd.dll";
44 const char *plugin_type = "-fd.so";
47 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
48 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
50 /* Function pointers to be set here */
51 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
52 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
53 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
54 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
55 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
58 /* Forward referenced functions */
59 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
60 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
61 static bRC baculaRegisterEvents(bpContext *ctx, ...);
62 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
63 int type, utime_t mtime, const char *fmt, ...);
64 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
65 int level, const char *fmt, ...);
66 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
68 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
69 static bRC baculaAddExclude(bpContext *ctx, const char *file);
70 static bRC baculaAddInclude(bpContext *ctx, const char *file);
71 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
72 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
73 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
74 static bRC baculaNewOptions(bpContext *ctx);
75 static bRC baculaNewInclude(bpContext *ctx);
76 static bool is_plugin_compatible(Plugin *plugin);
77 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
78 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
81 * These will be plugged into the global pointer structure for
84 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
85 static int my_plugin_bclose(BFILE *bfd);
86 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
87 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
88 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
92 static bInfo binfo = {
94 FD_PLUGIN_INTERFACE_VERSION
97 /* Bacula entry points */
98 static bFuncs bfuncs = {
100 FD_PLUGIN_INTERFACE_VERSION,
101 baculaRegisterEvents,
119 * Bacula private context
122 JCR *jcr; /* jcr for plugin */
123 bRC rc; /* last return code */
124 bool disabled; /* set if plugin disabled */
125 findINCEXE *exclude; /* pointer to exclude files */
126 findINCEXE *include; /* pointer to include/exclude files */
129 static bool is_plugin_disabled(bpContext *plugin_ctx)
135 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
136 return b_ctx->disabled;
139 static bool is_plugin_disabled(JCR *jcr)
141 return is_plugin_disabled(jcr->plugin_ctx);
145 * Create a plugin event
146 * When receiving bEventCancelCommand, this function is called by an other thread.
148 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
150 bpContext *plugin_ctx;
156 bool call_if_canceled = false;
159 if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
160 return; /* Return if no plugins loaded */
164 * Some events are sent to only a particular plugin or must be
165 * called even if the job is canceled
168 case bEventPluginCommand:
169 name = (char *)value;
170 if (!get_plugin_name(jcr, name, &len)) {
174 case bEventEndBackupJob:
175 case bEventEndVerifyJob:
176 call_if_canceled = true;
178 case bEventEndRestoreJob:
179 call_if_canceled = true;
180 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
181 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
182 jcr->plugin->restoreFileStarted = false;
189 if (!call_if_canceled && jcr->is_job_canceled()) {
193 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
194 event.eventType = eventType;
196 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
198 /* Pass event to every plugin (except if name is set) */
199 foreach_alist(plugin, plugin_list) {
200 if (name && strncmp(plugin->file, name, len) != 0) {
204 plugin_ctx = &plugin_ctx_list[i++];
205 if (is_plugin_disabled(plugin_ctx)) {
208 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
217 * Check if file was seen for accurate
219 bool plugin_check_file(JCR *jcr, char *fname)
225 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
226 return false; /* Return if no plugins loaded */
229 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
231 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
233 /* Pass event to every plugin */
234 foreach_alist(plugin, plugin_list) {
235 jcr->plugin_ctx = &plugin_ctx_list[i++];
236 jcr->plugin = plugin;
237 if (is_plugin_disabled(jcr)) {
240 if (plug_func(plugin)->checkFile == NULL) {
243 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
244 if (rc == bRC_Seen) {
250 jcr->plugin_ctx = NULL;
251 return rc == bRC_Seen;
254 /* Get the first part of the the plugin command
255 * systemstate:/@SYSTEMSTATE/
257 * => can use strncmp(plugin_name, cmd, ret);
259 * The plugin command can contain only the plugin name
263 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
267 if (!cmd || (*cmd == '\0')) {
270 /* Handle plugin command here backup */
271 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
272 if ((p = strchr(cmd, ':')) == NULL) {
273 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
276 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
279 } else { /* plugin:argument */
290 * Sequence of calls for a backup:
291 * 1. plugin_save() here is called with ff_pkt
292 * 2. we find the plugin requested on the command string
293 * 3. we generate a bEventBackupCommand event to the specified plugin
294 * and pass it the command string.
295 * 4. we make a startPluginBackup call to the plugin, which gives
296 * us the data we need in save_pkt
297 * 5. we call Bacula's save_file() subroutine to save the specified
298 * file. The plugin will be called at pluginIO() to supply the
301 * Sequence of calls for restore:
302 * See subroutine plugin_name_stream() below.
304 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
309 char *cmd = ff_pkt->top_fname;
312 POOL_MEM fname(PM_FNAME);
313 POOL_MEM link(PM_FNAME);
315 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
316 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
317 return 1; /* Return if no plugins loaded */
320 jcr->cmd_plugin = true;
321 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
322 event.eventType = bEventBackupCommand;
324 if (!get_plugin_name(jcr, cmd, &len)) {
328 /* Note, we stop the loop on the first plugin that matches the name */
329 foreach_alist(plugin, plugin_list) {
330 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
331 if (strncmp(plugin->file, cmd, len) != 0) {
336 * We put the current plugin pointer, and the plugin context
337 * into the jcr, because during save_file(), the plugin
338 * will be called many times and these values are needed.
340 jcr->plugin_ctx = &plugin_ctx_list[i];
341 jcr->plugin = plugin;
342 if (is_plugin_disabled(jcr)) {
346 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
347 /* Send the backup command to the right plugin*/
348 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
351 /* Loop getting filenames to backup then saving them */
352 while (!jcr->is_job_canceled()) {
353 memset(&sp, 0, sizeof(sp));
354 sp.pkt_size = sizeof(sp);
355 sp.pkt_end = sizeof(sp);
358 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
360 /* Get the file save parameters. I.e. the stat pkt ... */
361 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
365 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
369 jcr->plugin_sp = &sp;
372 * Copy fname and link because save_file() zaps them. This
373 * avoids zaping the plugin's strings.
375 ff_pkt->type = sp.type;
376 if (sp.type == FT_RESTORE_FIRST) {
377 if (!sp.object_name) {
378 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
382 ff_pkt->fname = cmd; /* full plugin string */
383 ff_pkt->object_name = sp.object_name;
384 ff_pkt->object_index = sp.index; /* restore object index */
385 ff_pkt->object_compression = 0; /* no compression for now */
386 ff_pkt->object = sp.object;
387 ff_pkt->object_len = sp.object_len;
390 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
394 pm_strcpy(fname, sp.fname);
395 pm_strcpy(link, sp.link);
396 ff_pkt->fname = fname.c_str();
397 ff_pkt->link = link.c_str();
400 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
401 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
403 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
405 /* Call Bacula core code to backup the plugin's file */
406 save_file(jcr, ff_pkt, true);
407 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
408 if (rc == bRC_More || rc == bRC_OK) {
409 accurate_mark_file_as_seen(jcr, fname.c_str());
411 if (rc == bRC_More) {
418 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
421 jcr->cmd_plugin = false;
423 jcr->plugin_ctx = NULL;
428 * Send plugin name start/end record to SD
430 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
433 int index = jcr->JobFiles;
434 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
437 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
440 if (jcr->is_job_canceled()) {
445 index++; /* JobFiles not incremented yet */
447 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
448 /* Send stream header */
449 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
450 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
454 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
457 /* Send data -- not much */
458 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
460 /* Send end of data */
461 stat = sd->fsend("0 0");
464 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
468 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
469 sd->signal(BNET_EOD); /* indicate end of plugin name data */
474 * Plugin name stream found during restore. The record passed in
475 * argument name was generated in send_plugin_name() above.
477 * Returns: true if start of stream
478 * false if end of steam
480 bool plugin_name_stream(JCR *jcr, char *name)
484 bool start, portable;
488 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
490 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
491 skip_nonspaces(&p); /* skip over jcr->JobFiles */
495 /* Start of plugin data */
496 skip_nonspaces(&p); /* skip start/end flag */
498 portable = *p == '1';
499 skip_nonspaces(&p); /* skip portable flag */
504 * End of plugin data, notify plugin, then clear flags
506 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
507 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
508 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
509 jcr->plugin->restoreFileStarted = false;
511 jcr->plugin_ctx = NULL;
515 if (!plugin_ctx_list) {
520 * After this point, we are dealing with a restore start
522 if (!get_plugin_name(jcr, cmd, &len)) {
527 * Search for correct plugin as specified on the command
529 foreach_alist(plugin, plugin_list) {
531 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
532 if (strncmp(plugin->file, cmd, len) != 0) {
536 jcr->plugin_ctx = &plugin_ctx_list[i];
537 jcr->plugin = plugin;
538 if (is_plugin_disabled(jcr)) {
541 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
542 event.eventType = bEventRestoreCommand;
543 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
544 &event, cmd) != bRC_OK) {
547 /* ***FIXME**** check error code */
548 if (plugin->restoreFileStarted) {
549 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
551 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
552 plugin->restoreFileStarted = true;
555 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
562 * Tell the plugin to create the file. Return values are
563 * This is called only during Restore
566 * CF_SKIP -- skip processing this file
567 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
568 * CF_CREATED -- created, but no content to extract (typically directories)
571 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
573 bpContext *plugin_ctx = jcr->plugin_ctx;
574 Plugin *plugin = jcr->plugin;
575 struct restore_pkt rp;
579 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
583 rp.pkt_size = sizeof(rp);
584 rp.pkt_end = sizeof(rp);
585 rp.stream = attr->stream;
586 rp.data_stream = attr->data_stream;
587 rp.type = attr->type;
588 rp.file_index = attr->file_index;
589 rp.LinkFI = attr->LinkFI;
591 rp.statp = attr->statp; /* structure assignment */
592 rp.attrEx = attr->attrEx;
593 rp.ofname = attr->ofname;
594 rp.olname = attr->olname;
595 rp.where = jcr->where;
596 rp.RegexWhere = jcr->RegexWhere;
597 rp.replace = jcr->replace;
598 rp.create_status = CF_ERROR;
599 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
600 rp.stream, rp.type, rp.LinkFI, rp.ofname);
602 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
604 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
606 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
610 if (rp.create_status == CF_SKIP) {
613 if (rp.create_status == CF_ERROR) {
614 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
618 /* Created link or directory? */
619 if (rp.create_status == CF_CREATED) {
620 return rp.create_status; /* yes, no need to bopen */
623 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
624 Dmsg0(dbglvl, "call bopen\n");
625 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
626 Dmsg1(50, "bopen status=%d\n", stat);
629 be.set_errno(bfd->berrno);
630 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
631 attr->ofname, be.bstrerror());
632 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
636 if (!is_bopen(bfd)) {
637 Dmsg0(000, "===== BFD is not open!!!!\n");
643 * Reset the file attributes after all file I/O is done -- this allows
644 * the previous access time/dates to be set properly, and it also allows
645 * us to properly set directory permissions.
646 * Not currently Implemented.
648 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
650 Dmsg0(dbglvl, "plugin_set_attributes\n");
654 pm_strcpy(attr->ofname, "*none*");
659 * Print to file the plugin info.
661 void dump_fd_plugin(Plugin *plugin, FILE *fp)
666 pInfo *info = (pInfo *)plugin->pinfo;
667 fprintf(fp, "\tversion=%d\n", info->version);
668 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
669 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
670 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
671 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
672 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
673 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
677 * This entry point is called internally by Bacula to ensure
678 * that the plugin IO calls come into this code.
680 void load_fd_plugins(const char *plugin_dir)
685 Dmsg0(dbglvl, "plugin dir is NULL\n");
689 plugin_list = New(alist(10, not_owned_by_alist));
690 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
691 is_plugin_compatible)) {
692 /* Either none found, or some error */
693 if (plugin_list->size() == 0) {
696 Dmsg0(dbglvl, "No plugins loaded\n");
701 /* Plug entry points called from findlib */
702 plugin_bopen = my_plugin_bopen;
703 plugin_bclose = my_plugin_bclose;
704 plugin_bread = my_plugin_bread;
705 plugin_bwrite = my_plugin_bwrite;
706 plugin_blseek = my_plugin_blseek;
709 * Verify that the plugin is acceptable, and print information
712 foreach_alist(plugin, plugin_list) {
713 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
714 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
717 dbg_plugin_add_hook(dump_fd_plugin);
721 * Check if a plugin is compatible. Called by the load_plugin function
722 * to allow us to verify the plugin.
724 static bool is_plugin_compatible(Plugin *plugin)
726 pInfo *info = (pInfo *)plugin->pinfo;
727 Dmsg0(50, "is_plugin_compatible called\n");
728 if (debug_level >= 50) {
729 dump_fd_plugin(plugin, stdin);
731 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
732 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
733 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
734 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
735 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
739 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
740 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
741 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
742 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
743 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
746 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
747 strcmp(info->plugin_license, "AGPLv3") != 0) {
748 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
749 plugin->file, info->plugin_license);
750 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
751 plugin->file, info->plugin_license);
760 * Create a new instance of each plugin for this Job
761 * Note, plugin_list can exist but jcr->plugin_ctx_list can
762 * be NULL if no plugins were loaded.
764 void new_plugins(JCR *jcr)
770 Dmsg0(dbglvl, "plugin list is NULL\n");
773 if (jcr->is_job_canceled() || jcr->JobId == 0) {
777 int num = plugin_list->size();
780 Dmsg0(dbglvl, "No plugins loaded\n");
784 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
786 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
787 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
788 foreach_alist(plugin, plugin_list) {
789 /* Start a new instance of each plugin */
790 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
791 memset(b_ctx, 0, sizeof(bacula_ctx));
793 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
794 plugin_ctx_list[i].pContext = NULL;
795 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
796 b_ctx->disabled = true;
802 * Free the plugin instances for this Job
804 void free_plugins(JCR *jcr)
809 if (!plugin_list || !jcr->plugin_ctx_list) {
810 return; /* no plugins, nothing to do */
813 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
814 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
815 foreach_alist(plugin, plugin_list) {
816 /* Free the plugin instance */
817 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
818 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
820 free(plugin_ctx_list);
821 jcr->plugin_ctx_list = NULL;
824 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
827 Plugin *plugin = (Plugin *)jcr->plugin;
830 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
831 if (!plugin || !jcr->plugin_ctx) {
834 io.pkt_size = sizeof(io);
835 io.pkt_end = sizeof(io);
844 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
845 bfd->berrno = io.io_errno;
847 errno = b_errno_win32;
850 bfd->lerror = io.lerror;
852 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
856 static int my_plugin_bclose(BFILE *bfd)
859 Plugin *plugin = (Plugin *)jcr->plugin;
862 Dmsg0(dbglvl, "===== plugin_bclose\n");
863 if (!plugin || !jcr->plugin_ctx) {
866 io.pkt_size = sizeof(io);
867 io.pkt_end = sizeof(io);
873 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
874 bfd->berrno = io.io_errno;
876 errno = b_errno_win32;
879 bfd->lerror = io.lerror;
881 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
885 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
888 Plugin *plugin = (Plugin *)jcr->plugin;
891 Dmsg0(dbglvl, "plugin_bread\n");
892 if (!plugin || !jcr->plugin_ctx) {
895 io.pkt_size = sizeof(io);
896 io.pkt_end = sizeof(io);
899 io.buf = (char *)buf;
902 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
903 bfd->berrno = io.io_errno;
905 errno = b_errno_win32;
908 bfd->lerror = io.lerror;
910 return (ssize_t)io.status;
913 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
916 Plugin *plugin = (Plugin *)jcr->plugin;
919 Dmsg0(dbglvl, "plugin_bwrite\n");
920 if (!plugin || !jcr->plugin_ctx) {
923 io.pkt_size = sizeof(io);
924 io.pkt_end = sizeof(io);
927 io.buf = (char *)buf;
930 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
931 bfd->berrno = io.io_errno;
933 errno = b_errno_win32;
936 bfd->lerror = io.lerror;
938 return (ssize_t)io.status;
941 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
944 Plugin *plugin = (Plugin *)jcr->plugin;
947 Dmsg0(dbglvl, "plugin_bseek\n");
948 if (!plugin || !jcr->plugin_ctx) {
951 io.pkt_size = sizeof(io);
952 io.pkt_end = sizeof(io);
958 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
959 bfd->berrno = io.io_errno;
961 errno = b_errno_win32;
964 bfd->lerror = io.lerror;
966 return (boffset_t)io.offset;
969 /* ==============================================================
971 * Callbacks from the plugin
973 * ==============================================================
975 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
982 switch (var) { /* General variables, no need of ctx */
984 *((char **)value) = my_name;
987 *(void **)value = me->working_directory;
990 *(char **)value = exepath;
996 if (!ctx) { /* Other variables need context */
1000 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1007 *((int *)value) = jcr->JobId;
1008 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1011 *((int *)value) = jcr->getJobLevel();
1012 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1015 *((int *)value) = jcr->getJobType();
1016 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1019 *((char **)value) = jcr->client_name;
1020 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1023 *((char **)value) = jcr->Job;
1024 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1027 *((int *)value) = jcr->JobStatus;
1028 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1031 *((int *)value) = (int)jcr->mtime;
1032 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1035 *((int *)value) = (int)jcr->accurate;
1036 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1039 break; /* a write only variable, ignore read request */
1043 *(void **)value = g_pVSSClient->GetVssObject();
1048 case bVarVssDllHandle:
1051 *(void **)value = g_pVSSClient->GetVssDllHandle();
1057 *(char **)value = jcr->where;
1059 case bVarRegexWhere:
1060 *(char **)value = jcr->RegexWhere;
1063 case bVarFDName: /* get warning with g++ if we missed one */
1064 case bVarWorkingDir:
1071 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1074 if (!value || !ctx) {
1077 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1078 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1082 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1085 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1095 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1104 va_start(args, ctx);
1105 while ((event = va_arg(args, uint32_t))) {
1106 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1112 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1113 int type, utime_t mtime, const char *fmt, ...)
1120 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1125 va_start(arg_ptr, fmt);
1126 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1128 Jmsg(jcr, type, mtime, "%s", buf);
1132 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1133 int level, const char *fmt, ...)
1138 va_start(arg_ptr, fmt);
1139 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1141 d_msg(file, line, level, "%s", buf);
1145 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1149 return sm_malloc(file, line, size);
1151 return malloc(size);
1155 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1158 sm_free(file, line, mem);
1164 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1169 bctx = (bacula_ctx *)ctx->bContext;
1181 * Let the plugin define files/directories to be excluded
1182 * from the main backup.
1184 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1188 if (!is_ctx_good(ctx, jcr, bctx)) {
1194 if (!bctx->exclude) {
1195 bctx->exclude = new_exclude(jcr);
1196 new_options(jcr, bctx->exclude);
1198 set_incexe(jcr, bctx->exclude);
1199 add_file_to_fileset(jcr, file, true);
1200 Dmsg1(100, "Add exclude file=%s\n", file);
1205 * Let the plugin define files/directories to be excluded
1206 * from the main backup.
1208 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1212 if (!is_ctx_good(ctx, jcr, bctx)) {
1218 if (!bctx->include) {
1219 bctx->include = new_preinclude(jcr);
1220 new_options(jcr, bctx->include);
1222 set_incexe(jcr, bctx->include);
1223 add_file_to_fileset(jcr, file, true);
1224 Dmsg1(100, "Add include file=%s\n", file);
1228 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1232 if (!is_ctx_good(ctx, jcr, bctx)) {
1238 add_options_to_fileset(jcr, opts);
1239 Dmsg1(1000, "Add options=%s\n", opts);
1243 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1247 if (!is_ctx_good(ctx, jcr, bctx)) {
1253 add_regex_to_fileset(jcr, item, type);
1254 Dmsg1(100, "Add regex=%s\n", item);
1258 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1262 if (!is_ctx_good(ctx, jcr, bctx)) {
1268 add_wild_to_fileset(jcr, item, type);
1269 Dmsg1(100, "Add wild=%s\n", item);
1273 static bRC baculaNewOptions(bpContext *ctx)
1277 if (!is_ctx_good(ctx, jcr, bctx)) {
1280 (void)new_options(jcr, NULL);
1284 static bRC baculaNewInclude(bpContext *ctx)
1288 if (!is_ctx_good(ctx, jcr, bctx)) {
1291 (void)new_include(jcr);
1297 * Check if a file have to be backuped using Accurate code
1299 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1304 bRC ret = bRC_Error;
1306 if (!is_ctx_good(ctx, jcr, bctx)) {
1315 * Copy fname and link because save_file() zaps them. This
1316 * avoids zaping the plugin's strings.
1318 ff_pkt->type = sp->type;
1320 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1324 ff_pkt->fname = sp->fname;
1325 ff_pkt->link = sp->link;
1326 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1328 if (check_changes(jcr, ff_pkt)) {
1335 Dmsg1(100, "checkChanges=%i\n", ret);
1342 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1343 int (*plugin_bclose)(JCR *jcr) = NULL;
1344 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1345 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1346 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1348 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1353 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1358 int main(int argc, char *argv[])
1360 char plugin_dir[1000];
1365 strcpy(my_name, "test-fd");
1367 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1368 load_fd_plugins(plugin_dir);
1376 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1377 generate_plugin_event(jcr1, bEventJobEnd);
1378 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1380 generate_plugin_event(jcr2, bEventJobEnd);
1385 Dmsg0(dbglvl, "bacula: OK ...\n");
1386 close_memory_pool();
1387 sm_dump(false); /* unit test */
1391 #endif /* TEST_PROGRAM */