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
39 const int dbglvl = 150;
41 const char *plugin_type = "-fd.dll";
43 const char *plugin_type = "-fd.so";
46 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
47 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
49 /* Function pointers to be set here */
50 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
51 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
52 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
53 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
54 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
57 /* Forward referenced functions */
58 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
59 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
60 static bRC baculaRegisterEvents(bpContext *ctx, ...);
61 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
62 int type, utime_t mtime, const char *fmt, ...);
63 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
64 int level, const char *fmt, ...);
65 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
67 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
68 static bRC baculaAddExclude(bpContext *ctx, const char *file);
69 static bRC baculaAddInclude(bpContext *ctx, const char *file);
70 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
71 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
72 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
73 static bRC baculaNewOptions(bpContext *ctx);
74 static bRC baculaNewInclude(bpContext *ctx);
75 static bool is_plugin_compatible(Plugin *plugin);
76 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
77 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
80 * These will be plugged into the global pointer structure for
83 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
84 static int my_plugin_bclose(BFILE *bfd);
85 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
86 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
87 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
91 static bInfo binfo = {
93 FD_PLUGIN_INTERFACE_VERSION
96 /* Bacula entry points */
97 static bFuncs bfuncs = {
99 FD_PLUGIN_INTERFACE_VERSION,
100 baculaRegisterEvents,
118 * Bacula private context
121 JCR *jcr; /* jcr for plugin */
122 bRC rc; /* last return code */
123 bool disabled; /* set if plugin disabled */
124 findINCEXE *exclude; /* pointer to exclude files */
125 findINCEXE *include; /* pointer to include/exclude files */
128 static bool is_plugin_disabled(bpContext *plugin_ctx)
134 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
135 return b_ctx->disabled;
138 static bool is_plugin_disabled(JCR *jcr)
140 return is_plugin_disabled(jcr->plugin_ctx);
144 * Create a plugin event
145 * When receiving bEventCancelCommand, this function is called by an other thread.
147 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
149 bpContext *plugin_ctx;
157 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
158 return; /* Return if no plugins loaded */
161 /* Some events are sent to only a particular plugin */
163 case bEventPluginCommand:
164 name = (char *)value;
165 if (!get_plugin_name(jcr, name, &len)) {
173 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
174 event.eventType = eventType;
176 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
178 /* Pass event to every plugin (except if name is set) */
179 foreach_alist(plugin, plugin_list) {
180 if (name && strncmp(plugin->file, name, len) != 0) {
184 plugin_ctx = &plugin_ctx_list[i++];
185 if (is_plugin_disabled(plugin_ctx)) {
188 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
197 * Check if file was seen for accurate
199 bool plugin_check_file(JCR *jcr, char *fname)
205 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
206 return false; /* Return if no plugins loaded */
209 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
211 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
213 /* Pass event to every plugin */
214 foreach_alist(plugin, plugin_list) {
215 jcr->plugin_ctx = &plugin_ctx_list[i++];
216 jcr->plugin = plugin;
217 if (is_plugin_disabled(jcr)) {
220 if (plug_func(plugin)->checkFile == NULL) {
223 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
224 if (rc == bRC_Seen) {
230 jcr->plugin_ctx = NULL;
231 return rc == bRC_Seen;
234 /* Get the first part of the the plugin command
235 * systemstate:/@SYSTEMSTATE/
237 * => can use strncmp(plugin_name, cmd, ret);
239 * The plugin command can contain only the plugin name
243 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
247 if (!cmd || (*cmd == '\0')) {
250 /* Handle plugin command here backup */
251 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
252 if ((p = strchr(cmd, ':')) == NULL) {
253 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
256 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
259 } else { /* plugin:argument */
270 * Sequence of calls for a backup:
271 * 1. plugin_save() here is called with ff_pkt
272 * 2. we find the plugin requested on the command string
273 * 3. we generate a bEventBackupCommand event to the specified plugin
274 * and pass it the command string.
275 * 4. we make a startPluginBackup call to the plugin, which gives
276 * us the data we need in save_pkt
277 * 5. we call Bacula's save_file() subroutine to save the specified
278 * file. The plugin will be called at pluginIO() to supply the
281 * Sequence of calls for restore:
282 * See subroutine plugin_name_stream() below.
284 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
289 char *cmd = ff_pkt->top_fname;
292 POOL_MEM fname(PM_FNAME);
293 POOL_MEM link(PM_FNAME);
295 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
296 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
297 return 1; /* Return if no plugins loaded */
300 jcr->cmd_plugin = true;
301 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
302 event.eventType = bEventBackupCommand;
304 if (!get_plugin_name(jcr, cmd, &len)) {
308 /* Note, we stop the loop on the first plugin that matches the name */
309 foreach_alist(plugin, plugin_list) {
310 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
311 if (strncmp(plugin->file, cmd, len) != 0) {
316 * We put the current plugin pointer, and the plugin context
317 * into the jcr, because during save_file(), the plugin
318 * will be called many times and these values are needed.
320 jcr->plugin_ctx = &plugin_ctx_list[i];
321 jcr->plugin = plugin;
322 if (is_plugin_disabled(jcr)) {
326 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
327 /* Send the backup command to the right plugin*/
328 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
331 /* Loop getting filenames to backup then saving them */
332 while (!jcr->is_job_canceled()) {
333 memset(&sp, 0, sizeof(sp));
334 sp.pkt_size = sizeof(sp);
335 sp.pkt_end = sizeof(sp);
338 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
340 /* Get the file save parameters. I.e. the stat pkt ... */
341 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
345 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
349 jcr->plugin_sp = &sp;
352 * Copy fname and link because save_file() zaps them. This
353 * avoids zaping the plugin's strings.
355 ff_pkt->type = sp.type;
356 if (sp.type == FT_RESTORE_FIRST) {
357 if (!sp.object_name) {
358 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
362 ff_pkt->fname = cmd; /* full plugin string */
363 ff_pkt->object_name = sp.object_name;
364 ff_pkt->object_index = sp.index; /* restore object index */
365 ff_pkt->object_compression = 0; /* no compression for now */
366 ff_pkt->object = sp.object;
367 ff_pkt->object_len = sp.object_len;
370 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
374 pm_strcpy(fname, sp.fname);
375 pm_strcpy(link, sp.link);
376 ff_pkt->fname = fname.c_str();
377 ff_pkt->link = link.c_str();
380 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
381 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
383 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
385 /* Call Bacula core code to backup the plugin's file */
386 save_file(jcr, ff_pkt, true);
387 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
388 if (rc == bRC_More || rc == bRC_OK) {
389 accurate_mark_file_as_seen(jcr, fname.c_str());
391 if (rc == bRC_More) {
398 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
401 jcr->cmd_plugin = false;
403 jcr->plugin_ctx = NULL;
408 * Send plugin name start/end record to SD
410 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
413 int index = jcr->JobFiles;
414 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
417 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
420 if (jcr->is_job_canceled()) {
425 index++; /* JobFiles not incremented yet */
427 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
428 /* Send stream header */
429 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
430 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
434 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
437 /* Send data -- not much */
438 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
440 /* Send end of data */
441 stat = sd->fsend("0 0");
444 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
448 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
449 sd->signal(BNET_EOD); /* indicate end of plugin name data */
454 * Plugin name stream found during restore. The record passed in
455 * argument name was generated in send_plugin_name() above.
457 * Returns: true if start of stream
458 * false if end of steam
460 bool plugin_name_stream(JCR *jcr, char *name)
464 bool start, portable;
468 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
470 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
471 skip_nonspaces(&p); /* skip over jcr->JobFiles */
475 /* Start of plugin data */
476 skip_nonspaces(&p); /* skip start/end flag */
478 portable = *p == '1';
479 skip_nonspaces(&p); /* skip portable flag */
484 * End of plugin data, notify plugin, then clear flags
486 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
488 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
490 jcr->plugin_ctx = NULL;
494 if (!plugin_ctx_list) {
499 * After this point, we are dealing with a restore start
501 if (!get_plugin_name(jcr, cmd, &len)) {
506 * Search for correct plugin as specified on the command
508 foreach_alist(plugin, plugin_list) {
510 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
511 if (strncmp(plugin->file, cmd, len) != 0) {
515 jcr->plugin_ctx = &plugin_ctx_list[i];
516 jcr->plugin = plugin;
517 if (is_plugin_disabled(jcr)) {
520 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
521 event.eventType = bEventRestoreCommand;
522 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
523 &event, cmd) != bRC_OK) {
526 /* ***FIXME**** check error code */
527 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
530 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
537 * Tell the plugin to create the file. Return values are
538 * This is called only during Restore
541 * CF_SKIP -- skip processing this file
542 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
543 * CF_CREATED -- created, but no content to extract (typically directories)
546 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
548 bpContext *plugin_ctx = jcr->plugin_ctx;
549 Plugin *plugin = jcr->plugin;
550 struct restore_pkt rp;
554 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
558 rp.pkt_size = sizeof(rp);
559 rp.pkt_end = sizeof(rp);
560 rp.stream = attr->stream;
561 rp.data_stream = attr->data_stream;
562 rp.type = attr->type;
563 rp.file_index = attr->file_index;
564 rp.LinkFI = attr->LinkFI;
566 rp.statp = attr->statp; /* structure assignment */
567 rp.attrEx = attr->attrEx;
568 rp.ofname = attr->ofname;
569 rp.olname = attr->olname;
570 rp.where = jcr->where;
571 rp.RegexWhere = jcr->RegexWhere;
572 rp.replace = jcr->replace;
573 rp.create_status = CF_ERROR;
574 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
575 rp.stream, rp.type, rp.LinkFI, rp.ofname);
577 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
579 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
581 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
585 if (rp.create_status == CF_SKIP) {
588 if (rp.create_status == CF_ERROR) {
589 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
593 /* Created link or directory? */
594 if (rp.create_status == CF_CREATED) {
595 return rp.create_status; /* yes, no need to bopen */
598 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
599 Dmsg0(dbglvl, "call bopen\n");
600 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
601 Dmsg1(50, "bopen status=%d\n", stat);
604 be.set_errno(bfd->berrno);
605 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
606 attr->ofname, be.bstrerror());
607 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
611 if (!is_bopen(bfd)) {
612 Dmsg0(000, "===== BFD is not open!!!!\n");
618 * Reset the file attributes after all file I/O is done -- this allows
619 * the previous access time/dates to be set properly, and it also allows
620 * us to properly set directory permissions.
621 * Not currently Implemented.
623 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
625 Dmsg0(dbglvl, "plugin_set_attributes\n");
629 pm_strcpy(attr->ofname, "*none*");
634 * Print to file the plugin info.
636 void dump_fd_plugin(Plugin *plugin, FILE *fp)
641 pInfo *info = (pInfo *)plugin->pinfo;
642 fprintf(fp, "\tversion=%d\n", info->version);
643 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
644 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
645 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
646 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
647 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
648 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
652 * This entry point is called internally by Bacula to ensure
653 * that the plugin IO calls come into this code.
655 void load_fd_plugins(const char *plugin_dir)
660 Dmsg0(dbglvl, "plugin dir is NULL\n");
664 plugin_list = New(alist(10, not_owned_by_alist));
665 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
666 is_plugin_compatible)) {
667 /* Either none found, or some error */
668 if (plugin_list->size() == 0) {
671 Dmsg0(dbglvl, "No plugins loaded\n");
676 /* Plug entry points called from findlib */
677 plugin_bopen = my_plugin_bopen;
678 plugin_bclose = my_plugin_bclose;
679 plugin_bread = my_plugin_bread;
680 plugin_bwrite = my_plugin_bwrite;
681 plugin_blseek = my_plugin_blseek;
684 * Verify that the plugin is acceptable, and print information
687 foreach_alist(plugin, plugin_list) {
688 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
689 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
692 dbg_plugin_add_hook(dump_fd_plugin);
696 * Check if a plugin is compatible. Called by the load_plugin function
697 * to allow us to verify the plugin.
699 static bool is_plugin_compatible(Plugin *plugin)
701 pInfo *info = (pInfo *)plugin->pinfo;
702 Dmsg0(50, "is_plugin_compatible called\n");
703 if (debug_level >= 50) {
704 dump_fd_plugin(plugin, stdin);
706 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
707 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
708 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
709 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
710 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
714 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
715 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
716 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
717 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
718 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
721 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
722 strcmp(info->plugin_license, "AGPLv3") != 0) {
723 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
724 plugin->file, info->plugin_license);
725 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
726 plugin->file, info->plugin_license);
735 * Create a new instance of each plugin for this Job
736 * Note, plugin_list can exist but jcr->plugin_ctx_list can
737 * be NULL if no plugins were loaded.
739 void new_plugins(JCR *jcr)
745 Dmsg0(dbglvl, "plugin list is NULL\n");
748 if (jcr->is_job_canceled() || jcr->JobId == 0) {
752 int num = plugin_list->size();
755 Dmsg0(dbglvl, "No plugins loaded\n");
759 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
761 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
762 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
763 foreach_alist(plugin, plugin_list) {
764 /* Start a new instance of each plugin */
765 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
766 memset(b_ctx, 0, sizeof(bacula_ctx));
768 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
769 plugin_ctx_list[i].pContext = NULL;
770 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
771 b_ctx->disabled = true;
777 * Free the plugin instances for this Job
779 void free_plugins(JCR *jcr)
784 if (!plugin_list || !jcr->plugin_ctx_list) {
785 return; /* no plugins, nothing to do */
788 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
789 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
790 foreach_alist(plugin, plugin_list) {
791 /* Free the plugin instance */
792 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
793 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
795 free(plugin_ctx_list);
796 jcr->plugin_ctx_list = NULL;
799 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
802 Plugin *plugin = (Plugin *)jcr->plugin;
805 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
806 if (!plugin || !jcr->plugin_ctx) {
809 io.pkt_size = sizeof(io);
810 io.pkt_end = sizeof(io);
819 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
820 bfd->berrno = io.io_errno;
822 errno = b_errno_win32;
825 bfd->lerror = io.lerror;
827 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
831 static int my_plugin_bclose(BFILE *bfd)
834 Plugin *plugin = (Plugin *)jcr->plugin;
837 Dmsg0(dbglvl, "===== plugin_bclose\n");
838 if (!plugin || !jcr->plugin_ctx) {
841 io.pkt_size = sizeof(io);
842 io.pkt_end = sizeof(io);
848 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
849 bfd->berrno = io.io_errno;
851 errno = b_errno_win32;
854 bfd->lerror = io.lerror;
856 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
860 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
863 Plugin *plugin = (Plugin *)jcr->plugin;
866 Dmsg0(dbglvl, "plugin_bread\n");
867 if (!plugin || !jcr->plugin_ctx) {
870 io.pkt_size = sizeof(io);
871 io.pkt_end = sizeof(io);
874 io.buf = (char *)buf;
877 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
878 bfd->berrno = io.io_errno;
880 errno = b_errno_win32;
883 bfd->lerror = io.lerror;
885 return (ssize_t)io.status;
888 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
891 Plugin *plugin = (Plugin *)jcr->plugin;
894 Dmsg0(dbglvl, "plugin_bwrite\n");
895 if (!plugin || !jcr->plugin_ctx) {
898 io.pkt_size = sizeof(io);
899 io.pkt_end = sizeof(io);
902 io.buf = (char *)buf;
905 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
906 bfd->berrno = io.io_errno;
908 errno = b_errno_win32;
911 bfd->lerror = io.lerror;
913 return (ssize_t)io.status;
916 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
919 Plugin *plugin = (Plugin *)jcr->plugin;
922 Dmsg0(dbglvl, "plugin_bseek\n");
923 if (!plugin || !jcr->plugin_ctx) {
926 io.pkt_size = sizeof(io);
927 io.pkt_end = sizeof(io);
933 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
934 bfd->berrno = io.io_errno;
936 errno = b_errno_win32;
939 bfd->lerror = io.lerror;
941 return (boffset_t)io.offset;
944 /* ==============================================================
946 * Callbacks from the plugin
948 * ==============================================================
950 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
953 if (!value || !ctx) {
956 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
957 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
961 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
964 *((int *)value) = jcr->JobId;
965 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
968 *((char **)value) = my_name;
969 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
972 *((int *)value) = jcr->getJobLevel();
973 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
976 *((int *)value) = jcr->getJobType();
977 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
980 *((char **)value) = jcr->client_name;
981 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
984 *((char **)value) = jcr->Job;
985 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
988 *((int *)value) = jcr->JobStatus;
989 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
992 *((int *)value) = (int)jcr->mtime;
993 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
996 *((int *)value) = (int)jcr->accurate;
997 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1000 break; /* a write only variable, ignore read request */
1004 *(void **)value = g_pVSSClient->GetVssObject();
1009 case bVarVssDllHandle:
1012 *(void **)value = g_pVSSClient->GetVssDllHandle();
1017 case bVarWorkingDir:
1018 *(void **)value = me->working_directory;
1021 *(char **)value = jcr->where;
1023 case bVarRegexWhere:
1024 *(char **)value = jcr->RegexWhere;
1030 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1033 if (!value || !ctx) {
1036 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1037 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1041 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1044 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1054 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1063 va_start(args, ctx);
1064 while ((event = va_arg(args, uint32_t))) {
1065 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1071 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1072 int type, utime_t mtime, const char *fmt, ...)
1079 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1084 va_start(arg_ptr, fmt);
1085 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1087 Jmsg(jcr, type, mtime, "%s", buf);
1091 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1092 int level, const char *fmt, ...)
1097 va_start(arg_ptr, fmt);
1098 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1100 d_msg(file, line, level, "%s", buf);
1104 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1108 return sm_malloc(file, line, size);
1110 return malloc(size);
1114 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1117 sm_free(file, line, mem);
1123 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1128 bctx = (bacula_ctx *)ctx->bContext;
1140 * Let the plugin define files/directories to be excluded
1141 * from the main backup.
1143 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1147 if (!is_ctx_good(ctx, jcr, bctx)) {
1153 if (!bctx->exclude) {
1154 bctx->exclude = new_exclude(jcr);
1155 new_options(jcr, bctx->exclude);
1157 set_incexe(jcr, bctx->exclude);
1158 add_file_to_fileset(jcr, file, true);
1159 Dmsg1(100, "Add exclude file=%s\n", file);
1164 * Let the plugin define files/directories to be excluded
1165 * from the main backup.
1167 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1171 if (!is_ctx_good(ctx, jcr, bctx)) {
1177 if (!bctx->include) {
1178 bctx->include = new_preinclude(jcr);
1179 new_options(jcr, bctx->include);
1181 set_incexe(jcr, bctx->include);
1182 add_file_to_fileset(jcr, file, true);
1183 Dmsg1(100, "Add include file=%s\n", file);
1187 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1191 if (!is_ctx_good(ctx, jcr, bctx)) {
1197 add_options_to_fileset(jcr, opts);
1198 Dmsg1(1000, "Add options=%s\n", opts);
1202 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1206 if (!is_ctx_good(ctx, jcr, bctx)) {
1212 add_regex_to_fileset(jcr, item, type);
1213 Dmsg1(100, "Add regex=%s\n", item);
1217 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1221 if (!is_ctx_good(ctx, jcr, bctx)) {
1227 add_wild_to_fileset(jcr, item, type);
1228 Dmsg1(100, "Add wild=%s\n", item);
1232 static bRC baculaNewOptions(bpContext *ctx)
1236 if (!is_ctx_good(ctx, jcr, bctx)) {
1239 (void)new_options(jcr, NULL);
1243 static bRC baculaNewInclude(bpContext *ctx)
1247 if (!is_ctx_good(ctx, jcr, bctx)) {
1250 (void)new_include(jcr);
1256 * Check if a file have to be backuped using Accurate code
1258 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1263 bRC ret = bRC_Error;
1265 if (!is_ctx_good(ctx, jcr, bctx)) {
1274 * Copy fname and link because save_file() zaps them. This
1275 * avoids zaping the plugin's strings.
1277 ff_pkt->type = sp->type;
1279 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1283 ff_pkt->fname = sp->fname;
1284 ff_pkt->link = sp->link;
1285 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1287 if (check_changes(jcr, ff_pkt)) {
1294 Dmsg1(100, "checkChanges=%i\n", ret);
1301 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1302 int (*plugin_bclose)(JCR *jcr) = NULL;
1303 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1304 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1305 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1307 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1312 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1317 int main(int argc, char *argv[])
1319 char plugin_dir[1000];
1324 strcpy(my_name, "test-fd");
1326 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1327 load_fd_plugins(plugin_dir);
1335 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1336 generate_plugin_event(jcr1, bEventJobEnd);
1337 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1339 generate_plugin_event(jcr2, bEventJobEnd);
1344 Dmsg0(dbglvl, "bacula: OK ...\n");
1345 close_memory_pool();
1346 sm_dump(false); /* unit test */
1350 #endif /* TEST_PROGRAM */