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);
200 * Pass event to every plugin (except if name is set). If name
201 * is set, we pass it only to the plugin with that name.
203 foreach_alist(plugin, plugin_list) {
204 if (name && !for_this_plug(plugin, name, len)) {
208 plugin_ctx = &plugin_ctx_list[i++];
209 if (is_plugin_disabled(plugin_ctx)) {
212 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
221 * Check if file was seen for accurate
223 bool plugin_check_file(JCR *jcr, char *fname)
229 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
230 return false; /* Return if no plugins loaded */
233 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
235 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
237 /* Pass event to every plugin */
238 foreach_alist(plugin, plugin_list) {
239 jcr->plugin_ctx = &plugin_ctx_list[i++];
240 jcr->plugin = plugin;
241 if (is_plugin_disabled(jcr)) {
244 if (plug_func(plugin)->checkFile == NULL) {
247 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
248 if (rc == bRC_Seen) {
254 jcr->plugin_ctx = NULL;
255 return rc == bRC_Seen;
258 /* Get the first part of the the plugin command
259 * systemstate:/@SYSTEMSTATE/
261 * => can use for_this_plug(plug, cmd, ret);
263 * The plugin command can contain only the plugin name
267 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
271 if (!cmd || (*cmd == '\0')) {
274 /* Handle plugin command here backup */
275 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
276 if ((p = strchr(cmd, ':')) == NULL) {
277 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
280 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
283 } else { /* plugin:argument */
294 * Sequence of calls for a backup:
295 * 1. plugin_save() here is called with ff_pkt
296 * 2. we find the plugin requested on the command string
297 * 3. we generate a bEventBackupCommand event to the specified plugin
298 * and pass it the command string.
299 * 4. we make a startPluginBackup call to the plugin, which gives
300 * us the data we need in save_pkt
301 * 5. we call Bacula's save_file() subroutine to save the specified
302 * file. The plugin will be called at pluginIO() to supply the
305 * Sequence of calls for restore:
306 * See subroutine plugin_name_stream() below.
308 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
313 char *cmd = ff_pkt->top_fname;
316 POOL_MEM fname(PM_FNAME);
317 POOL_MEM link(PM_FNAME);
319 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
320 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
321 return 1; /* Return if no plugins loaded */
324 jcr->cmd_plugin = true;
325 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
326 event.eventType = bEventBackupCommand;
328 if (!get_plugin_name(jcr, cmd, &len)) {
332 /* Note, we stop the loop on the first plugin that matches the name */
333 foreach_alist(plugin, plugin_list) {
334 Dmsg4(0, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
335 if (!for_this_plug(plugin, cmd, len)) {
340 * We put the current plugin pointer, and the plugin context
341 * into the jcr, because during save_file(), the plugin
342 * will be called many times and these values are needed.
344 jcr->plugin_ctx = &plugin_ctx_list[i];
345 jcr->plugin = plugin;
346 if (is_plugin_disabled(jcr)) {
350 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
351 /* Send the backup command to the right plugin*/
352 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
355 /* Loop getting filenames to backup then saving them */
356 while (!jcr->is_job_canceled()) {
357 memset(&sp, 0, sizeof(sp));
358 sp.pkt_size = sizeof(sp);
359 sp.pkt_end = sizeof(sp);
363 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
365 /* Get the file save parameters. I.e. the stat pkt ... */
366 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
370 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
374 jcr->plugin_sp = &sp;
377 * Copy fname and link because save_file() zaps them. This
378 * avoids zaping the plugin's strings.
380 ff_pkt->type = sp.type;
381 if (sp.type == FT_RESTORE_FIRST) {
382 if (!sp.object_name) {
383 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
387 ff_pkt->fname = cmd; /* full plugin string */
388 ff_pkt->object_name = sp.object_name;
389 ff_pkt->object_index = sp.index; /* restore object index */
390 ff_pkt->object_compression = 0; /* no compression for now */
391 ff_pkt->object = sp.object;
392 ff_pkt->object_len = sp.object_len;
395 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
399 pm_strcpy(fname, sp.fname);
400 pm_strcpy(link, sp.link);
401 ff_pkt->fname = fname.c_str();
402 ff_pkt->link = link.c_str();
403 ff_pkt->delta_seq = sp.delta_seq;
404 if (sp.flags & FO_DELTA) {
405 ff_pkt->flags |= FO_DELTA;
406 ff_pkt->delta_seq++; /* make new delta sequence number */
408 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
409 ff_pkt->delta_seq = 0;
411 if (sp.flags & FO_OFFSETS) {
412 ff_pkt->flags |= FO_OFFSETS;
414 if (sp.flags & FO_PORTABLE_DATA) {
415 ff_pkt->flags |= FO_PORTABLE_DATA;
417 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
420 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
421 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
423 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
425 /* Call Bacula core code to backup the plugin's file */
426 save_file(jcr, ff_pkt, true);
427 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
428 if (rc == bRC_More || rc == bRC_OK) {
429 accurate_mark_file_as_seen(jcr, fname.c_str());
431 if (rc == bRC_More) {
435 } /* end while loop */
437 } /* end loop over all plugins */
438 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
441 jcr->cmd_plugin = false;
443 jcr->plugin_ctx = NULL;
448 * Send plugin name start/end record to SD
450 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
453 int index = jcr->JobFiles;
454 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
457 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
460 if (jcr->is_job_canceled()) {
465 index++; /* JobFiles not incremented yet */
467 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
468 /* Send stream header */
469 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
470 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
474 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
477 /* Send data -- not much */
478 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
480 /* Send end of data */
481 stat = sd->fsend("0 0");
484 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
488 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
489 sd->signal(BNET_EOD); /* indicate end of plugin name data */
494 * Plugin name stream found during restore. The record passed in
495 * argument name was generated in send_plugin_name() above.
497 * Returns: true if start of stream
498 * false if end of steam
500 bool plugin_name_stream(JCR *jcr, char *name)
504 bool start, portable;
508 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
510 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
511 skip_nonspaces(&p); /* skip over jcr->JobFiles */
515 /* Start of plugin data */
516 skip_nonspaces(&p); /* skip start/end flag */
518 portable = *p == '1';
519 skip_nonspaces(&p); /* skip portable flag */
524 * End of plugin data, notify plugin, then clear flags
526 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
527 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
528 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
529 jcr->plugin->restoreFileStarted = false;
531 jcr->plugin_ctx = NULL;
535 if (!plugin_ctx_list) {
540 * After this point, we are dealing with a restore start
542 if (!get_plugin_name(jcr, cmd, &len)) {
547 * Search for correct plugin as specified on the command
549 foreach_alist(plugin, plugin_list) {
551 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
552 if (!for_this_plug(plugin, cmd, len)) {
556 jcr->plugin_ctx = &plugin_ctx_list[i];
557 jcr->plugin = plugin;
558 if (is_plugin_disabled(jcr)) {
561 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
562 event.eventType = bEventRestoreCommand;
563 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
564 &event, cmd) != bRC_OK) {
567 /* ***FIXME**** check error code */
568 if (plugin->restoreFileStarted) {
569 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
571 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
572 plugin->restoreFileStarted = true;
575 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
582 * Tell the plugin to create the file. Return values are
583 * This is called only during Restore
586 * CF_SKIP -- skip processing this file
587 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
588 * CF_CREATED -- created, but no content to extract (typically directories)
591 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
593 bpContext *plugin_ctx = jcr->plugin_ctx;
594 Plugin *plugin = jcr->plugin;
595 struct restore_pkt rp;
599 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
603 rp.pkt_size = sizeof(rp);
604 rp.pkt_end = sizeof(rp);
605 rp.delta_seq = attr->delta_seq;
606 rp.stream = attr->stream;
607 rp.data_stream = attr->data_stream;
608 rp.type = attr->type;
609 rp.file_index = attr->file_index;
610 rp.LinkFI = attr->LinkFI;
612 rp.statp = attr->statp; /* structure assignment */
613 rp.attrEx = attr->attrEx;
614 rp.ofname = attr->ofname;
615 rp.olname = attr->olname;
616 rp.where = jcr->where;
617 rp.RegexWhere = jcr->RegexWhere;
618 rp.replace = jcr->replace;
619 rp.create_status = CF_ERROR;
620 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
621 rp.stream, rp.type, rp.LinkFI, rp.ofname);
623 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
625 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
627 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
631 if (rp.create_status == CF_SKIP) {
634 if (rp.create_status == CF_ERROR) {
635 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
639 /* Created link or directory? */
640 if (rp.create_status == CF_CREATED) {
641 return rp.create_status; /* yes, no need to bopen */
644 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
645 Dmsg0(dbglvl, "call bopen\n");
646 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
647 Dmsg1(50, "bopen status=%d\n", stat);
650 be.set_errno(bfd->berrno);
651 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
652 attr->ofname, be.bstrerror());
653 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
657 if (!is_bopen(bfd)) {
658 Dmsg0(000, "===== BFD is not open!!!!\n");
664 * Reset the file attributes after all file I/O is done -- this allows
665 * the previous access time/dates to be set properly, and it also allows
666 * us to properly set directory permissions.
667 * Not currently Implemented.
669 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
671 Dmsg0(dbglvl, "plugin_set_attributes\n");
675 pm_strcpy(attr->ofname, "*none*");
680 * Print to file the plugin info.
682 void dump_fd_plugin(Plugin *plugin, FILE *fp)
687 pInfo *info = (pInfo *)plugin->pinfo;
688 fprintf(fp, "\tversion=%d\n", info->version);
689 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
690 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
691 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
692 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
693 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
694 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
698 * This entry point is called internally by Bacula to ensure
699 * that the plugin IO calls come into this code.
701 void load_fd_plugins(const char *plugin_dir)
706 Dmsg0(dbglvl, "plugin dir is NULL\n");
710 plugin_list = New(alist(10, not_owned_by_alist));
711 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
712 is_plugin_compatible)) {
713 /* Either none found, or some error */
714 if (plugin_list->size() == 0) {
717 Dmsg0(dbglvl, "No plugins loaded\n");
722 /* Plug entry points called from findlib */
723 plugin_bopen = my_plugin_bopen;
724 plugin_bclose = my_plugin_bclose;
725 plugin_bread = my_plugin_bread;
726 plugin_bwrite = my_plugin_bwrite;
727 plugin_blseek = my_plugin_blseek;
730 * Verify that the plugin is acceptable, and print information
733 foreach_alist(plugin, plugin_list) {
734 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
735 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
738 dbg_plugin_add_hook(dump_fd_plugin);
742 * Check if a plugin is compatible. Called by the load_plugin function
743 * to allow us to verify the plugin.
745 static bool is_plugin_compatible(Plugin *plugin)
747 pInfo *info = (pInfo *)plugin->pinfo;
748 Dmsg0(50, "is_plugin_compatible called\n");
749 if (debug_level >= 50) {
750 dump_fd_plugin(plugin, stdin);
752 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
753 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
754 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
755 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
756 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
760 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
761 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
762 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
763 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
764 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
767 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
768 strcmp(info->plugin_license, "AGPLv3") != 0) {
769 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
770 plugin->file, info->plugin_license);
771 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
772 plugin->file, info->plugin_license);
781 * Create a new instance of each plugin for this Job
782 * Note, plugin_list can exist but jcr->plugin_ctx_list can
783 * be NULL if no plugins were loaded.
785 void new_plugins(JCR *jcr)
791 Dmsg0(dbglvl, "plugin list is NULL\n");
794 if (jcr->is_job_canceled() || jcr->JobId == 0) {
798 int num = plugin_list->size();
801 Dmsg0(dbglvl, "No plugins loaded\n");
805 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
807 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
808 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
809 foreach_alist(plugin, plugin_list) {
810 /* Start a new instance of each plugin */
811 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
812 memset(b_ctx, 0, sizeof(bacula_ctx));
814 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
815 plugin_ctx_list[i].pContext = NULL;
816 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
817 b_ctx->disabled = true;
823 * Free the plugin instances for this Job
825 void free_plugins(JCR *jcr)
830 if (!plugin_list || !jcr->plugin_ctx_list) {
831 return; /* no plugins, nothing to do */
834 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
835 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
836 foreach_alist(plugin, plugin_list) {
837 /* Free the plugin instance */
838 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
839 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
841 free(plugin_ctx_list);
842 jcr->plugin_ctx_list = NULL;
845 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
848 Plugin *plugin = (Plugin *)jcr->plugin;
851 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
852 if (!plugin || !jcr->plugin_ctx) {
855 io.pkt_size = sizeof(io);
856 io.pkt_end = sizeof(io);
865 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
866 bfd->berrno = io.io_errno;
868 errno = b_errno_win32;
871 bfd->lerror = io.lerror;
873 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
877 static int my_plugin_bclose(BFILE *bfd)
880 Plugin *plugin = (Plugin *)jcr->plugin;
883 Dmsg0(dbglvl, "===== plugin_bclose\n");
884 if (!plugin || !jcr->plugin_ctx) {
887 io.pkt_size = sizeof(io);
888 io.pkt_end = sizeof(io);
894 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
895 bfd->berrno = io.io_errno;
897 errno = b_errno_win32;
900 bfd->lerror = io.lerror;
902 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
906 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
909 Plugin *plugin = (Plugin *)jcr->plugin;
912 Dmsg0(dbglvl, "plugin_bread\n");
913 if (!plugin || !jcr->plugin_ctx) {
916 io.pkt_size = sizeof(io);
917 io.pkt_end = sizeof(io);
920 io.buf = (char *)buf;
924 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
925 bfd->offset = io.offset;
926 bfd->berrno = io.io_errno;
928 errno = b_errno_win32;
931 bfd->lerror = io.lerror;
933 return (ssize_t)io.status;
936 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
939 Plugin *plugin = (Plugin *)jcr->plugin;
942 Dmsg0(dbglvl, "plugin_bwrite\n");
943 if (!plugin || !jcr->plugin_ctx) {
946 io.pkt_size = sizeof(io);
947 io.pkt_end = sizeof(io);
950 io.buf = (char *)buf;
953 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
954 bfd->berrno = io.io_errno;
956 errno = b_errno_win32;
959 bfd->lerror = io.lerror;
961 return (ssize_t)io.status;
964 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
967 Plugin *plugin = (Plugin *)jcr->plugin;
970 Dmsg0(dbglvl, "plugin_bseek\n");
971 if (!plugin || !jcr->plugin_ctx) {
974 io.pkt_size = sizeof(io);
975 io.pkt_end = sizeof(io);
981 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
982 bfd->berrno = io.io_errno;
984 errno = b_errno_win32;
987 bfd->lerror = io.lerror;
989 return (boffset_t)io.offset;
992 /* ==============================================================
994 * Callbacks from the plugin
996 * ==============================================================
998 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1005 switch (var) { /* General variables, no need of ctx */
1007 *((char **)value) = my_name;
1009 case bVarWorkingDir:
1010 *(void **)value = me->working_directory;
1013 *(char **)value = exepath;
1019 if (!ctx) { /* Other variables need context */
1023 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1030 *((int *)value) = jcr->JobId;
1031 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1034 *((int *)value) = jcr->getJobLevel();
1035 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1038 *((int *)value) = jcr->getJobType();
1039 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1042 *((char **)value) = jcr->client_name;
1043 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1046 *((char **)value) = jcr->Job;
1047 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1050 *((int *)value) = jcr->JobStatus;
1051 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1054 *((int *)value) = (int)jcr->mtime;
1055 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1058 *((int *)value) = (int)jcr->accurate;
1059 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1062 break; /* a write only variable, ignore read request */
1066 *(void **)value = g_pVSSClient->GetVssObject();
1071 case bVarVssDllHandle:
1074 *(void **)value = g_pVSSClient->GetVssDllHandle();
1080 *(char **)value = jcr->where;
1082 case bVarRegexWhere:
1083 *(char **)value = jcr->RegexWhere;
1086 case bVarFDName: /* get warning with g++ if we missed one */
1087 case bVarWorkingDir:
1094 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1097 if (!value || !ctx) {
1100 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1101 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1105 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1108 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1118 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1127 va_start(args, ctx);
1128 while ((event = va_arg(args, uint32_t))) {
1129 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1135 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1136 int type, utime_t mtime, const char *fmt, ...)
1143 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1148 va_start(arg_ptr, fmt);
1149 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1151 Jmsg(jcr, type, mtime, "%s", buf);
1155 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1156 int level, const char *fmt, ...)
1161 va_start(arg_ptr, fmt);
1162 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1164 d_msg(file, line, level, "%s", buf);
1168 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1172 return sm_malloc(file, line, size);
1174 return malloc(size);
1178 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1181 sm_free(file, line, mem);
1187 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1192 bctx = (bacula_ctx *)ctx->bContext;
1204 * Let the plugin define files/directories to be excluded
1205 * from the main backup.
1207 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1211 if (!is_ctx_good(ctx, jcr, bctx)) {
1217 if (!bctx->exclude) {
1218 bctx->exclude = new_exclude(jcr);
1219 new_options(jcr, bctx->exclude);
1221 set_incexe(jcr, bctx->exclude);
1222 add_file_to_fileset(jcr, file, true);
1223 Dmsg1(100, "Add exclude file=%s\n", file);
1228 * Let the plugin define files/directories to be excluded
1229 * from the main backup.
1231 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1235 if (!is_ctx_good(ctx, jcr, bctx)) {
1241 if (!bctx->include) {
1242 bctx->include = new_preinclude(jcr);
1243 new_options(jcr, bctx->include);
1245 set_incexe(jcr, bctx->include);
1246 add_file_to_fileset(jcr, file, true);
1247 Dmsg1(100, "Add include file=%s\n", file);
1251 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1255 if (!is_ctx_good(ctx, jcr, bctx)) {
1261 add_options_to_fileset(jcr, opts);
1262 Dmsg1(1000, "Add options=%s\n", opts);
1266 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1270 if (!is_ctx_good(ctx, jcr, bctx)) {
1276 add_regex_to_fileset(jcr, item, type);
1277 Dmsg1(100, "Add regex=%s\n", item);
1281 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1285 if (!is_ctx_good(ctx, jcr, bctx)) {
1291 add_wild_to_fileset(jcr, item, type);
1292 Dmsg1(100, "Add wild=%s\n", item);
1296 static bRC baculaNewOptions(bpContext *ctx)
1300 if (!is_ctx_good(ctx, jcr, bctx)) {
1303 (void)new_options(jcr, NULL);
1307 static bRC baculaNewInclude(bpContext *ctx)
1311 if (!is_ctx_good(ctx, jcr, bctx)) {
1314 (void)new_include(jcr);
1320 * Check if a file have to be backuped using Accurate code
1322 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1327 bRC ret = bRC_Error;
1329 if (!is_ctx_good(ctx, jcr, bctx)) {
1338 * Copy fname and link because save_file() zaps them. This
1339 * avoids zaping the plugin's strings.
1341 ff_pkt->type = sp->type;
1343 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1347 ff_pkt->fname = sp->fname;
1348 ff_pkt->link = sp->link;
1349 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1351 if (check_changes(jcr, ff_pkt)) {
1357 /* check_changes() can update delta sequence number, return it to the
1360 sp->delta_seq = ff_pkt->delta_seq;
1363 Dmsg1(100, "checkChanges=%i\n", ret);
1370 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1371 int (*plugin_bclose)(JCR *jcr) = NULL;
1372 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1373 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1374 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1376 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1381 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1386 int main(int argc, char *argv[])
1388 char plugin_dir[1000];
1393 strcpy(my_name, "test-fd");
1395 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1396 load_fd_plugins(plugin_dir);
1404 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1405 generate_plugin_event(jcr1, bEventJobEnd);
1406 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1408 generate_plugin_event(jcr2, bEventJobEnd);
1413 Dmsg0(dbglvl, "bacula: OK ...\n");
1414 close_memory_pool();
1415 sm_dump(false); /* unit test */
1419 #endif /* TEST_PROGRAM */