2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2011 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 bool is_plugin_compatible(Plugin *plugin);
80 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
81 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
84 * These will be plugged into the global pointer structure for
87 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
88 static int my_plugin_bclose(BFILE *bfd);
89 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
90 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
91 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
93 #define for_this_plug(plugin, str, len) (((len) == (plugin)->file_len) && strncmp((plugin)->file, str, len) == 0)
96 static bInfo binfo = {
98 FD_PLUGIN_INTERFACE_VERSION
101 /* Bacula entry points */
102 static bFuncs bfuncs = {
104 FD_PLUGIN_INTERFACE_VERSION,
105 baculaRegisterEvents,
123 * Bacula private context
126 JCR *jcr; /* jcr for plugin */
127 bRC rc; /* last return code */
128 bool disabled; /* set if plugin disabled */
129 findINCEXE *exclude; /* pointer to exclude files */
130 findINCEXE *include; /* pointer to include/exclude files */
133 static bool is_plugin_disabled(bpContext *plugin_ctx)
139 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
140 return b_ctx->disabled;
143 static bool is_plugin_disabled(JCR *jcr)
145 return is_plugin_disabled(jcr->plugin_ctx);
149 * Create a plugin event
150 * When receiving bEventCancelCommand, this function is called by an other thread.
152 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
154 bpContext *plugin_ctx;
160 bool call_if_canceled = false;
163 if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
164 return; /* Return if no plugins loaded */
168 * Some events are sent to only a particular plugin or must be
169 * called even if the job is canceled
172 case bEventPluginCommand:
173 name = (char *)value;
174 if (!get_plugin_name(jcr, name, &len)) {
178 case bEventEndBackupJob:
179 case bEventEndVerifyJob:
180 call_if_canceled = true;
182 case bEventStartRestoreJob:
184 jcr->plugin->restoreFileStarted = false;
185 jcr->plugin->createFileCalled = false;
188 case bEventEndRestoreJob:
189 call_if_canceled = true;
190 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
191 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
192 jcr->plugin->restoreFileStarted = false;
193 jcr->plugin->createFileCalled = false;
200 if (!call_if_canceled && jcr->is_job_canceled()) {
204 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
205 event.eventType = eventType;
207 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
210 * Pass event to every plugin (except if name is set). If name
211 * is set, we pass it only to the plugin with that name.
213 foreach_alist(plugin, plugin_list) {
214 if (name && !for_this_plug(plugin, name, len)) {
218 plugin_ctx = &plugin_ctx_list[i++];
219 if (is_plugin_disabled(plugin_ctx)) {
222 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
231 * Check if file was seen for accurate
233 bool plugin_check_file(JCR *jcr, char *fname)
239 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
240 return false; /* Return if no plugins loaded */
243 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
245 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
247 /* Pass event to every plugin */
248 foreach_alist(plugin, plugin_list) {
249 jcr->plugin_ctx = &plugin_ctx_list[i++];
250 jcr->plugin = plugin;
251 if (is_plugin_disabled(jcr)) {
254 if (plug_func(plugin)->checkFile == NULL) {
257 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
258 if (rc == bRC_Seen) {
264 jcr->plugin_ctx = NULL;
265 return rc == bRC_Seen;
268 /* Get the first part of the the plugin command
269 * systemstate:/@SYSTEMSTATE/
271 * => can use for_this_plug(plug, cmd, ret);
273 * The plugin command can contain only the plugin name
277 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
281 if (!cmd || (*cmd == '\0')) {
284 /* Handle plugin command here backup */
285 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
286 if ((p = strchr(cmd, ':')) == NULL) {
287 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
290 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
293 } else { /* plugin:argument */
304 * Sequence of calls for a backup:
305 * 1. plugin_save() here is called with ff_pkt
306 * 2. we find the plugin requested on the command string
307 * 3. we generate a bEventBackupCommand event to the specified plugin
308 * and pass it the command string.
309 * 4. we make a startPluginBackup call to the plugin, which gives
310 * us the data we need in save_pkt
311 * 5. we call Bacula's save_file() subroutine to save the specified
312 * file. The plugin will be called at pluginIO() to supply the
315 * Sequence of calls for restore:
316 * See subroutine plugin_name_stream() below.
318 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
323 char *cmd = ff_pkt->top_fname;
326 POOL_MEM fname(PM_FNAME);
327 POOL_MEM link(PM_FNAME);
329 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
330 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
331 return 1; /* Return if no plugins loaded */
334 jcr->cmd_plugin = true;
335 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
336 event.eventType = bEventBackupCommand;
338 if (!get_plugin_name(jcr, cmd, &len)) {
342 /* Note, we stop the loop on the first plugin that matches the name */
343 foreach_alist(plugin, plugin_list) {
344 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
345 if (!for_this_plug(plugin, cmd, len)) {
350 * We put the current plugin pointer, and the plugin context
351 * into the jcr, because during save_file(), the plugin
352 * will be called many times and these values are needed.
354 jcr->plugin_ctx = &plugin_ctx_list[i];
355 jcr->plugin = plugin;
356 if (is_plugin_disabled(jcr)) {
360 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
361 /* Send the backup command to the right plugin*/
362 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
365 /* Loop getting filenames to backup then saving them */
366 while (!jcr->is_job_canceled()) {
367 memset(&sp, 0, sizeof(sp));
368 sp.pkt_size = sizeof(sp);
369 sp.pkt_end = sizeof(sp);
373 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
375 /* Get the file save parameters. I.e. the stat pkt ... */
376 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
380 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
384 jcr->plugin_sp = &sp;
387 * Copy fname and link because save_file() zaps them. This
388 * avoids zaping the plugin's strings.
390 ff_pkt->type = sp.type;
391 if (sp.type == FT_RESTORE_FIRST) {
392 if (!sp.object_name) {
393 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
397 ff_pkt->fname = cmd; /* full plugin string */
398 ff_pkt->object_name = sp.object_name;
399 ff_pkt->object_index = sp.index; /* restore object index */
400 ff_pkt->object_compression = 0; /* no compression for now */
401 ff_pkt->object = sp.object;
402 ff_pkt->object_len = sp.object_len;
405 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
409 pm_strcpy(fname, sp.fname);
410 pm_strcpy(link, sp.link);
411 ff_pkt->fname = fname.c_str();
412 ff_pkt->link = link.c_str();
413 ff_pkt->delta_seq = sp.delta_seq;
414 if (sp.flags & FO_DELTA) {
415 ff_pkt->flags |= FO_DELTA;
416 ff_pkt->delta_seq++; /* make new delta sequence number */
418 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
419 ff_pkt->delta_seq = 0;
421 if (sp.flags & FO_OFFSETS) {
422 ff_pkt->flags |= FO_OFFSETS;
424 if (sp.flags & FO_PORTABLE_DATA) {
425 ff_pkt->flags |= FO_PORTABLE_DATA;
427 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
430 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
431 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
433 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
435 /* Call Bacula core code to backup the plugin's file */
436 save_file(jcr, ff_pkt, true);
437 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
438 if (rc == bRC_More || rc == bRC_OK) {
439 accurate_mark_file_as_seen(jcr, fname.c_str());
441 if (rc == bRC_More) {
445 } /* end while loop */
447 } /* end loop over all plugins */
448 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
451 jcr->cmd_plugin = false;
453 jcr->plugin_ctx = NULL;
458 * Send plugin name start/end record to SD
460 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
463 int index = jcr->JobFiles;
464 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
467 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
470 if (jcr->is_job_canceled()) {
475 index++; /* JobFiles not incremented yet */
477 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
478 /* Send stream header */
479 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
480 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
484 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
487 /* Send data -- not much */
488 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
490 /* Send end of data */
491 stat = sd->fsend("%ld 0", jcr->JobFiles);
494 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
498 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
499 sd->signal(BNET_EOD); /* indicate end of plugin name data */
504 * Plugin name stream found during restore. The record passed in
505 * argument name was generated in send_plugin_name() above.
507 * Returns: true if start of stream
508 * false if end of steam
510 bool plugin_name_stream(JCR *jcr, char *name)
514 bool start, portable;
518 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
520 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
521 skip_nonspaces(&p); /* skip over jcr->JobFiles */
525 /* Start of plugin data */
526 skip_nonspaces(&p); /* skip start/end flag */
528 portable = *p == '1';
529 skip_nonspaces(&p); /* skip portable flag */
534 * End of plugin data, notify plugin, then clear flags
536 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
537 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
538 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
539 jcr->plugin->restoreFileStarted = false;
540 jcr->plugin->createFileCalled = false;
542 jcr->plugin_ctx = NULL;
546 if (!plugin_ctx_list) {
551 * After this point, we are dealing with a restore start
553 if (!get_plugin_name(jcr, cmd, &len)) {
558 * Search for correct plugin as specified on the command
560 foreach_alist(plugin, plugin_list) {
562 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
563 if (!for_this_plug(plugin, cmd, len)) {
567 jcr->plugin_ctx = &plugin_ctx_list[i];
568 jcr->plugin = plugin;
569 if (is_plugin_disabled(jcr)) {
572 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
573 event.eventType = bEventRestoreCommand;
574 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
575 &event, cmd) != bRC_OK) {
578 if (plugin->restoreFileStarted)
580 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile\n");
583 plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd);
584 plugin->restoreFileStarted = true;
587 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
594 * Tell the plugin to create the file. Return values are
595 * This is called only during Restore
598 * CF_SKIP -- skip processing this file
599 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
600 * CF_CREATED -- created, but no content to extract (typically directories)
603 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
605 bpContext *plugin_ctx = jcr->plugin_ctx;
606 Plugin *plugin = jcr->plugin;
607 struct restore_pkt rp;
611 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
615 rp.pkt_size = sizeof(rp);
616 rp.pkt_end = sizeof(rp);
617 rp.delta_seq = attr->delta_seq;
618 rp.stream = attr->stream;
619 rp.data_stream = attr->data_stream;
620 rp.type = attr->type;
621 rp.file_index = attr->file_index;
622 rp.LinkFI = attr->LinkFI;
624 rp.statp = attr->statp; /* structure assignment */
625 rp.attrEx = attr->attrEx;
626 rp.ofname = attr->ofname;
627 rp.olname = attr->olname;
628 rp.where = jcr->where;
629 rp.RegexWhere = jcr->RegexWhere;
630 rp.replace = jcr->replace;
631 rp.create_status = CF_ERROR;
632 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
633 rp.stream, rp.type, rp.LinkFI, rp.ofname);
635 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
637 if (!plugin->restoreFileStarted || plugin->createFileCalled)
639 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
642 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
644 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
648 if (rp.create_status == CF_SKIP) {
651 if (rp.create_status == CF_ERROR) {
652 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
656 /* Created link or directory? */
657 if (rp.create_status == CF_CREATED) {
658 return rp.create_status; /* yes, no need to bopen */
661 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
662 Dmsg0(dbglvl, "call bopen\n");
663 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
664 Dmsg1(50, "bopen status=%d\n", stat);
667 be.set_errno(bfd->berrno);
668 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
669 attr->ofname, be.bstrerror());
670 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
674 if (!is_bopen(bfd)) {
675 Dmsg0(000, "===== BFD is not open!!!!\n");
681 * Reset the file attributes after all file I/O is done -- this allows
682 * the previous access time/dates to be set properly, and it also allows
683 * us to properly set directory permissions.
684 * Not currently Implemented.
686 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
688 Dmsg0(dbglvl, "plugin_set_attributes\n");
692 pm_strcpy(attr->ofname, "*none*");
697 * Print to file the plugin info.
699 void dump_fd_plugin(Plugin *plugin, FILE *fp)
704 pInfo *info = (pInfo *)plugin->pinfo;
705 fprintf(fp, "\tversion=%d\n", info->version);
706 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
707 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
708 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
709 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
710 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
711 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
715 * This entry point is called internally by Bacula to ensure
716 * that the plugin IO calls come into this code.
718 void load_fd_plugins(const char *plugin_dir)
723 Dmsg0(dbglvl, "plugin dir is NULL\n");
727 plugin_list = New(alist(10, not_owned_by_alist));
728 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
729 is_plugin_compatible)) {
730 /* Either none found, or some error */
731 if (plugin_list->size() == 0) {
734 Dmsg0(dbglvl, "No plugins loaded\n");
739 /* Plug entry points called from findlib */
740 plugin_bopen = my_plugin_bopen;
741 plugin_bclose = my_plugin_bclose;
742 plugin_bread = my_plugin_bread;
743 plugin_bwrite = my_plugin_bwrite;
744 plugin_blseek = my_plugin_blseek;
747 * Verify that the plugin is acceptable, and print information
750 foreach_alist(plugin, plugin_list) {
751 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
752 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
755 dbg_plugin_add_hook(dump_fd_plugin);
759 * Check if a plugin is compatible. Called by the load_plugin function
760 * to allow us to verify the plugin.
762 static bool is_plugin_compatible(Plugin *plugin)
764 pInfo *info = (pInfo *)plugin->pinfo;
765 Dmsg0(50, "is_plugin_compatible called\n");
766 if (debug_level >= 50) {
767 dump_fd_plugin(plugin, stdin);
769 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
770 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
771 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
772 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
773 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
777 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
778 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
779 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
780 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
781 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
784 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
785 strcmp(info->plugin_license, "AGPLv3") != 0) {
786 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
787 plugin->file, info->plugin_license);
788 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
789 plugin->file, info->plugin_license);
792 if (info->size != sizeof(pInfo)) {
793 Jmsg(NULL, M_ERROR, 0, _("Plugin size mismatch.\n"));
801 * Create a new instance of each plugin for this Job
802 * Note, plugin_list can exist but jcr->plugin_ctx_list can
803 * be NULL if no plugins were loaded.
805 void new_plugins(JCR *jcr)
811 Dmsg0(dbglvl, "plugin list is NULL\n");
814 if (jcr->is_job_canceled() || jcr->JobId == 0) {
818 int num = plugin_list->size();
821 Dmsg0(dbglvl, "No plugins loaded\n");
825 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
827 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
828 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
829 foreach_alist(plugin, plugin_list) {
830 /* Start a new instance of each plugin */
831 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
832 memset(b_ctx, 0, sizeof(bacula_ctx));
834 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
835 plugin_ctx_list[i].pContext = NULL;
836 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
837 b_ctx->disabled = true;
843 * Free the plugin instances for this Job
845 void free_plugins(JCR *jcr)
850 if (!plugin_list || !jcr->plugin_ctx_list) {
851 return; /* no plugins, nothing to do */
854 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
855 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
856 foreach_alist(plugin, plugin_list) {
857 /* Free the plugin instance */
858 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
859 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
861 free(plugin_ctx_list);
862 jcr->plugin_ctx_list = NULL;
865 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
868 Plugin *plugin = (Plugin *)jcr->plugin;
871 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
872 if (!plugin || !jcr->plugin_ctx) {
875 io.pkt_size = sizeof(io);
876 io.pkt_end = sizeof(io);
885 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
886 bfd->berrno = io.io_errno;
888 errno = b_errno_win32;
891 bfd->lerror = io.lerror;
893 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
897 static int my_plugin_bclose(BFILE *bfd)
900 Plugin *plugin = (Plugin *)jcr->plugin;
903 Dmsg0(dbglvl, "===== plugin_bclose\n");
904 if (!plugin || !jcr->plugin_ctx) {
907 io.pkt_size = sizeof(io);
908 io.pkt_end = sizeof(io);
914 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
915 bfd->berrno = io.io_errno;
917 errno = b_errno_win32;
920 bfd->lerror = io.lerror;
922 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
926 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
929 Plugin *plugin = (Plugin *)jcr->plugin;
932 Dmsg0(dbglvl, "plugin_bread\n");
933 if (!plugin || !jcr->plugin_ctx) {
936 io.pkt_size = sizeof(io);
937 io.pkt_end = sizeof(io);
940 io.buf = (char *)buf;
944 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
945 bfd->offset = io.offset;
946 bfd->berrno = io.io_errno;
948 errno = b_errno_win32;
951 bfd->lerror = io.lerror;
953 return (ssize_t)io.status;
956 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
959 Plugin *plugin = (Plugin *)jcr->plugin;
962 Dmsg0(dbglvl, "plugin_bwrite\n");
963 if (!plugin || !jcr->plugin_ctx) {
966 io.pkt_size = sizeof(io);
967 io.pkt_end = sizeof(io);
970 io.buf = (char *)buf;
973 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
974 bfd->berrno = io.io_errno;
976 errno = b_errno_win32;
979 bfd->lerror = io.lerror;
981 return (ssize_t)io.status;
984 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
987 Plugin *plugin = (Plugin *)jcr->plugin;
990 Dmsg0(dbglvl, "plugin_bseek\n");
991 if (!plugin || !jcr->plugin_ctx) {
994 io.pkt_size = sizeof(io);
995 io.pkt_end = sizeof(io);
1001 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1002 bfd->berrno = io.io_errno;
1004 errno = b_errno_win32;
1006 errno = io.io_errno;
1007 bfd->lerror = io.lerror;
1009 return (boffset_t)io.offset;
1012 /* ==============================================================
1014 * Callbacks from the plugin
1016 * ==============================================================
1018 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1025 switch (var) { /* General variables, no need of ctx */
1027 *((char **)value) = my_name;
1029 case bVarWorkingDir:
1030 *(void **)value = me->working_directory;
1033 *(char **)value = exepath;
1036 *(char **)value = version;
1039 *(char **)value = dist_name;
1042 *((int *)value) = beef;
1048 if (!ctx) { /* Other variables need context */
1052 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1059 *((int *)value) = jcr->JobId;
1060 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1063 *((int *)value) = jcr->getJobLevel();
1064 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1067 *((int *)value) = jcr->getJobType();
1068 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1071 *((char **)value) = jcr->client_name;
1072 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1075 *((char **)value) = jcr->Job;
1076 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1079 *((int *)value) = jcr->JobStatus;
1080 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1083 *((int *)value) = (int)jcr->mtime;
1084 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1087 *((int *)value) = (int)jcr->accurate;
1088 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1091 break; /* a write only variable, ignore read request */
1095 *(void **)value = g_pVSSClient->GetVssObject();
1100 case bVarVssDllHandle:
1103 *(void **)value = g_pVSSClient->GetVssDllHandle();
1109 *(char **)value = jcr->where;
1111 case bVarRegexWhere:
1112 *(char **)value = jcr->RegexWhere;
1115 case bVarFDName: /* get warning with g++ if we missed one */
1116 case bVarWorkingDir:
1126 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1129 if (!value || !ctx) {
1132 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1133 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1137 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1140 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1150 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1159 va_start(args, ctx);
1160 while ((event = va_arg(args, uint32_t))) {
1161 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1167 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1168 int type, utime_t mtime, const char *fmt, ...)
1175 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1180 va_start(arg_ptr, fmt);
1181 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1183 Jmsg(jcr, type, mtime, "%s", buf);
1187 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1188 int level, const char *fmt, ...)
1193 va_start(arg_ptr, fmt);
1194 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1196 d_msg(file, line, level, "%s", buf);
1200 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1204 return sm_malloc(file, line, size);
1206 return malloc(size);
1210 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1213 sm_free(file, line, mem);
1219 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1224 bctx = (bacula_ctx *)ctx->bContext;
1236 * Let the plugin define files/directories to be excluded
1237 * from the main backup.
1239 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1243 if (!is_ctx_good(ctx, jcr, bctx)) {
1249 if (!bctx->exclude) {
1250 bctx->exclude = new_exclude(jcr);
1251 new_options(jcr, bctx->exclude);
1253 set_incexe(jcr, bctx->exclude);
1254 add_file_to_fileset(jcr, file, true);
1255 Dmsg1(100, "Add exclude file=%s\n", file);
1260 * Let the plugin define files/directories to be excluded
1261 * from the main backup.
1263 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1267 if (!is_ctx_good(ctx, jcr, bctx)) {
1273 if (!bctx->include) {
1274 bctx->include = new_preinclude(jcr);
1275 new_options(jcr, bctx->include);
1277 set_incexe(jcr, bctx->include);
1278 add_file_to_fileset(jcr, file, true);
1279 Dmsg1(100, "Add include file=%s\n", file);
1283 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1287 if (!is_ctx_good(ctx, jcr, bctx)) {
1293 add_options_to_fileset(jcr, opts);
1294 Dmsg1(1000, "Add options=%s\n", opts);
1298 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1302 if (!is_ctx_good(ctx, jcr, bctx)) {
1308 add_regex_to_fileset(jcr, item, type);
1309 Dmsg1(100, "Add regex=%s\n", item);
1313 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1317 if (!is_ctx_good(ctx, jcr, bctx)) {
1323 add_wild_to_fileset(jcr, item, type);
1324 Dmsg1(100, "Add wild=%s\n", item);
1328 static bRC baculaNewOptions(bpContext *ctx)
1332 if (!is_ctx_good(ctx, jcr, bctx)) {
1335 (void)new_options(jcr, NULL);
1339 static bRC baculaNewInclude(bpContext *ctx)
1343 if (!is_ctx_good(ctx, jcr, bctx)) {
1346 (void)new_include(jcr);
1352 * Check if a file have to be backuped using Accurate code
1354 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1359 bRC ret = bRC_Error;
1361 if (!is_ctx_good(ctx, jcr, bctx)) {
1370 * Copy fname and link because save_file() zaps them. This
1371 * avoids zaping the plugin's strings.
1373 ff_pkt->type = sp->type;
1375 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1379 ff_pkt->fname = sp->fname;
1380 ff_pkt->link = sp->link;
1381 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1383 if (check_changes(jcr, ff_pkt)) {
1389 /* check_changes() can update delta sequence number, return it to the
1392 sp->delta_seq = ff_pkt->delta_seq;
1395 Dmsg1(100, "checkChanges=%i\n", ret);
1402 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1403 int (*plugin_bclose)(JCR *jcr) = NULL;
1404 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1405 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1406 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1408 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1413 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1418 int main(int argc, char *argv[])
1420 char plugin_dir[1000];
1425 strcpy(my_name, "test-fd");
1427 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1428 load_fd_plugins(plugin_dir);
1436 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1437 generate_plugin_event(jcr1, bEventJobEnd);
1438 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1440 generate_plugin_event(jcr2, bEventJobEnd);
1445 Dmsg0(dbglvl, "bacula: OK ...\n");
1446 close_memory_pool();
1447 sm_dump(false); /* unit test */
1451 #endif /* TEST_PROGRAM */