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);
90 #define for_this_plug(plugin, str, len) (((len) == (plugin)->len) && strncmp((plugin)->file, str, len) == 0)
93 static bInfo binfo = {
95 FD_PLUGIN_INTERFACE_VERSION
98 /* Bacula entry points */
99 static bFuncs bfuncs = {
101 FD_PLUGIN_INTERFACE_VERSION,
102 baculaRegisterEvents,
120 * Bacula private context
123 JCR *jcr; /* jcr for plugin */
124 bRC rc; /* last return code */
125 bool disabled; /* set if plugin disabled */
126 findINCEXE *exclude; /* pointer to exclude files */
127 findINCEXE *include; /* pointer to include/exclude files */
130 static bool is_plugin_disabled(bpContext *plugin_ctx)
136 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
137 return b_ctx->disabled;
140 static bool is_plugin_disabled(JCR *jcr)
142 return is_plugin_disabled(jcr->plugin_ctx);
146 * Create a plugin event
147 * When receiving bEventCancelCommand, this function is called by an other thread.
149 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
151 bpContext *plugin_ctx;
157 bool call_if_canceled = false;
160 if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
161 return; /* Return if no plugins loaded */
165 * Some events are sent to only a particular plugin or must be
166 * called even if the job is canceled
169 case bEventPluginCommand:
170 name = (char *)value;
171 if (!get_plugin_name(jcr, name, &len)) {
175 case bEventEndBackupJob:
176 case bEventEndVerifyJob:
177 call_if_canceled = true;
179 case bEventEndRestoreJob:
180 call_if_canceled = true;
181 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
182 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
183 jcr->plugin->restoreFileStarted = false;
190 if (!call_if_canceled && jcr->is_job_canceled()) {
194 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
195 event.eventType = eventType;
197 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
199 /* Pass event to every plugin (except if name is set) */
200 foreach_alist(plugin, plugin_list) {
201 if (name && !for_this_plug(plugin, name, len)) {
205 plugin_ctx = &plugin_ctx_list[i++];
206 if (is_plugin_disabled(plugin_ctx)) {
209 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
218 * Check if file was seen for accurate
220 bool plugin_check_file(JCR *jcr, char *fname)
226 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
227 return false; /* Return if no plugins loaded */
230 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
232 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
234 /* Pass event to every plugin */
235 foreach_alist(plugin, plugin_list) {
236 jcr->plugin_ctx = &plugin_ctx_list[i++];
237 jcr->plugin = plugin;
238 if (is_plugin_disabled(jcr)) {
241 if (plug_func(plugin)->checkFile == NULL) {
244 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
245 if (rc == bRC_Seen) {
251 jcr->plugin_ctx = NULL;
252 return rc == bRC_Seen;
255 /* Get the first part of the the plugin command
256 * systemstate:/@SYSTEMSTATE/
258 * => can use for_this_plug(plug, cmd, ret);
260 * The plugin command can contain only the plugin name
264 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
268 if (!cmd || (*cmd == '\0')) {
271 /* Handle plugin command here backup */
272 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
273 if ((p = strchr(cmd, ':')) == NULL) {
274 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
277 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
280 } else { /* plugin:argument */
291 * Sequence of calls for a backup:
292 * 1. plugin_save() here is called with ff_pkt
293 * 2. we find the plugin requested on the command string
294 * 3. we generate a bEventBackupCommand event to the specified plugin
295 * and pass it the command string.
296 * 4. we make a startPluginBackup call to the plugin, which gives
297 * us the data we need in save_pkt
298 * 5. we call Bacula's save_file() subroutine to save the specified
299 * file. The plugin will be called at pluginIO() to supply the
302 * Sequence of calls for restore:
303 * See subroutine plugin_name_stream() below.
305 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
310 char *cmd = ff_pkt->top_fname;
313 POOL_MEM fname(PM_FNAME);
314 POOL_MEM link(PM_FNAME);
316 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
317 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
318 return 1; /* Return if no plugins loaded */
321 jcr->cmd_plugin = true;
322 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
323 event.eventType = bEventBackupCommand;
325 if (!get_plugin_name(jcr, cmd, &len)) {
329 /* Note, we stop the loop on the first plugin that matches the name */
330 foreach_alist(plugin, plugin_list) {
331 Dmsg4(0, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->len, cmd, len);
332 if (!for_this_plug(plugin, cmd, len)) {
337 * We put the current plugin pointer, and the plugin context
338 * into the jcr, because during save_file(), the plugin
339 * will be called many times and these values are needed.
341 jcr->plugin_ctx = &plugin_ctx_list[i];
342 jcr->plugin = plugin;
343 if (is_plugin_disabled(jcr)) {
347 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
348 /* Send the backup command to the right plugin*/
349 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
352 /* Loop getting filenames to backup then saving them */
353 while (!jcr->is_job_canceled()) {
354 memset(&sp, 0, sizeof(sp));
355 sp.pkt_size = sizeof(sp);
356 sp.pkt_end = sizeof(sp);
360 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
362 /* Get the file save parameters. I.e. the stat pkt ... */
363 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
367 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
371 jcr->plugin_sp = &sp;
374 * Copy fname and link because save_file() zaps them. This
375 * avoids zaping the plugin's strings.
377 ff_pkt->type = sp.type;
378 if (sp.type == FT_RESTORE_FIRST) {
379 if (!sp.object_name) {
380 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
384 ff_pkt->fname = cmd; /* full plugin string */
385 ff_pkt->object_name = sp.object_name;
386 ff_pkt->object_index = sp.index; /* restore object index */
387 ff_pkt->object_compression = 0; /* no compression for now */
388 ff_pkt->object = sp.object;
389 ff_pkt->object_len = sp.object_len;
392 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
396 pm_strcpy(fname, sp.fname);
397 pm_strcpy(link, sp.link);
398 ff_pkt->fname = fname.c_str();
399 ff_pkt->link = link.c_str();
400 ff_pkt->delta_seq = sp.delta_seq;
401 if (sp.flags & FO_DELTA) {
402 ff_pkt->flags |= FO_DELTA;
403 ff_pkt->delta_seq++; /* make new delta sequence number */
405 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
406 ff_pkt->delta_seq = 0;
410 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
411 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
413 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
415 /* Call Bacula core code to backup the plugin's file */
416 save_file(jcr, ff_pkt, true);
417 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
418 if (rc == bRC_More || rc == bRC_OK) {
419 accurate_mark_file_as_seen(jcr, fname.c_str());
421 if (rc == bRC_More) {
425 } /* end while loop */
427 } /* end loop over all plugins */
428 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
431 jcr->cmd_plugin = false;
433 jcr->plugin_ctx = NULL;
438 * Send plugin name start/end record to SD
440 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
443 int index = jcr->JobFiles;
444 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
447 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
450 if (jcr->is_job_canceled()) {
455 index++; /* JobFiles not incremented yet */
457 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
458 /* Send stream header */
459 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
460 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
464 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
467 /* Send data -- not much */
468 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
470 /* Send end of data */
471 stat = sd->fsend("0 0");
474 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
478 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
479 sd->signal(BNET_EOD); /* indicate end of plugin name data */
484 * Plugin name stream found during restore. The record passed in
485 * argument name was generated in send_plugin_name() above.
487 * Returns: true if start of stream
488 * false if end of steam
490 bool plugin_name_stream(JCR *jcr, char *name)
494 bool start, portable;
498 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
500 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
501 skip_nonspaces(&p); /* skip over jcr->JobFiles */
505 /* Start of plugin data */
506 skip_nonspaces(&p); /* skip start/end flag */
508 portable = *p == '1';
509 skip_nonspaces(&p); /* skip portable flag */
514 * End of plugin data, notify plugin, then clear flags
516 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
517 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
518 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
519 jcr->plugin->restoreFileStarted = false;
521 jcr->plugin_ctx = NULL;
525 if (!plugin_ctx_list) {
530 * After this point, we are dealing with a restore start
532 if (!get_plugin_name(jcr, cmd, &len)) {
537 * Search for correct plugin as specified on the command
539 foreach_alist(plugin, plugin_list) {
541 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
542 if (!for_this_plug(plugin, cmd, len)) {
546 jcr->plugin_ctx = &plugin_ctx_list[i];
547 jcr->plugin = plugin;
548 if (is_plugin_disabled(jcr)) {
551 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
552 event.eventType = bEventRestoreCommand;
553 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
554 &event, cmd) != bRC_OK) {
557 /* ***FIXME**** check error code */
558 if (plugin->restoreFileStarted) {
559 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
561 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
562 plugin->restoreFileStarted = true;
565 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
572 * Tell the plugin to create the file. Return values are
573 * This is called only during Restore
576 * CF_SKIP -- skip processing this file
577 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
578 * CF_CREATED -- created, but no content to extract (typically directories)
581 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
583 bpContext *plugin_ctx = jcr->plugin_ctx;
584 Plugin *plugin = jcr->plugin;
585 struct restore_pkt rp;
589 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
593 rp.pkt_size = sizeof(rp);
594 rp.pkt_end = sizeof(rp);
595 rp.stream = attr->stream;
596 rp.data_stream = attr->data_stream;
597 rp.type = attr->type;
598 rp.file_index = attr->file_index;
599 rp.LinkFI = attr->LinkFI;
601 rp.statp = attr->statp; /* structure assignment */
602 rp.attrEx = attr->attrEx;
603 rp.ofname = attr->ofname;
604 rp.olname = attr->olname;
605 rp.where = jcr->where;
606 rp.RegexWhere = jcr->RegexWhere;
607 rp.replace = jcr->replace;
608 rp.create_status = CF_ERROR;
609 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
610 rp.stream, rp.type, rp.LinkFI, rp.ofname);
612 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
614 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
616 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
620 if (rp.create_status == CF_SKIP) {
623 if (rp.create_status == CF_ERROR) {
624 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
628 /* Created link or directory? */
629 if (rp.create_status == CF_CREATED) {
630 return rp.create_status; /* yes, no need to bopen */
633 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
634 Dmsg0(dbglvl, "call bopen\n");
635 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
636 Dmsg1(50, "bopen status=%d\n", stat);
639 be.set_errno(bfd->berrno);
640 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
641 attr->ofname, be.bstrerror());
642 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
646 if (!is_bopen(bfd)) {
647 Dmsg0(000, "===== BFD is not open!!!!\n");
653 * Reset the file attributes after all file I/O is done -- this allows
654 * the previous access time/dates to be set properly, and it also allows
655 * us to properly set directory permissions.
656 * Not currently Implemented.
658 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
660 Dmsg0(dbglvl, "plugin_set_attributes\n");
664 pm_strcpy(attr->ofname, "*none*");
669 * Print to file the plugin info.
671 void dump_fd_plugin(Plugin *plugin, FILE *fp)
676 pInfo *info = (pInfo *)plugin->pinfo;
677 fprintf(fp, "\tversion=%d\n", info->version);
678 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
679 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
680 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
681 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
682 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
683 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
687 * This entry point is called internally by Bacula to ensure
688 * that the plugin IO calls come into this code.
690 void load_fd_plugins(const char *plugin_dir)
695 Dmsg0(dbglvl, "plugin dir is NULL\n");
699 plugin_list = New(alist(10, not_owned_by_alist));
700 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
701 is_plugin_compatible)) {
702 /* Either none found, or some error */
703 if (plugin_list->size() == 0) {
706 Dmsg0(dbglvl, "No plugins loaded\n");
711 /* Plug entry points called from findlib */
712 plugin_bopen = my_plugin_bopen;
713 plugin_bclose = my_plugin_bclose;
714 plugin_bread = my_plugin_bread;
715 plugin_bwrite = my_plugin_bwrite;
716 plugin_blseek = my_plugin_blseek;
719 * Verify that the plugin is acceptable, and print information
722 foreach_alist(plugin, plugin_list) {
723 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
724 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
727 dbg_plugin_add_hook(dump_fd_plugin);
731 * Check if a plugin is compatible. Called by the load_plugin function
732 * to allow us to verify the plugin.
734 static bool is_plugin_compatible(Plugin *plugin)
736 pInfo *info = (pInfo *)plugin->pinfo;
737 Dmsg0(50, "is_plugin_compatible called\n");
738 if (debug_level >= 50) {
739 dump_fd_plugin(plugin, stdin);
741 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
742 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
743 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
744 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
745 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
749 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
750 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
751 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
752 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
753 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
756 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
757 strcmp(info->plugin_license, "AGPLv3") != 0) {
758 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
759 plugin->file, info->plugin_license);
760 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
761 plugin->file, info->plugin_license);
770 * Create a new instance of each plugin for this Job
771 * Note, plugin_list can exist but jcr->plugin_ctx_list can
772 * be NULL if no plugins were loaded.
774 void new_plugins(JCR *jcr)
780 Dmsg0(dbglvl, "plugin list is NULL\n");
783 if (jcr->is_job_canceled() || jcr->JobId == 0) {
787 int num = plugin_list->size();
790 Dmsg0(dbglvl, "No plugins loaded\n");
794 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
796 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
797 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
798 foreach_alist(plugin, plugin_list) {
799 /* Start a new instance of each plugin */
800 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
801 memset(b_ctx, 0, sizeof(bacula_ctx));
803 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
804 plugin_ctx_list[i].pContext = NULL;
805 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
806 b_ctx->disabled = true;
812 * Free the plugin instances for this Job
814 void free_plugins(JCR *jcr)
819 if (!plugin_list || !jcr->plugin_ctx_list) {
820 return; /* no plugins, nothing to do */
823 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
824 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
825 foreach_alist(plugin, plugin_list) {
826 /* Free the plugin instance */
827 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
828 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
830 free(plugin_ctx_list);
831 jcr->plugin_ctx_list = NULL;
834 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
837 Plugin *plugin = (Plugin *)jcr->plugin;
840 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
841 if (!plugin || !jcr->plugin_ctx) {
844 io.pkt_size = sizeof(io);
845 io.pkt_end = sizeof(io);
854 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
855 bfd->berrno = io.io_errno;
857 errno = b_errno_win32;
860 bfd->lerror = io.lerror;
862 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
866 static int my_plugin_bclose(BFILE *bfd)
869 Plugin *plugin = (Plugin *)jcr->plugin;
872 Dmsg0(dbglvl, "===== plugin_bclose\n");
873 if (!plugin || !jcr->plugin_ctx) {
876 io.pkt_size = sizeof(io);
877 io.pkt_end = sizeof(io);
883 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
884 bfd->berrno = io.io_errno;
886 errno = b_errno_win32;
889 bfd->lerror = io.lerror;
891 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
895 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
898 Plugin *plugin = (Plugin *)jcr->plugin;
901 Dmsg0(dbglvl, "plugin_bread\n");
902 if (!plugin || !jcr->plugin_ctx) {
905 io.pkt_size = sizeof(io);
906 io.pkt_end = sizeof(io);
909 io.buf = (char *)buf;
913 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
914 bfd->offset = io.offset;
915 bfd->berrno = io.io_errno;
917 errno = b_errno_win32;
920 bfd->lerror = io.lerror;
922 return (ssize_t)io.status;
925 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
928 Plugin *plugin = (Plugin *)jcr->plugin;
931 Dmsg0(dbglvl, "plugin_bwrite\n");
932 if (!plugin || !jcr->plugin_ctx) {
935 io.pkt_size = sizeof(io);
936 io.pkt_end = sizeof(io);
939 io.buf = (char *)buf;
942 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
943 bfd->berrno = io.io_errno;
945 errno = b_errno_win32;
948 bfd->lerror = io.lerror;
950 return (ssize_t)io.status;
953 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
956 Plugin *plugin = (Plugin *)jcr->plugin;
959 Dmsg0(dbglvl, "plugin_bseek\n");
960 if (!plugin || !jcr->plugin_ctx) {
963 io.pkt_size = sizeof(io);
964 io.pkt_end = sizeof(io);
970 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
971 bfd->berrno = io.io_errno;
973 errno = b_errno_win32;
976 bfd->lerror = io.lerror;
978 return (boffset_t)io.offset;
981 /* ==============================================================
983 * Callbacks from the plugin
985 * ==============================================================
987 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
994 switch (var) { /* General variables, no need of ctx */
996 *((char **)value) = my_name;
999 *(void **)value = me->working_directory;
1002 *(char **)value = exepath;
1008 if (!ctx) { /* Other variables need context */
1012 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1019 *((int *)value) = jcr->JobId;
1020 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1023 *((int *)value) = jcr->getJobLevel();
1024 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1027 *((int *)value) = jcr->getJobType();
1028 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1031 *((char **)value) = jcr->client_name;
1032 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1035 *((char **)value) = jcr->Job;
1036 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1039 *((int *)value) = jcr->JobStatus;
1040 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1043 *((int *)value) = (int)jcr->mtime;
1044 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1047 *((int *)value) = (int)jcr->accurate;
1048 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1051 break; /* a write only variable, ignore read request */
1055 *(void **)value = g_pVSSClient->GetVssObject();
1060 case bVarVssDllHandle:
1063 *(void **)value = g_pVSSClient->GetVssDllHandle();
1069 *(char **)value = jcr->where;
1071 case bVarRegexWhere:
1072 *(char **)value = jcr->RegexWhere;
1075 case bVarFDName: /* get warning with g++ if we missed one */
1076 case bVarWorkingDir:
1083 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1086 if (!value || !ctx) {
1089 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1090 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1094 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1097 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1107 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1116 va_start(args, ctx);
1117 while ((event = va_arg(args, uint32_t))) {
1118 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1124 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1125 int type, utime_t mtime, const char *fmt, ...)
1132 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1137 va_start(arg_ptr, fmt);
1138 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1140 Jmsg(jcr, type, mtime, "%s", buf);
1144 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1145 int level, const char *fmt, ...)
1150 va_start(arg_ptr, fmt);
1151 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1153 d_msg(file, line, level, "%s", buf);
1157 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1161 return sm_malloc(file, line, size);
1163 return malloc(size);
1167 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1170 sm_free(file, line, mem);
1176 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1181 bctx = (bacula_ctx *)ctx->bContext;
1193 * Let the plugin define files/directories to be excluded
1194 * from the main backup.
1196 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1200 if (!is_ctx_good(ctx, jcr, bctx)) {
1206 if (!bctx->exclude) {
1207 bctx->exclude = new_exclude(jcr);
1208 new_options(jcr, bctx->exclude);
1210 set_incexe(jcr, bctx->exclude);
1211 add_file_to_fileset(jcr, file, true);
1212 Dmsg1(100, "Add exclude file=%s\n", file);
1217 * Let the plugin define files/directories to be excluded
1218 * from the main backup.
1220 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1224 if (!is_ctx_good(ctx, jcr, bctx)) {
1230 if (!bctx->include) {
1231 bctx->include = new_preinclude(jcr);
1232 new_options(jcr, bctx->include);
1234 set_incexe(jcr, bctx->include);
1235 add_file_to_fileset(jcr, file, true);
1236 Dmsg1(100, "Add include file=%s\n", file);
1240 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1244 if (!is_ctx_good(ctx, jcr, bctx)) {
1250 add_options_to_fileset(jcr, opts);
1251 Dmsg1(1000, "Add options=%s\n", opts);
1255 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1259 if (!is_ctx_good(ctx, jcr, bctx)) {
1265 add_regex_to_fileset(jcr, item, type);
1266 Dmsg1(100, "Add regex=%s\n", item);
1270 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1274 if (!is_ctx_good(ctx, jcr, bctx)) {
1280 add_wild_to_fileset(jcr, item, type);
1281 Dmsg1(100, "Add wild=%s\n", item);
1285 static bRC baculaNewOptions(bpContext *ctx)
1289 if (!is_ctx_good(ctx, jcr, bctx)) {
1292 (void)new_options(jcr, NULL);
1296 static bRC baculaNewInclude(bpContext *ctx)
1300 if (!is_ctx_good(ctx, jcr, bctx)) {
1303 (void)new_include(jcr);
1309 * Check if a file have to be backuped using Accurate code
1311 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1316 bRC ret = bRC_Error;
1318 if (!is_ctx_good(ctx, jcr, bctx)) {
1327 * Copy fname and link because save_file() zaps them. This
1328 * avoids zaping the plugin's strings.
1330 ff_pkt->type = sp->type;
1332 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1336 ff_pkt->fname = sp->fname;
1337 ff_pkt->link = sp->link;
1338 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1340 if (check_changes(jcr, ff_pkt)) {
1346 /* check_changes() can update delta sequence number, return it to the
1349 sp->delta_seq = ff_pkt->delta_seq;
1352 Dmsg1(100, "checkChanges=%i\n", ret);
1359 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1360 int (*plugin_bclose)(JCR *jcr) = NULL;
1361 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1362 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1363 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1365 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1370 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1375 int main(int argc, char *argv[])
1377 char plugin_dir[1000];
1382 strcpy(my_name, "test-fd");
1384 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1385 load_fd_plugins(plugin_dir);
1393 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1394 generate_plugin_event(jcr1, bEventJobEnd);
1395 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1397 generate_plugin_event(jcr2, bEventJobEnd);
1402 Dmsg0(dbglvl, "bacula: OK ...\n");
1403 close_memory_pool();
1404 sm_dump(false); /* unit test */
1408 #endif /* TEST_PROGRAM */