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);
359 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
361 /* Get the file save parameters. I.e. the stat pkt ... */
362 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
366 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
370 jcr->plugin_sp = &sp;
373 * Copy fname and link because save_file() zaps them. This
374 * avoids zaping the plugin's strings.
376 ff_pkt->type = sp.type;
377 if (sp.type == FT_RESTORE_FIRST) {
378 if (!sp.object_name) {
379 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
383 ff_pkt->fname = cmd; /* full plugin string */
384 ff_pkt->object_name = sp.object_name;
385 ff_pkt->object_index = sp.index; /* restore object index */
386 ff_pkt->object_compression = 0; /* no compression for now */
387 ff_pkt->object = sp.object;
388 ff_pkt->object_len = sp.object_len;
391 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
395 pm_strcpy(fname, sp.fname);
396 pm_strcpy(link, sp.link);
397 ff_pkt->fname = fname.c_str();
398 ff_pkt->link = link.c_str();
399 ff_pkt->delta_seq = sp.delta_seq;
400 if (sp.flags & FO_DELTA) {
401 ff_pkt->flags |= FO_DELTA;
402 ff_pkt->delta_seq++; /* make new delta sequence number */
404 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
405 ff_pkt->delta_seq = 0;
409 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
410 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
412 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
414 /* Call Bacula core code to backup the plugin's file */
415 save_file(jcr, ff_pkt, true);
416 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
417 if (rc == bRC_More || rc == bRC_OK) {
418 accurate_mark_file_as_seen(jcr, fname.c_str());
420 if (rc == bRC_More) {
424 } /* end while loop */
426 } /* end loop over all plugins */
427 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
430 jcr->cmd_plugin = false;
432 jcr->plugin_ctx = NULL;
437 * Send plugin name start/end record to SD
439 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
442 int index = jcr->JobFiles;
443 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
446 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
449 if (jcr->is_job_canceled()) {
454 index++; /* JobFiles not incremented yet */
456 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
457 /* Send stream header */
458 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
459 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
463 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
466 /* Send data -- not much */
467 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
469 /* Send end of data */
470 stat = sd->fsend("0 0");
473 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
477 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
478 sd->signal(BNET_EOD); /* indicate end of plugin name data */
483 * Plugin name stream found during restore. The record passed in
484 * argument name was generated in send_plugin_name() above.
486 * Returns: true if start of stream
487 * false if end of steam
489 bool plugin_name_stream(JCR *jcr, char *name)
493 bool start, portable;
497 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
499 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
500 skip_nonspaces(&p); /* skip over jcr->JobFiles */
504 /* Start of plugin data */
505 skip_nonspaces(&p); /* skip start/end flag */
507 portable = *p == '1';
508 skip_nonspaces(&p); /* skip portable flag */
513 * End of plugin data, notify plugin, then clear flags
515 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
516 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
517 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
518 jcr->plugin->restoreFileStarted = false;
520 jcr->plugin_ctx = NULL;
524 if (!plugin_ctx_list) {
529 * After this point, we are dealing with a restore start
531 if (!get_plugin_name(jcr, cmd, &len)) {
536 * Search for correct plugin as specified on the command
538 foreach_alist(plugin, plugin_list) {
540 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
541 if (strncmp(plugin->file, cmd, len) != 0) {
545 jcr->plugin_ctx = &plugin_ctx_list[i];
546 jcr->plugin = plugin;
547 if (is_plugin_disabled(jcr)) {
550 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
551 event.eventType = bEventRestoreCommand;
552 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
553 &event, cmd) != bRC_OK) {
556 /* ***FIXME**** check error code */
557 if (plugin->restoreFileStarted) {
558 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
560 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
561 plugin->restoreFileStarted = true;
564 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
571 * Tell the plugin to create the file. Return values are
572 * This is called only during Restore
575 * CF_SKIP -- skip processing this file
576 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
577 * CF_CREATED -- created, but no content to extract (typically directories)
580 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
582 bpContext *plugin_ctx = jcr->plugin_ctx;
583 Plugin *plugin = jcr->plugin;
584 struct restore_pkt rp;
588 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
592 rp.pkt_size = sizeof(rp);
593 rp.pkt_end = sizeof(rp);
594 rp.stream = attr->stream;
595 rp.data_stream = attr->data_stream;
596 rp.type = attr->type;
597 rp.file_index = attr->file_index;
598 rp.LinkFI = attr->LinkFI;
600 rp.statp = attr->statp; /* structure assignment */
601 rp.attrEx = attr->attrEx;
602 rp.ofname = attr->ofname;
603 rp.olname = attr->olname;
604 rp.where = jcr->where;
605 rp.RegexWhere = jcr->RegexWhere;
606 rp.replace = jcr->replace;
607 rp.create_status = CF_ERROR;
608 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
609 rp.stream, rp.type, rp.LinkFI, rp.ofname);
611 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
613 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
615 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
619 if (rp.create_status == CF_SKIP) {
622 if (rp.create_status == CF_ERROR) {
623 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
627 /* Created link or directory? */
628 if (rp.create_status == CF_CREATED) {
629 return rp.create_status; /* yes, no need to bopen */
632 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
633 Dmsg0(dbglvl, "call bopen\n");
634 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
635 Dmsg1(50, "bopen status=%d\n", stat);
638 be.set_errno(bfd->berrno);
639 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
640 attr->ofname, be.bstrerror());
641 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
645 if (!is_bopen(bfd)) {
646 Dmsg0(000, "===== BFD is not open!!!!\n");
652 * Reset the file attributes after all file I/O is done -- this allows
653 * the previous access time/dates to be set properly, and it also allows
654 * us to properly set directory permissions.
655 * Not currently Implemented.
657 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
659 Dmsg0(dbglvl, "plugin_set_attributes\n");
663 pm_strcpy(attr->ofname, "*none*");
668 * Print to file the plugin info.
670 void dump_fd_plugin(Plugin *plugin, FILE *fp)
675 pInfo *info = (pInfo *)plugin->pinfo;
676 fprintf(fp, "\tversion=%d\n", info->version);
677 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
678 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
679 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
680 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
681 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
682 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
686 * This entry point is called internally by Bacula to ensure
687 * that the plugin IO calls come into this code.
689 void load_fd_plugins(const char *plugin_dir)
694 Dmsg0(dbglvl, "plugin dir is NULL\n");
698 plugin_list = New(alist(10, not_owned_by_alist));
699 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
700 is_plugin_compatible)) {
701 /* Either none found, or some error */
702 if (plugin_list->size() == 0) {
705 Dmsg0(dbglvl, "No plugins loaded\n");
710 /* Plug entry points called from findlib */
711 plugin_bopen = my_plugin_bopen;
712 plugin_bclose = my_plugin_bclose;
713 plugin_bread = my_plugin_bread;
714 plugin_bwrite = my_plugin_bwrite;
715 plugin_blseek = my_plugin_blseek;
718 * Verify that the plugin is acceptable, and print information
721 foreach_alist(plugin, plugin_list) {
722 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
723 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
726 dbg_plugin_add_hook(dump_fd_plugin);
730 * Check if a plugin is compatible. Called by the load_plugin function
731 * to allow us to verify the plugin.
733 static bool is_plugin_compatible(Plugin *plugin)
735 pInfo *info = (pInfo *)plugin->pinfo;
736 Dmsg0(50, "is_plugin_compatible called\n");
737 if (debug_level >= 50) {
738 dump_fd_plugin(plugin, stdin);
740 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
741 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
742 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
743 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
744 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
748 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
749 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
750 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
751 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
752 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
755 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
756 strcmp(info->plugin_license, "AGPLv3") != 0) {
757 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
758 plugin->file, info->plugin_license);
759 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
760 plugin->file, info->plugin_license);
769 * Create a new instance of each plugin for this Job
770 * Note, plugin_list can exist but jcr->plugin_ctx_list can
771 * be NULL if no plugins were loaded.
773 void new_plugins(JCR *jcr)
779 Dmsg0(dbglvl, "plugin list is NULL\n");
782 if (jcr->is_job_canceled() || jcr->JobId == 0) {
786 int num = plugin_list->size();
789 Dmsg0(dbglvl, "No plugins loaded\n");
793 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
795 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
796 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
797 foreach_alist(plugin, plugin_list) {
798 /* Start a new instance of each plugin */
799 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
800 memset(b_ctx, 0, sizeof(bacula_ctx));
802 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
803 plugin_ctx_list[i].pContext = NULL;
804 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
805 b_ctx->disabled = true;
811 * Free the plugin instances for this Job
813 void free_plugins(JCR *jcr)
818 if (!plugin_list || !jcr->plugin_ctx_list) {
819 return; /* no plugins, nothing to do */
822 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
823 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
824 foreach_alist(plugin, plugin_list) {
825 /* Free the plugin instance */
826 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
827 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
829 free(plugin_ctx_list);
830 jcr->plugin_ctx_list = NULL;
833 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
836 Plugin *plugin = (Plugin *)jcr->plugin;
839 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
840 if (!plugin || !jcr->plugin_ctx) {
843 io.pkt_size = sizeof(io);
844 io.pkt_end = sizeof(io);
853 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
854 bfd->berrno = io.io_errno;
856 errno = b_errno_win32;
859 bfd->lerror = io.lerror;
861 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
865 static int my_plugin_bclose(BFILE *bfd)
868 Plugin *plugin = (Plugin *)jcr->plugin;
871 Dmsg0(dbglvl, "===== plugin_bclose\n");
872 if (!plugin || !jcr->plugin_ctx) {
875 io.pkt_size = sizeof(io);
876 io.pkt_end = sizeof(io);
882 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
883 bfd->berrno = io.io_errno;
885 errno = b_errno_win32;
888 bfd->lerror = io.lerror;
890 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
894 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
897 Plugin *plugin = (Plugin *)jcr->plugin;
900 Dmsg0(dbglvl, "plugin_bread\n");
901 if (!plugin || !jcr->plugin_ctx) {
904 io.pkt_size = sizeof(io);
905 io.pkt_end = sizeof(io);
908 io.buf = (char *)buf;
911 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
912 bfd->berrno = io.io_errno;
914 errno = b_errno_win32;
917 bfd->lerror = io.lerror;
919 return (ssize_t)io.status;
922 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
925 Plugin *plugin = (Plugin *)jcr->plugin;
928 Dmsg0(dbglvl, "plugin_bwrite\n");
929 if (!plugin || !jcr->plugin_ctx) {
932 io.pkt_size = sizeof(io);
933 io.pkt_end = sizeof(io);
936 io.buf = (char *)buf;
939 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
940 bfd->berrno = io.io_errno;
942 errno = b_errno_win32;
945 bfd->lerror = io.lerror;
947 return (ssize_t)io.status;
950 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
953 Plugin *plugin = (Plugin *)jcr->plugin;
956 Dmsg0(dbglvl, "plugin_bseek\n");
957 if (!plugin || !jcr->plugin_ctx) {
960 io.pkt_size = sizeof(io);
961 io.pkt_end = sizeof(io);
967 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
968 bfd->berrno = io.io_errno;
970 errno = b_errno_win32;
973 bfd->lerror = io.lerror;
975 return (boffset_t)io.offset;
978 /* ==============================================================
980 * Callbacks from the plugin
982 * ==============================================================
984 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
991 switch (var) { /* General variables, no need of ctx */
993 *((char **)value) = my_name;
996 *(void **)value = me->working_directory;
999 *(char **)value = exepath;
1005 if (!ctx) { /* Other variables need context */
1009 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1016 *((int *)value) = jcr->JobId;
1017 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1020 *((int *)value) = jcr->getJobLevel();
1021 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1024 *((int *)value) = jcr->getJobType();
1025 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1028 *((char **)value) = jcr->client_name;
1029 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1032 *((char **)value) = jcr->Job;
1033 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1036 *((int *)value) = jcr->JobStatus;
1037 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1040 *((int *)value) = (int)jcr->mtime;
1041 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1044 *((int *)value) = (int)jcr->accurate;
1045 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1048 break; /* a write only variable, ignore read request */
1052 *(void **)value = g_pVSSClient->GetVssObject();
1057 case bVarVssDllHandle:
1060 *(void **)value = g_pVSSClient->GetVssDllHandle();
1066 *(char **)value = jcr->where;
1068 case bVarRegexWhere:
1069 *(char **)value = jcr->RegexWhere;
1072 case bVarFDName: /* get warning with g++ if we missed one */
1073 case bVarWorkingDir:
1080 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1083 if (!value || !ctx) {
1086 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1087 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1091 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1094 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1104 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1113 va_start(args, ctx);
1114 while ((event = va_arg(args, uint32_t))) {
1115 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1121 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1122 int type, utime_t mtime, const char *fmt, ...)
1129 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1134 va_start(arg_ptr, fmt);
1135 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1137 Jmsg(jcr, type, mtime, "%s", buf);
1141 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1142 int level, const char *fmt, ...)
1147 va_start(arg_ptr, fmt);
1148 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1150 d_msg(file, line, level, "%s", buf);
1154 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1158 return sm_malloc(file, line, size);
1160 return malloc(size);
1164 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1167 sm_free(file, line, mem);
1173 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1178 bctx = (bacula_ctx *)ctx->bContext;
1190 * Let the plugin define files/directories to be excluded
1191 * from the main backup.
1193 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1197 if (!is_ctx_good(ctx, jcr, bctx)) {
1203 if (!bctx->exclude) {
1204 bctx->exclude = new_exclude(jcr);
1205 new_options(jcr, bctx->exclude);
1207 set_incexe(jcr, bctx->exclude);
1208 add_file_to_fileset(jcr, file, true);
1209 Dmsg1(100, "Add exclude file=%s\n", file);
1214 * Let the plugin define files/directories to be excluded
1215 * from the main backup.
1217 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1221 if (!is_ctx_good(ctx, jcr, bctx)) {
1227 if (!bctx->include) {
1228 bctx->include = new_preinclude(jcr);
1229 new_options(jcr, bctx->include);
1231 set_incexe(jcr, bctx->include);
1232 add_file_to_fileset(jcr, file, true);
1233 Dmsg1(100, "Add include file=%s\n", file);
1237 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1241 if (!is_ctx_good(ctx, jcr, bctx)) {
1247 add_options_to_fileset(jcr, opts);
1248 Dmsg1(1000, "Add options=%s\n", opts);
1252 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1256 if (!is_ctx_good(ctx, jcr, bctx)) {
1262 add_regex_to_fileset(jcr, item, type);
1263 Dmsg1(100, "Add regex=%s\n", item);
1267 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1271 if (!is_ctx_good(ctx, jcr, bctx)) {
1277 add_wild_to_fileset(jcr, item, type);
1278 Dmsg1(100, "Add wild=%s\n", item);
1282 static bRC baculaNewOptions(bpContext *ctx)
1286 if (!is_ctx_good(ctx, jcr, bctx)) {
1289 (void)new_options(jcr, NULL);
1293 static bRC baculaNewInclude(bpContext *ctx)
1297 if (!is_ctx_good(ctx, jcr, bctx)) {
1300 (void)new_include(jcr);
1306 * Check if a file have to be backuped using Accurate code
1308 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1313 bRC ret = bRC_Error;
1315 if (!is_ctx_good(ctx, jcr, bctx)) {
1324 * Copy fname and link because save_file() zaps them. This
1325 * avoids zaping the plugin's strings.
1327 ff_pkt->type = sp->type;
1329 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1333 ff_pkt->fname = sp->fname;
1334 ff_pkt->link = sp->link;
1335 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1337 if (check_changes(jcr, ff_pkt)) {
1343 /* check_changes() can update delta sequence number, return it to the
1346 sp->delta_seq = ff_pkt->delta_seq;
1349 Dmsg1(100, "checkChanges=%i\n", ret);
1356 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1357 int (*plugin_bclose)(JCR *jcr) = NULL;
1358 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1359 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1360 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1362 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1367 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1372 int main(int argc, char *argv[])
1374 char plugin_dir[1000];
1379 strcpy(my_name, "test-fd");
1381 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1382 load_fd_plugins(plugin_dir);
1390 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1391 generate_plugin_event(jcr1, bEventJobEnd);
1392 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1394 generate_plugin_event(jcr2, bEventJobEnd);
1399 Dmsg0(dbglvl, "bacula: OK ...\n");
1400 close_memory_pool();
1401 sm_dump(false); /* unit test */
1405 #endif /* TEST_PROGRAM */