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);
48 /* Function pointers to be set here */
49 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
50 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
51 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
52 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
53 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
56 /* Forward referenced functions */
57 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
58 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
59 static bRC baculaRegisterEvents(bpContext *ctx, ...);
60 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
61 int type, utime_t mtime, const char *fmt, ...);
62 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
63 int level, const char *fmt, ...);
64 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
66 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
67 static bRC baculaAddExclude(bpContext *ctx, const char *file);
68 static bRC baculaAddInclude(bpContext *ctx, const char *file);
69 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
70 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
71 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
72 static bRC baculaNewOptions(bpContext *ctx);
73 static bRC baculaNewInclude(bpContext *ctx);
74 static bool is_plugin_compatible(Plugin *plugin);
75 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
78 * These will be plugged into the global pointer structure for
81 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
82 static int my_plugin_bclose(BFILE *bfd);
83 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
84 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
85 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
89 static bInfo binfo = {
91 FD_PLUGIN_INTERFACE_VERSION
94 /* Bacula entry points */
95 static bFuncs bfuncs = {
97 FD_PLUGIN_INTERFACE_VERSION,
115 * Bacula private context
118 JCR *jcr; /* jcr for plugin */
119 bRC rc; /* last return code */
120 bool disabled; /* set if plugin disabled */
121 findINCEXE *exclude; /* pointer to exclude files */
122 findINCEXE *include; /* pointer to include/exclude files */
125 static bool is_plugin_disabled(bpContext *plugin_ctx)
131 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
132 return b_ctx->disabled;
135 static bool is_plugin_disabled(JCR *jcr)
137 return is_plugin_disabled(jcr->plugin_ctx);
141 * Create a plugin event
142 * When receiving bEventCancelCommand, this function is called by an other thread.
144 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
146 bpContext *plugin_ctx;
154 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
155 return; /* Return if no plugins loaded */
158 /* Some events are sent to only a particular plugin */
160 case bEventPluginCommand:
161 name = (char *)value;
162 if (!get_plugin_name(jcr, name, &len)) {
170 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
171 event.eventType = eventType;
173 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
175 /* Pass event to every plugin (except if name is set) */
176 foreach_alist(plugin, plugin_list) {
177 if (name && strncmp(plugin->file, name, len) != 0) {
181 plugin_ctx = &plugin_ctx_list[i++];
182 if (is_plugin_disabled(plugin_ctx)) {
185 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
194 * Check if file was seen for accurate
196 bool plugin_check_file(JCR *jcr, char *fname)
202 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
203 return false; /* Return if no plugins loaded */
206 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
208 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
210 /* Pass event to every plugin */
211 foreach_alist(plugin, plugin_list) {
212 jcr->plugin_ctx = &plugin_ctx_list[i++];
213 jcr->plugin = plugin;
214 if (is_plugin_disabled(jcr)) {
217 if (plug_func(plugin)->checkFile == NULL) {
220 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
221 if (rc == bRC_Seen) {
227 jcr->plugin_ctx = NULL;
228 return rc == bRC_Seen;
231 /* Get the first part of the the plugin command
232 * systemstate:/@SYSTEMSTATE/
234 * => can use strncmp(plugin_name, cmd, ret);
236 * The plugin command can contain only the plugin name
240 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
244 if (!cmd || (*cmd == '\0')) {
247 /* Handle plugin command here backup */
248 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
249 if ((p = strchr(cmd, ':')) == NULL) {
250 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
253 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
256 } else { /* plugin:argument */
267 * Sequence of calls for a backup:
268 * 1. plugin_save() here is called with ff_pkt
269 * 2. we find the plugin requested on the command string
270 * 3. we generate a bEventBackupCommand event to the specified plugin
271 * and pass it the command string.
272 * 4. we make a startPluginBackup call to the plugin, which gives
273 * us the data we need in save_pkt
274 * 5. we call Bacula's save_file() subroutine to save the specified
275 * file. The plugin will be called at pluginIO() to supply the
278 * Sequence of calls for restore:
279 * See subroutine plugin_name_stream() below.
281 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
286 char *cmd = ff_pkt->top_fname;
289 POOL_MEM fname(PM_FNAME);
290 POOL_MEM link(PM_FNAME);
292 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
293 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
294 return 1; /* Return if no plugins loaded */
297 jcr->cmd_plugin = true;
298 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
299 event.eventType = bEventBackupCommand;
301 if (!get_plugin_name(jcr, cmd, &len)) {
305 /* Note, we stop the loop on the first plugin that matches the name */
306 foreach_alist(plugin, plugin_list) {
307 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
308 if (strncmp(plugin->file, cmd, len) != 0) {
313 * We put the current plugin pointer, and the plugin context
314 * into the jcr, because during save_file(), the plugin
315 * will be called many times and these values are needed.
317 jcr->plugin_ctx = &plugin_ctx_list[i];
318 jcr->plugin = plugin;
319 if (is_plugin_disabled(jcr)) {
323 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
324 /* Send the backup command to the right plugin*/
325 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
328 /* Loop getting filenames to backup then saving them */
329 while (!jcr->is_job_canceled()) {
330 memset(&sp, 0, sizeof(sp));
331 sp.pkt_size = sizeof(sp);
332 sp.pkt_end = sizeof(sp);
335 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
337 /* Get the file save parameters. I.e. the stat pkt ... */
338 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
342 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
346 jcr->plugin_sp = &sp;
349 * Copy fname and link because save_file() zaps them. This
350 * avoids zaping the plugin's strings.
352 ff_pkt->type = sp.type;
353 if (sp.type == FT_RESTORE_FIRST) {
354 if (!sp.object_name) {
355 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
359 ff_pkt->fname = cmd; /* full plugin string */
360 ff_pkt->object_name = sp.object_name;
361 ff_pkt->object_index = sp.index; /* restore object index */
362 ff_pkt->object_compression = 0; /* no compression for now */
363 ff_pkt->object = sp.object;
364 ff_pkt->object_len = sp.object_len;
367 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
371 pm_strcpy(fname, sp.fname);
372 pm_strcpy(link, sp.link);
373 ff_pkt->fname = fname.c_str();
374 ff_pkt->link = link.c_str();
377 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
378 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
380 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
382 /* Call Bacula core code to backup the plugin's file */
383 save_file(jcr, ff_pkt, true);
384 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
385 if (rc == bRC_More || rc == bRC_OK) {
386 accurate_mark_file_as_seen(jcr, fname.c_str());
388 if (rc == bRC_More) {
395 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
398 jcr->cmd_plugin = false;
400 jcr->plugin_ctx = NULL;
405 * Send plugin name start/end record to SD
407 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
410 int index = jcr->JobFiles;
411 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
414 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
417 if (jcr->is_job_canceled()) {
422 index++; /* JobFiles not incremented yet */
424 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
425 /* Send stream header */
426 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
427 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
431 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
434 /* Send data -- not much */
435 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
437 /* Send end of data */
438 stat = sd->fsend("0 0");
441 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
445 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
446 sd->signal(BNET_EOD); /* indicate end of plugin name data */
451 * Plugin name stream found during restore. The record passed in
452 * argument name was generated in send_plugin_name() above.
454 * Returns: true if start of stream
455 * false if end of steam
457 bool plugin_name_stream(JCR *jcr, char *name)
461 bool start, portable;
465 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
467 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
468 skip_nonspaces(&p); /* skip over jcr->JobFiles */
472 /* Start of plugin data */
473 skip_nonspaces(&p); /* skip start/end flag */
475 portable = *p == '1';
476 skip_nonspaces(&p); /* skip portable flag */
481 * End of plugin data, notify plugin, then clear flags
483 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
485 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
487 jcr->plugin_ctx = NULL;
491 if (!plugin_ctx_list) {
496 * After this point, we are dealing with a restore start
498 if (!get_plugin_name(jcr, cmd, &len)) {
503 * Search for correct plugin as specified on the command
505 foreach_alist(plugin, plugin_list) {
507 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
508 if (strncmp(plugin->file, cmd, len) != 0) {
512 jcr->plugin_ctx = &plugin_ctx_list[i];
513 jcr->plugin = plugin;
514 if (is_plugin_disabled(jcr)) {
517 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
518 event.eventType = bEventRestoreCommand;
519 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
520 &event, cmd) != bRC_OK) {
523 /* ***FIXME**** check error code */
524 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
527 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
534 * Tell the plugin to create the file. Return values are
535 * This is called only during Restore
538 * CF_SKIP -- skip processing this file
539 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
540 * CF_CREATED -- created, but no content to extract (typically directories)
543 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
545 bpContext *plugin_ctx = jcr->plugin_ctx;
546 Plugin *plugin = jcr->plugin;
547 struct restore_pkt rp;
551 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
555 rp.pkt_size = sizeof(rp);
556 rp.pkt_end = sizeof(rp);
557 rp.stream = attr->stream;
558 rp.data_stream = attr->data_stream;
559 rp.type = attr->type;
560 rp.file_index = attr->file_index;
561 rp.LinkFI = attr->LinkFI;
563 rp.statp = attr->statp; /* structure assignment */
564 rp.attrEx = attr->attrEx;
565 rp.ofname = attr->ofname;
566 rp.olname = attr->olname;
567 rp.where = jcr->where;
568 rp.RegexWhere = jcr->RegexWhere;
569 rp.replace = jcr->replace;
570 rp.create_status = CF_ERROR;
571 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
572 rp.stream, rp.type, rp.LinkFI, rp.ofname);
574 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
576 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
578 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
582 if (rp.create_status == CF_SKIP) {
585 if (rp.create_status == CF_ERROR) {
586 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
590 /* Created link or directory? */
591 if (rp.create_status == CF_CREATED) {
592 return rp.create_status; /* yes, no need to bopen */
595 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
596 Dmsg0(dbglvl, "call bopen\n");
597 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
598 Dmsg1(50, "bopen status=%d\n", stat);
601 be.set_errno(bfd->berrno);
602 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
603 attr->ofname, be.bstrerror());
604 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
608 if (!is_bopen(bfd)) {
609 Dmsg0(000, "===== BFD is not open!!!!\n");
615 * Reset the file attributes after all file I/O is done -- this allows
616 * the previous access time/dates to be set properly, and it also allows
617 * us to properly set directory permissions.
618 * Not currently Implemented.
620 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
622 Dmsg0(dbglvl, "plugin_set_attributes\n");
626 pm_strcpy(attr->ofname, "*none*");
631 * Print to file the plugin info.
633 void dump_fd_plugin(Plugin *plugin, FILE *fp)
638 pInfo *info = (pInfo *)plugin->pinfo;
639 fprintf(fp, "\tversion=%d\n", info->version);
640 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
641 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
642 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
643 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
644 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
645 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
649 * This entry point is called internally by Bacula to ensure
650 * that the plugin IO calls come into this code.
652 void load_fd_plugins(const char *plugin_dir)
657 Dmsg0(dbglvl, "plugin dir is NULL\n");
661 plugin_list = New(alist(10, not_owned_by_alist));
662 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
663 is_plugin_compatible)) {
664 /* Either none found, or some error */
665 if (plugin_list->size() == 0) {
668 Dmsg0(dbglvl, "No plugins loaded\n");
673 /* Plug entry points called from findlib */
674 plugin_bopen = my_plugin_bopen;
675 plugin_bclose = my_plugin_bclose;
676 plugin_bread = my_plugin_bread;
677 plugin_bwrite = my_plugin_bwrite;
678 plugin_blseek = my_plugin_blseek;
681 * Verify that the plugin is acceptable, and print information
684 foreach_alist(plugin, plugin_list) {
685 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
686 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
689 dbg_plugin_add_hook(dump_fd_plugin);
693 * Check if a plugin is compatible. Called by the load_plugin function
694 * to allow us to verify the plugin.
696 static bool is_plugin_compatible(Plugin *plugin)
698 pInfo *info = (pInfo *)plugin->pinfo;
699 Dmsg0(50, "is_plugin_compatible called\n");
700 if (debug_level >= 50) {
701 dump_fd_plugin(plugin, stdin);
703 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
704 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
705 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
706 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
707 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
711 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
712 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
713 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
714 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
715 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
718 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
719 strcmp(info->plugin_license, "AGPLv3") != 0) {
720 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
721 plugin->file, info->plugin_license);
722 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
723 plugin->file, info->plugin_license);
732 * Create a new instance of each plugin for this Job
733 * Note, plugin_list can exist but jcr->plugin_ctx_list can
734 * be NULL if no plugins were loaded.
736 void new_plugins(JCR *jcr)
742 Dmsg0(dbglvl, "plugin list is NULL\n");
745 if (jcr->is_job_canceled() || jcr->JobId == 0) {
749 int num = plugin_list->size();
752 Dmsg0(dbglvl, "No plugins loaded\n");
756 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
758 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
759 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
760 foreach_alist(plugin, plugin_list) {
761 /* Start a new instance of each plugin */
762 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
763 memset(b_ctx, 0, sizeof(bacula_ctx));
765 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
766 plugin_ctx_list[i].pContext = NULL;
767 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
768 b_ctx->disabled = true;
774 * Free the plugin instances for this Job
776 void free_plugins(JCR *jcr)
781 if (!plugin_list || !jcr->plugin_ctx_list) {
782 return; /* no plugins, nothing to do */
785 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
786 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
787 foreach_alist(plugin, plugin_list) {
788 /* Free the plugin instance */
789 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
790 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
792 free(plugin_ctx_list);
793 jcr->plugin_ctx_list = NULL;
796 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
799 Plugin *plugin = (Plugin *)jcr->plugin;
802 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
803 if (!plugin || !jcr->plugin_ctx) {
806 io.pkt_size = sizeof(io);
807 io.pkt_end = sizeof(io);
816 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
817 bfd->berrno = io.io_errno;
819 errno = b_errno_win32;
822 bfd->lerror = io.lerror;
824 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
828 static int my_plugin_bclose(BFILE *bfd)
831 Plugin *plugin = (Plugin *)jcr->plugin;
834 Dmsg0(dbglvl, "===== plugin_bclose\n");
835 if (!plugin || !jcr->plugin_ctx) {
838 io.pkt_size = sizeof(io);
839 io.pkt_end = sizeof(io);
845 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
846 bfd->berrno = io.io_errno;
848 errno = b_errno_win32;
851 bfd->lerror = io.lerror;
853 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
857 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
860 Plugin *plugin = (Plugin *)jcr->plugin;
863 Dmsg0(dbglvl, "plugin_bread\n");
864 if (!plugin || !jcr->plugin_ctx) {
867 io.pkt_size = sizeof(io);
868 io.pkt_end = sizeof(io);
871 io.buf = (char *)buf;
874 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
875 bfd->berrno = io.io_errno;
877 errno = b_errno_win32;
880 bfd->lerror = io.lerror;
882 return (ssize_t)io.status;
885 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
888 Plugin *plugin = (Plugin *)jcr->plugin;
891 Dmsg0(dbglvl, "plugin_bwrite\n");
892 if (!plugin || !jcr->plugin_ctx) {
895 io.pkt_size = sizeof(io);
896 io.pkt_end = sizeof(io);
899 io.buf = (char *)buf;
902 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
903 bfd->berrno = io.io_errno;
905 errno = b_errno_win32;
908 bfd->lerror = io.lerror;
910 return (ssize_t)io.status;
913 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
916 Plugin *plugin = (Plugin *)jcr->plugin;
919 Dmsg0(dbglvl, "plugin_bseek\n");
920 if (!plugin || !jcr->plugin_ctx) {
923 io.pkt_size = sizeof(io);
924 io.pkt_end = sizeof(io);
930 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
931 bfd->berrno = io.io_errno;
933 errno = b_errno_win32;
936 bfd->lerror = io.lerror;
938 return (boffset_t)io.offset;
941 /* ==============================================================
943 * Callbacks from the plugin
945 * ==============================================================
947 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
950 if (!value || !ctx) {
953 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
954 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
958 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
961 *((int *)value) = jcr->JobId;
962 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
965 *((char **)value) = my_name;
966 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
969 *((int *)value) = jcr->getJobLevel();
970 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
973 *((int *)value) = jcr->getJobType();
974 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
977 *((char **)value) = jcr->client_name;
978 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
981 *((char **)value) = jcr->Job;
982 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
985 *((int *)value) = jcr->JobStatus;
986 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
989 *((int *)value) = (int)jcr->mtime;
990 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
993 *((int *)value) = (int)jcr->accurate;
994 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
997 break; /* a write only variable, ignore read request */
1001 *(void **)value = g_pVSSClient->GetVssObject();
1006 case bVarVssDllHandle:
1009 *(void **)value = g_pVSSClient->GetVssDllHandle();
1014 case bVarWorkingDir:
1015 *(void **)value = me->working_directory;
1021 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1024 if (!value || !ctx) {
1027 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1028 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1032 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1035 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1045 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1054 va_start(args, ctx);
1055 while ((event = va_arg(args, uint32_t))) {
1056 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1062 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1063 int type, utime_t mtime, const char *fmt, ...)
1070 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1075 va_start(arg_ptr, fmt);
1076 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1078 Jmsg(jcr, type, mtime, "%s", buf);
1082 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1083 int level, const char *fmt, ...)
1088 va_start(arg_ptr, fmt);
1089 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1091 d_msg(file, line, level, "%s", buf);
1095 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1099 return sm_malloc(file, line, size);
1101 return malloc(size);
1105 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1108 sm_free(file, line, mem);
1114 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1119 bctx = (bacula_ctx *)ctx->bContext;
1131 * Let the plugin define files/directories to be excluded
1132 * from the main backup.
1134 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1138 if (!is_ctx_good(ctx, jcr, bctx)) {
1144 if (!bctx->exclude) {
1145 bctx->exclude = new_exclude(jcr);
1146 new_options(jcr, bctx->exclude);
1148 set_incexe(jcr, bctx->exclude);
1149 add_file_to_fileset(jcr, file, true);
1150 Dmsg1(100, "Add exclude file=%s\n", file);
1155 * Let the plugin define files/directories to be excluded
1156 * from the main backup.
1158 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1162 if (!is_ctx_good(ctx, jcr, bctx)) {
1168 if (!bctx->include) {
1169 bctx->include = new_preinclude(jcr);
1170 new_options(jcr, bctx->include);
1172 set_incexe(jcr, bctx->include);
1173 add_file_to_fileset(jcr, file, true);
1174 Dmsg1(100, "Add include file=%s\n", file);
1178 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1182 if (!is_ctx_good(ctx, jcr, bctx)) {
1188 add_options_to_fileset(jcr, opts);
1189 Dmsg1(1000, "Add options=%s\n", opts);
1193 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1197 if (!is_ctx_good(ctx, jcr, bctx)) {
1203 add_regex_to_fileset(jcr, item, type);
1204 Dmsg1(100, "Add regex=%s\n", item);
1208 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1212 if (!is_ctx_good(ctx, jcr, bctx)) {
1218 add_wild_to_fileset(jcr, item, type);
1219 Dmsg1(100, "Add wild=%s\n", item);
1223 static bRC baculaNewOptions(bpContext *ctx)
1227 if (!is_ctx_good(ctx, jcr, bctx)) {
1230 (void)new_options(jcr, NULL);
1234 static bRC baculaNewInclude(bpContext *ctx)
1238 if (!is_ctx_good(ctx, jcr, bctx)) {
1241 (void)new_include(jcr);
1248 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1249 int (*plugin_bclose)(JCR *jcr) = NULL;
1250 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1251 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1252 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1254 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1259 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1264 int main(int argc, char *argv[])
1266 char plugin_dir[1000];
1271 strcpy(my_name, "test-fd");
1273 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1274 load_fd_plugins(plugin_dir);
1282 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1283 generate_plugin_event(jcr1, bEventJobEnd);
1284 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1286 generate_plugin_event(jcr2, bEventJobEnd);
1291 Dmsg0(dbglvl, "bacula: OK ...\n");
1292 close_memory_pool();
1293 sm_dump(false); /* unit test */
1297 #endif /* TEST_PROGRAM */