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)->file_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->file_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;
408 if (sp.flags & FO_OFFSETS) {
409 ff_pkt->flags |= FO_OFFSETS;
411 if (sp.flags & FO_PORTABLE_DATA) {
412 ff_pkt->flags |= FO_PORTABLE_DATA;
414 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
417 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
418 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
420 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
422 /* Call Bacula core code to backup the plugin's file */
423 save_file(jcr, ff_pkt, true);
424 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
425 if (rc == bRC_More || rc == bRC_OK) {
426 accurate_mark_file_as_seen(jcr, fname.c_str());
428 if (rc == bRC_More) {
432 } /* end while loop */
434 } /* end loop over all plugins */
435 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
438 jcr->cmd_plugin = false;
440 jcr->plugin_ctx = NULL;
445 * Send plugin name start/end record to SD
447 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
450 int index = jcr->JobFiles;
451 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
454 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
457 if (jcr->is_job_canceled()) {
462 index++; /* JobFiles not incremented yet */
464 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
465 /* Send stream header */
466 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
467 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
471 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
474 /* Send data -- not much */
475 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
477 /* Send end of data */
478 stat = sd->fsend("0 0");
481 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
485 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
486 sd->signal(BNET_EOD); /* indicate end of plugin name data */
491 * Plugin name stream found during restore. The record passed in
492 * argument name was generated in send_plugin_name() above.
494 * Returns: true if start of stream
495 * false if end of steam
497 bool plugin_name_stream(JCR *jcr, char *name)
501 bool start, portable;
505 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
507 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
508 skip_nonspaces(&p); /* skip over jcr->JobFiles */
512 /* Start of plugin data */
513 skip_nonspaces(&p); /* skip start/end flag */
515 portable = *p == '1';
516 skip_nonspaces(&p); /* skip portable flag */
521 * End of plugin data, notify plugin, then clear flags
523 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
524 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
525 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
526 jcr->plugin->restoreFileStarted = false;
528 jcr->plugin_ctx = NULL;
532 if (!plugin_ctx_list) {
537 * After this point, we are dealing with a restore start
539 if (!get_plugin_name(jcr, cmd, &len)) {
544 * Search for correct plugin as specified on the command
546 foreach_alist(plugin, plugin_list) {
548 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
549 if (!for_this_plug(plugin, cmd, len)) {
553 jcr->plugin_ctx = &plugin_ctx_list[i];
554 jcr->plugin = plugin;
555 if (is_plugin_disabled(jcr)) {
558 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
559 event.eventType = bEventRestoreCommand;
560 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
561 &event, cmd) != bRC_OK) {
564 /* ***FIXME**** check error code */
565 if (plugin->restoreFileStarted) {
566 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
568 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
569 plugin->restoreFileStarted = true;
572 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
579 * Tell the plugin to create the file. Return values are
580 * This is called only during Restore
583 * CF_SKIP -- skip processing this file
584 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
585 * CF_CREATED -- created, but no content to extract (typically directories)
588 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
590 bpContext *plugin_ctx = jcr->plugin_ctx;
591 Plugin *plugin = jcr->plugin;
592 struct restore_pkt rp;
596 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
600 rp.pkt_size = sizeof(rp);
601 rp.pkt_end = sizeof(rp);
602 rp.stream = attr->stream;
603 rp.data_stream = attr->data_stream;
604 rp.type = attr->type;
605 rp.file_index = attr->file_index;
606 rp.LinkFI = attr->LinkFI;
608 rp.statp = attr->statp; /* structure assignment */
609 rp.attrEx = attr->attrEx;
610 rp.ofname = attr->ofname;
611 rp.olname = attr->olname;
612 rp.where = jcr->where;
613 rp.RegexWhere = jcr->RegexWhere;
614 rp.replace = jcr->replace;
615 rp.create_status = CF_ERROR;
616 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
617 rp.stream, rp.type, rp.LinkFI, rp.ofname);
619 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
621 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
623 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
627 if (rp.create_status == CF_SKIP) {
630 if (rp.create_status == CF_ERROR) {
631 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
635 /* Created link or directory? */
636 if (rp.create_status == CF_CREATED) {
637 return rp.create_status; /* yes, no need to bopen */
640 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
641 Dmsg0(dbglvl, "call bopen\n");
642 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
643 Dmsg1(50, "bopen status=%d\n", stat);
646 be.set_errno(bfd->berrno);
647 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
648 attr->ofname, be.bstrerror());
649 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
653 if (!is_bopen(bfd)) {
654 Dmsg0(000, "===== BFD is not open!!!!\n");
660 * Reset the file attributes after all file I/O is done -- this allows
661 * the previous access time/dates to be set properly, and it also allows
662 * us to properly set directory permissions.
663 * Not currently Implemented.
665 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
667 Dmsg0(dbglvl, "plugin_set_attributes\n");
671 pm_strcpy(attr->ofname, "*none*");
676 * Print to file the plugin info.
678 void dump_fd_plugin(Plugin *plugin, FILE *fp)
683 pInfo *info = (pInfo *)plugin->pinfo;
684 fprintf(fp, "\tversion=%d\n", info->version);
685 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
686 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
687 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
688 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
689 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
690 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
694 * This entry point is called internally by Bacula to ensure
695 * that the plugin IO calls come into this code.
697 void load_fd_plugins(const char *plugin_dir)
702 Dmsg0(dbglvl, "plugin dir is NULL\n");
706 plugin_list = New(alist(10, not_owned_by_alist));
707 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
708 is_plugin_compatible)) {
709 /* Either none found, or some error */
710 if (plugin_list->size() == 0) {
713 Dmsg0(dbglvl, "No plugins loaded\n");
718 /* Plug entry points called from findlib */
719 plugin_bopen = my_plugin_bopen;
720 plugin_bclose = my_plugin_bclose;
721 plugin_bread = my_plugin_bread;
722 plugin_bwrite = my_plugin_bwrite;
723 plugin_blseek = my_plugin_blseek;
726 * Verify that the plugin is acceptable, and print information
729 foreach_alist(plugin, plugin_list) {
730 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
731 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
734 dbg_plugin_add_hook(dump_fd_plugin);
738 * Check if a plugin is compatible. Called by the load_plugin function
739 * to allow us to verify the plugin.
741 static bool is_plugin_compatible(Plugin *plugin)
743 pInfo *info = (pInfo *)plugin->pinfo;
744 Dmsg0(50, "is_plugin_compatible called\n");
745 if (debug_level >= 50) {
746 dump_fd_plugin(plugin, stdin);
748 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
749 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
750 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
751 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
752 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
756 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
757 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
758 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
759 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
760 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
763 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
764 strcmp(info->plugin_license, "AGPLv3") != 0) {
765 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
766 plugin->file, info->plugin_license);
767 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
768 plugin->file, info->plugin_license);
777 * Create a new instance of each plugin for this Job
778 * Note, plugin_list can exist but jcr->plugin_ctx_list can
779 * be NULL if no plugins were loaded.
781 void new_plugins(JCR *jcr)
787 Dmsg0(dbglvl, "plugin list is NULL\n");
790 if (jcr->is_job_canceled() || jcr->JobId == 0) {
794 int num = plugin_list->size();
797 Dmsg0(dbglvl, "No plugins loaded\n");
801 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
803 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
804 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
805 foreach_alist(plugin, plugin_list) {
806 /* Start a new instance of each plugin */
807 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
808 memset(b_ctx, 0, sizeof(bacula_ctx));
810 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
811 plugin_ctx_list[i].pContext = NULL;
812 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
813 b_ctx->disabled = true;
819 * Free the plugin instances for this Job
821 void free_plugins(JCR *jcr)
826 if (!plugin_list || !jcr->plugin_ctx_list) {
827 return; /* no plugins, nothing to do */
830 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
831 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
832 foreach_alist(plugin, plugin_list) {
833 /* Free the plugin instance */
834 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
835 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
837 free(plugin_ctx_list);
838 jcr->plugin_ctx_list = NULL;
841 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
844 Plugin *plugin = (Plugin *)jcr->plugin;
847 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
848 if (!plugin || !jcr->plugin_ctx) {
851 io.pkt_size = sizeof(io);
852 io.pkt_end = sizeof(io);
861 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
862 bfd->berrno = io.io_errno;
864 errno = b_errno_win32;
867 bfd->lerror = io.lerror;
869 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
873 static int my_plugin_bclose(BFILE *bfd)
876 Plugin *plugin = (Plugin *)jcr->plugin;
879 Dmsg0(dbglvl, "===== plugin_bclose\n");
880 if (!plugin || !jcr->plugin_ctx) {
883 io.pkt_size = sizeof(io);
884 io.pkt_end = sizeof(io);
890 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
891 bfd->berrno = io.io_errno;
893 errno = b_errno_win32;
896 bfd->lerror = io.lerror;
898 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
902 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
905 Plugin *plugin = (Plugin *)jcr->plugin;
908 Dmsg0(dbglvl, "plugin_bread\n");
909 if (!plugin || !jcr->plugin_ctx) {
912 io.pkt_size = sizeof(io);
913 io.pkt_end = sizeof(io);
916 io.buf = (char *)buf;
920 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
921 bfd->offset = io.offset;
922 bfd->berrno = io.io_errno;
924 errno = b_errno_win32;
927 bfd->lerror = io.lerror;
929 return (ssize_t)io.status;
932 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
935 Plugin *plugin = (Plugin *)jcr->plugin;
938 Dmsg0(dbglvl, "plugin_bwrite\n");
939 if (!plugin || !jcr->plugin_ctx) {
942 io.pkt_size = sizeof(io);
943 io.pkt_end = sizeof(io);
946 io.buf = (char *)buf;
949 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
950 bfd->berrno = io.io_errno;
952 errno = b_errno_win32;
955 bfd->lerror = io.lerror;
957 return (ssize_t)io.status;
960 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
963 Plugin *plugin = (Plugin *)jcr->plugin;
966 Dmsg0(dbglvl, "plugin_bseek\n");
967 if (!plugin || !jcr->plugin_ctx) {
970 io.pkt_size = sizeof(io);
971 io.pkt_end = sizeof(io);
977 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
978 bfd->berrno = io.io_errno;
980 errno = b_errno_win32;
983 bfd->lerror = io.lerror;
985 return (boffset_t)io.offset;
988 /* ==============================================================
990 * Callbacks from the plugin
992 * ==============================================================
994 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1001 switch (var) { /* General variables, no need of ctx */
1003 *((char **)value) = my_name;
1005 case bVarWorkingDir:
1006 *(void **)value = me->working_directory;
1009 *(char **)value = exepath;
1015 if (!ctx) { /* Other variables need context */
1019 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1026 *((int *)value) = jcr->JobId;
1027 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1030 *((int *)value) = jcr->getJobLevel();
1031 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1034 *((int *)value) = jcr->getJobType();
1035 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1038 *((char **)value) = jcr->client_name;
1039 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1042 *((char **)value) = jcr->Job;
1043 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1046 *((int *)value) = jcr->JobStatus;
1047 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1050 *((int *)value) = (int)jcr->mtime;
1051 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1054 *((int *)value) = (int)jcr->accurate;
1055 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1058 break; /* a write only variable, ignore read request */
1062 *(void **)value = g_pVSSClient->GetVssObject();
1067 case bVarVssDllHandle:
1070 *(void **)value = g_pVSSClient->GetVssDllHandle();
1076 *(char **)value = jcr->where;
1078 case bVarRegexWhere:
1079 *(char **)value = jcr->RegexWhere;
1082 case bVarFDName: /* get warning with g++ if we missed one */
1083 case bVarWorkingDir:
1090 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1093 if (!value || !ctx) {
1096 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1097 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1101 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1104 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1114 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1123 va_start(args, ctx);
1124 while ((event = va_arg(args, uint32_t))) {
1125 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1131 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1132 int type, utime_t mtime, const char *fmt, ...)
1139 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1144 va_start(arg_ptr, fmt);
1145 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1147 Jmsg(jcr, type, mtime, "%s", buf);
1151 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1152 int level, const char *fmt, ...)
1157 va_start(arg_ptr, fmt);
1158 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1160 d_msg(file, line, level, "%s", buf);
1164 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1168 return sm_malloc(file, line, size);
1170 return malloc(size);
1174 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1177 sm_free(file, line, mem);
1183 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1188 bctx = (bacula_ctx *)ctx->bContext;
1200 * Let the plugin define files/directories to be excluded
1201 * from the main backup.
1203 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1207 if (!is_ctx_good(ctx, jcr, bctx)) {
1213 if (!bctx->exclude) {
1214 bctx->exclude = new_exclude(jcr);
1215 new_options(jcr, bctx->exclude);
1217 set_incexe(jcr, bctx->exclude);
1218 add_file_to_fileset(jcr, file, true);
1219 Dmsg1(100, "Add exclude file=%s\n", file);
1224 * Let the plugin define files/directories to be excluded
1225 * from the main backup.
1227 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1231 if (!is_ctx_good(ctx, jcr, bctx)) {
1237 if (!bctx->include) {
1238 bctx->include = new_preinclude(jcr);
1239 new_options(jcr, bctx->include);
1241 set_incexe(jcr, bctx->include);
1242 add_file_to_fileset(jcr, file, true);
1243 Dmsg1(100, "Add include file=%s\n", file);
1247 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1251 if (!is_ctx_good(ctx, jcr, bctx)) {
1257 add_options_to_fileset(jcr, opts);
1258 Dmsg1(1000, "Add options=%s\n", opts);
1262 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1266 if (!is_ctx_good(ctx, jcr, bctx)) {
1272 add_regex_to_fileset(jcr, item, type);
1273 Dmsg1(100, "Add regex=%s\n", item);
1277 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1281 if (!is_ctx_good(ctx, jcr, bctx)) {
1287 add_wild_to_fileset(jcr, item, type);
1288 Dmsg1(100, "Add wild=%s\n", item);
1292 static bRC baculaNewOptions(bpContext *ctx)
1296 if (!is_ctx_good(ctx, jcr, bctx)) {
1299 (void)new_options(jcr, NULL);
1303 static bRC baculaNewInclude(bpContext *ctx)
1307 if (!is_ctx_good(ctx, jcr, bctx)) {
1310 (void)new_include(jcr);
1316 * Check if a file have to be backuped using Accurate code
1318 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1323 bRC ret = bRC_Error;
1325 if (!is_ctx_good(ctx, jcr, bctx)) {
1334 * Copy fname and link because save_file() zaps them. This
1335 * avoids zaping the plugin's strings.
1337 ff_pkt->type = sp->type;
1339 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1343 ff_pkt->fname = sp->fname;
1344 ff_pkt->link = sp->link;
1345 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1347 if (check_changes(jcr, ff_pkt)) {
1353 /* check_changes() can update delta sequence number, return it to the
1356 sp->delta_seq = ff_pkt->delta_seq;
1359 Dmsg1(100, "checkChanges=%i\n", ret);
1366 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1367 int (*plugin_bclose)(JCR *jcr) = NULL;
1368 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1369 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1370 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1372 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1377 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1382 int main(int argc, char *argv[])
1384 char plugin_dir[1000];
1389 strcpy(my_name, "test-fd");
1391 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1392 load_fd_plugins(plugin_dir);
1400 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1401 generate_plugin_event(jcr1, bEventJobEnd);
1402 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1404 generate_plugin_event(jcr2, bEventJobEnd);
1409 Dmsg0(dbglvl, "bacula: OK ...\n");
1410 close_memory_pool();
1411 sm_dump(false); /* unit test */
1415 #endif /* TEST_PROGRAM */