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 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
243 /* Handle plugin command here backup */
244 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
245 if (!(p = strchr(cmd, ':'))) {
246 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
258 * Sequence of calls for a backup:
259 * 1. plugin_save() here is called with ff_pkt
260 * 2. we find the plugin requested on the command string
261 * 3. we generate a bEventBackupCommand event to the specified plugin
262 * and pass it the command string.
263 * 4. we make a startPluginBackup call to the plugin, which gives
264 * us the data we need in save_pkt
265 * 5. we call Bacula's save_file() subroutine to save the specified
266 * file. The plugin will be called at pluginIO() to supply the
269 * Sequence of calls for restore:
270 * See subroutine plugin_name_stream() below.
272 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
277 char *cmd = ff_pkt->top_fname;
280 POOL_MEM fname(PM_FNAME);
281 POOL_MEM link(PM_FNAME);
283 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
284 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
285 return 1; /* Return if no plugins loaded */
288 jcr->cmd_plugin = true;
289 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
290 event.eventType = bEventBackupCommand;
292 if (!get_plugin_name(jcr, cmd, &len)) {
296 /* Note, we stop the loop on the first plugin that matches the name */
297 foreach_alist(plugin, plugin_list) {
298 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
299 if (strncmp(plugin->file, cmd, len) != 0) {
304 * We put the current plugin pointer, and the plugin context
305 * into the jcr, because during save_file(), the plugin
306 * will be called many times and these values are needed.
308 jcr->plugin_ctx = &plugin_ctx_list[i];
309 jcr->plugin = plugin;
310 if (is_plugin_disabled(jcr)) {
314 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
315 /* Send the backup command to the right plugin*/
316 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
319 /* Loop getting filenames to backup then saving them */
320 while (!jcr->is_job_canceled()) {
321 memset(&sp, 0, sizeof(sp));
322 sp.pkt_size = sizeof(sp);
323 sp.pkt_end = sizeof(sp);
326 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
328 /* Get the file save parameters. I.e. the stat pkt ... */
329 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
333 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
337 jcr->plugin_sp = &sp;
340 * Copy fname and link because save_file() zaps them. This
341 * avoids zaping the plugin's strings.
343 ff_pkt->type = sp.type;
344 if (sp.type == FT_RESTORE_FIRST) {
345 if (!sp.object_name) {
346 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
350 ff_pkt->fname = cmd; /* full plugin string */
351 ff_pkt->object_name = sp.object_name;
352 ff_pkt->object_index = sp.index; /* restore object index */
353 ff_pkt->object_compression = 0; /* no compression for now */
354 ff_pkt->object = sp.object;
355 ff_pkt->object_len = sp.object_len;
358 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
362 pm_strcpy(fname, sp.fname);
363 pm_strcpy(link, sp.link);
364 ff_pkt->fname = fname.c_str();
365 ff_pkt->link = link.c_str();
368 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
369 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
371 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
373 /* Call Bacula core code to backup the plugin's file */
374 save_file(jcr, ff_pkt, true);
375 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
376 if (rc == bRC_More || rc == bRC_OK) {
377 accurate_mark_file_as_seen(jcr, fname.c_str());
379 if (rc == bRC_More) {
386 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
389 jcr->cmd_plugin = false;
391 jcr->plugin_ctx = NULL;
396 * Send plugin name start/end record to SD
398 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
401 int index = jcr->JobFiles;
402 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
405 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
408 if (jcr->is_job_canceled()) {
413 index++; /* JobFiles not incremented yet */
415 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
416 /* Send stream header */
417 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
418 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
422 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
425 /* Send data -- not much */
426 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
428 /* Send end of data */
429 stat = sd->fsend("0 0");
432 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
436 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
437 sd->signal(BNET_EOD); /* indicate end of plugin name data */
442 * Plugin name stream found during restore. The record passed in
443 * argument name was generated in send_plugin_name() above.
445 * Returns: true if start of stream
446 * false if end of steam
448 bool plugin_name_stream(JCR *jcr, char *name)
452 bool start, portable;
456 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
458 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
459 skip_nonspaces(&p); /* skip over jcr->JobFiles */
463 /* Start of plugin data */
464 skip_nonspaces(&p); /* skip start/end flag */
466 portable = *p == '1';
467 skip_nonspaces(&p); /* skip portable flag */
472 * End of plugin data, notify plugin, then clear flags
474 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
476 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
478 jcr->plugin_ctx = NULL;
482 if (!plugin_ctx_list) {
487 * After this point, we are dealing with a restore start
489 if (!get_plugin_name(jcr, cmd, &len)) {
494 * Search for correct plugin as specified on the command
496 foreach_alist(plugin, plugin_list) {
498 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
499 if (strncmp(plugin->file, cmd, len) != 0) {
503 jcr->plugin_ctx = &plugin_ctx_list[i];
504 jcr->plugin = plugin;
505 if (is_plugin_disabled(jcr)) {
508 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
509 event.eventType = bEventRestoreCommand;
510 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
511 &event, cmd) != bRC_OK) {
514 /* ***FIXME**** check error code */
515 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
518 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
525 * Tell the plugin to create the file. Return values are
526 * This is called only during Restore
529 * CF_SKIP -- skip processing this file
530 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
531 * CF_CREATED -- created, but no content to extract (typically directories)
534 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
536 bpContext *plugin_ctx = jcr->plugin_ctx;
537 Plugin *plugin = jcr->plugin;
538 struct restore_pkt rp;
542 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
546 rp.pkt_size = sizeof(rp);
547 rp.pkt_end = sizeof(rp);
548 rp.stream = attr->stream;
549 rp.data_stream = attr->data_stream;
550 rp.type = attr->type;
551 rp.file_index = attr->file_index;
552 rp.LinkFI = attr->LinkFI;
554 rp.statp = attr->statp; /* structure assignment */
555 rp.attrEx = attr->attrEx;
556 rp.ofname = attr->ofname;
557 rp.olname = attr->olname;
558 rp.where = jcr->where;
559 rp.RegexWhere = jcr->RegexWhere;
560 rp.replace = jcr->replace;
561 rp.create_status = CF_ERROR;
562 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
563 rp.stream, rp.type, rp.LinkFI, rp.ofname);
565 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
567 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
569 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
573 if (rp.create_status == CF_SKIP) {
576 if (rp.create_status == CF_ERROR) {
577 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
581 /* Created link or directory? */
582 if (rp.create_status == CF_CREATED) {
583 return rp.create_status; /* yes, no need to bopen */
586 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
587 Dmsg0(dbglvl, "call bopen\n");
588 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
589 Dmsg1(50, "bopen status=%d\n", stat);
592 be.set_errno(bfd->berrno);
593 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
594 attr->ofname, be.bstrerror());
595 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
599 if (!is_bopen(bfd)) {
600 Dmsg0(000, "===== BFD is not open!!!!\n");
606 * Reset the file attributes after all file I/O is done -- this allows
607 * the previous access time/dates to be set properly, and it also allows
608 * us to properly set directory permissions.
609 * Not currently Implemented.
611 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
613 Dmsg0(dbglvl, "plugin_set_attributes\n");
617 pm_strcpy(attr->ofname, "*none*");
622 * Print to file the plugin info.
624 void dump_fd_plugin(Plugin *plugin, FILE *fp)
629 pInfo *info = (pInfo *)plugin->pinfo;
630 fprintf(fp, "\tversion=%d\n", info->version);
631 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
632 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
633 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
634 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
635 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
636 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
640 * This entry point is called internally by Bacula to ensure
641 * that the plugin IO calls come into this code.
643 void load_fd_plugins(const char *plugin_dir)
648 Dmsg0(dbglvl, "plugin dir is NULL\n");
652 plugin_list = New(alist(10, not_owned_by_alist));
653 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
654 is_plugin_compatible)) {
655 /* Either none found, or some error */
656 if (plugin_list->size() == 0) {
659 Dmsg0(dbglvl, "No plugins loaded\n");
664 /* Plug entry points called from findlib */
665 plugin_bopen = my_plugin_bopen;
666 plugin_bclose = my_plugin_bclose;
667 plugin_bread = my_plugin_bread;
668 plugin_bwrite = my_plugin_bwrite;
669 plugin_blseek = my_plugin_blseek;
672 * Verify that the plugin is acceptable, and print information
675 foreach_alist(plugin, plugin_list) {
676 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
677 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
680 dbg_plugin_add_hook(dump_fd_plugin);
684 * Check if a plugin is compatible. Called by the load_plugin function
685 * to allow us to verify the plugin.
687 static bool is_plugin_compatible(Plugin *plugin)
689 pInfo *info = (pInfo *)plugin->pinfo;
690 Dmsg0(50, "is_plugin_compatible called\n");
691 if (debug_level >= 50) {
692 dump_fd_plugin(plugin, stdin);
694 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
695 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
696 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
697 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
698 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
702 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
703 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
704 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
705 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
706 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
709 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
710 strcmp(info->plugin_license, "AGPLv3") != 0) {
711 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
712 plugin->file, info->plugin_license);
713 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
714 plugin->file, info->plugin_license);
723 * Create a new instance of each plugin for this Job
724 * Note, plugin_list can exist but jcr->plugin_ctx_list can
725 * be NULL if no plugins were loaded.
727 void new_plugins(JCR *jcr)
733 Dmsg0(dbglvl, "plugin list is NULL\n");
736 if (jcr->is_job_canceled() || jcr->JobId == 0) {
740 int num = plugin_list->size();
743 Dmsg0(dbglvl, "No plugins loaded\n");
747 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
749 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
750 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
751 foreach_alist(plugin, plugin_list) {
752 /* Start a new instance of each plugin */
753 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
754 memset(b_ctx, 0, sizeof(bacula_ctx));
756 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
757 plugin_ctx_list[i].pContext = NULL;
758 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
759 b_ctx->disabled = true;
765 * Free the plugin instances for this Job
767 void free_plugins(JCR *jcr)
772 if (!plugin_list || !jcr->plugin_ctx_list) {
773 return; /* no plugins, nothing to do */
776 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
777 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
778 foreach_alist(plugin, plugin_list) {
779 /* Free the plugin instance */
780 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
781 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
783 free(plugin_ctx_list);
784 jcr->plugin_ctx_list = NULL;
787 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
790 Plugin *plugin = (Plugin *)jcr->plugin;
793 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
794 if (!plugin || !jcr->plugin_ctx) {
797 io.pkt_size = sizeof(io);
798 io.pkt_end = sizeof(io);
807 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
808 bfd->berrno = io.io_errno;
810 errno = b_errno_win32;
813 bfd->lerror = io.lerror;
815 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
819 static int my_plugin_bclose(BFILE *bfd)
822 Plugin *plugin = (Plugin *)jcr->plugin;
825 Dmsg0(dbglvl, "===== plugin_bclose\n");
826 if (!plugin || !jcr->plugin_ctx) {
829 io.pkt_size = sizeof(io);
830 io.pkt_end = sizeof(io);
836 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
837 bfd->berrno = io.io_errno;
839 errno = b_errno_win32;
842 bfd->lerror = io.lerror;
844 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
848 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
851 Plugin *plugin = (Plugin *)jcr->plugin;
854 Dmsg0(dbglvl, "plugin_bread\n");
855 if (!plugin || !jcr->plugin_ctx) {
858 io.pkt_size = sizeof(io);
859 io.pkt_end = sizeof(io);
862 io.buf = (char *)buf;
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 return (ssize_t)io.status;
876 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
879 Plugin *plugin = (Plugin *)jcr->plugin;
882 Dmsg0(dbglvl, "plugin_bwrite\n");
883 if (!plugin || !jcr->plugin_ctx) {
886 io.pkt_size = sizeof(io);
887 io.pkt_end = sizeof(io);
890 io.buf = (char *)buf;
893 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
894 bfd->berrno = io.io_errno;
896 errno = b_errno_win32;
899 bfd->lerror = io.lerror;
901 return (ssize_t)io.status;
904 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
907 Plugin *plugin = (Plugin *)jcr->plugin;
910 Dmsg0(dbglvl, "plugin_bseek\n");
911 if (!plugin || !jcr->plugin_ctx) {
914 io.pkt_size = sizeof(io);
915 io.pkt_end = sizeof(io);
921 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
922 bfd->berrno = io.io_errno;
924 errno = b_errno_win32;
927 bfd->lerror = io.lerror;
929 return (boffset_t)io.offset;
932 /* ==============================================================
934 * Callbacks from the plugin
936 * ==============================================================
938 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
941 if (!value || !ctx) {
944 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
945 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
949 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
952 *((int *)value) = jcr->JobId;
953 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
956 *((char **)value) = my_name;
957 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
960 *((int *)value) = jcr->getJobLevel();
961 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
964 *((int *)value) = jcr->getJobType();
965 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
968 *((char **)value) = jcr->client_name;
969 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
972 *((char **)value) = jcr->Job;
973 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
976 *((int *)value) = jcr->JobStatus;
977 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
980 *((int *)value) = (int)jcr->mtime;
981 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
984 *((int *)value) = (int)jcr->accurate;
985 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
988 break; /* a write only variable, ignore read request */
992 *(void **)value = g_pVSSClient->GetVssObject();
997 case bVarVssDllHandle:
1000 *(void **)value = g_pVSSClient->GetVssDllHandle();
1005 case bVarWorkingDir:
1006 *(void **)value = me->working_directory;
1012 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1015 if (!value || !ctx) {
1018 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1019 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1023 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1026 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1036 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1045 va_start(args, ctx);
1046 while ((event = va_arg(args, uint32_t))) {
1047 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1053 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1054 int type, utime_t mtime, const char *fmt, ...)
1061 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1066 va_start(arg_ptr, fmt);
1067 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1069 Jmsg(jcr, type, mtime, "%s", buf);
1073 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1074 int level, const char *fmt, ...)
1079 va_start(arg_ptr, fmt);
1080 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1082 d_msg(file, line, level, "%s", buf);
1086 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1090 return sm_malloc(file, line, size);
1092 return malloc(size);
1096 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1099 sm_free(file, line, mem);
1105 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1110 bctx = (bacula_ctx *)ctx->bContext;
1122 * Let the plugin define files/directories to be excluded
1123 * from the main backup.
1125 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1129 if (!is_ctx_good(ctx, jcr, bctx)) {
1135 if (!bctx->exclude) {
1136 bctx->exclude = new_exclude(jcr);
1137 new_options(jcr, bctx->exclude);
1139 set_incexe(jcr, bctx->exclude);
1140 add_file_to_fileset(jcr, file, true);
1141 Dmsg1(100, "Add exclude file=%s\n", file);
1146 * Let the plugin define files/directories to be excluded
1147 * from the main backup.
1149 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1153 if (!is_ctx_good(ctx, jcr, bctx)) {
1159 if (!bctx->include) {
1160 bctx->include = new_preinclude(jcr);
1161 new_options(jcr, bctx->include);
1163 set_incexe(jcr, bctx->include);
1164 add_file_to_fileset(jcr, file, true);
1165 Dmsg1(100, "Add include file=%s\n", file);
1169 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1173 if (!is_ctx_good(ctx, jcr, bctx)) {
1179 add_options_to_fileset(jcr, opts);
1180 Dmsg1(1000, "Add options=%s\n", opts);
1184 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1188 if (!is_ctx_good(ctx, jcr, bctx)) {
1194 add_regex_to_fileset(jcr, item, type);
1195 Dmsg1(100, "Add regex=%s\n", item);
1199 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1203 if (!is_ctx_good(ctx, jcr, bctx)) {
1209 add_wild_to_fileset(jcr, item, type);
1210 Dmsg1(100, "Add wild=%s\n", item);
1214 static bRC baculaNewOptions(bpContext *ctx)
1218 if (!is_ctx_good(ctx, jcr, bctx)) {
1221 (void)new_options(jcr, NULL);
1225 static bRC baculaNewInclude(bpContext *ctx)
1229 if (!is_ctx_good(ctx, jcr, bctx)) {
1232 (void)new_include(jcr);
1239 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1240 int (*plugin_bclose)(JCR *jcr) = NULL;
1241 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1242 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1243 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1245 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1250 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1255 int main(int argc, char *argv[])
1257 char plugin_dir[1000];
1262 strcpy(my_name, "test-fd");
1264 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1265 load_fd_plugins(plugin_dir);
1273 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1274 generate_plugin_event(jcr1, bEventJobEnd);
1275 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1277 generate_plugin_event(jcr2, bEventJobEnd);
1282 Dmsg0(dbglvl, "bacula: OK ...\n");
1283 close_memory_pool();
1284 sm_dump(false); /* unit test */
1288 #endif /* TEST_PROGRAM */