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\" 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_ERROR) {
574 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
578 /* Created link or directory? */
579 if (rp.create_status == CF_CREATED) {
580 return rp.create_status; /* yes, no need to bopen */
583 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
584 Dmsg0(dbglvl, "call bopen\n");
585 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
586 Dmsg1(50, "bopen status=%d\n", stat);
589 be.set_errno(bfd->berrno);
590 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
591 attr->ofname, be.bstrerror());
592 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
596 if (!is_bopen(bfd)) {
597 Dmsg0(000, "===== BFD is not open!!!!\n");
603 * Reset the file attributes after all file I/O is done -- this allows
604 * the previous access time/dates to be set properly, and it also allows
605 * us to properly set directory permissions.
606 * Not currently Implemented.
608 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
610 Dmsg0(dbglvl, "plugin_set_attributes\n");
614 pm_strcpy(attr->ofname, "*none*");
619 * Print to file the plugin info.
621 void dump_fd_plugin(Plugin *plugin, FILE *fp)
626 pInfo *info = (pInfo *)plugin->pinfo;
627 fprintf(fp, "\tversion=%d\n", info->version);
628 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
629 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
630 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
631 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
632 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
633 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
637 * This entry point is called internally by Bacula to ensure
638 * that the plugin IO calls come into this code.
640 void load_fd_plugins(const char *plugin_dir)
645 Dmsg0(dbglvl, "plugin dir is NULL\n");
649 plugin_list = New(alist(10, not_owned_by_alist));
650 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
651 is_plugin_compatible)) {
652 /* Either none found, or some error */
653 if (plugin_list->size() == 0) {
656 Dmsg0(dbglvl, "No plugins loaded\n");
661 /* Plug entry points called from findlib */
662 plugin_bopen = my_plugin_bopen;
663 plugin_bclose = my_plugin_bclose;
664 plugin_bread = my_plugin_bread;
665 plugin_bwrite = my_plugin_bwrite;
666 plugin_blseek = my_plugin_blseek;
669 * Verify that the plugin is acceptable, and print information
672 foreach_alist(plugin, plugin_list) {
673 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
674 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
677 dbg_plugin_add_hook(dump_fd_plugin);
681 * Check if a plugin is compatible. Called by the load_plugin function
682 * to allow us to verify the plugin.
684 static bool is_plugin_compatible(Plugin *plugin)
686 pInfo *info = (pInfo *)plugin->pinfo;
687 Dmsg0(50, "is_plugin_compatible called\n");
688 if (debug_level >= 50) {
689 dump_fd_plugin(plugin, stdin);
691 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
692 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
693 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
694 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
695 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
699 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
700 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
701 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
702 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
703 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
706 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
707 strcmp(info->plugin_license, "AGPLv3") != 0) {
708 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
709 plugin->file, info->plugin_license);
710 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
711 plugin->file, info->plugin_license);
720 * Create a new instance of each plugin for this Job
721 * Note, plugin_list can exist but jcr->plugin_ctx_list can
722 * be NULL if no plugins were loaded.
724 void new_plugins(JCR *jcr)
730 Dmsg0(dbglvl, "plugin list is NULL\n");
733 if (jcr->is_job_canceled() || jcr->JobId == 0) {
737 int num = plugin_list->size();
740 Dmsg0(dbglvl, "No plugins loaded\n");
744 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
746 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
747 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
748 foreach_alist(plugin, plugin_list) {
749 /* Start a new instance of each plugin */
750 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
751 memset(b_ctx, 0, sizeof(bacula_ctx));
753 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
754 plugin_ctx_list[i].pContext = NULL;
755 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
756 b_ctx->disabled = true;
762 * Free the plugin instances for this Job
764 void free_plugins(JCR *jcr)
769 if (!plugin_list || !jcr->plugin_ctx_list) {
770 return; /* no plugins, nothing to do */
773 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
774 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
775 foreach_alist(plugin, plugin_list) {
776 /* Free the plugin instance */
777 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
778 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
780 free(plugin_ctx_list);
781 jcr->plugin_ctx_list = NULL;
784 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
787 Plugin *plugin = (Plugin *)jcr->plugin;
790 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
791 if (!plugin || !jcr->plugin_ctx) {
794 io.pkt_size = sizeof(io);
795 io.pkt_end = sizeof(io);
804 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
805 bfd->berrno = io.io_errno;
807 errno = b_errno_win32;
810 bfd->lerror = io.lerror;
812 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
816 static int my_plugin_bclose(BFILE *bfd)
819 Plugin *plugin = (Plugin *)jcr->plugin;
822 Dmsg0(dbglvl, "===== plugin_bclose\n");
823 if (!plugin || !jcr->plugin_ctx) {
826 io.pkt_size = sizeof(io);
827 io.pkt_end = sizeof(io);
833 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
834 bfd->berrno = io.io_errno;
836 errno = b_errno_win32;
839 bfd->lerror = io.lerror;
841 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
845 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
848 Plugin *plugin = (Plugin *)jcr->plugin;
851 Dmsg0(dbglvl, "plugin_bread\n");
852 if (!plugin || !jcr->plugin_ctx) {
855 io.pkt_size = sizeof(io);
856 io.pkt_end = sizeof(io);
859 io.buf = (char *)buf;
862 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
863 bfd->berrno = io.io_errno;
865 errno = b_errno_win32;
868 bfd->lerror = io.lerror;
870 return (ssize_t)io.status;
873 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
876 Plugin *plugin = (Plugin *)jcr->plugin;
879 Dmsg0(dbglvl, "plugin_bwrite\n");
880 if (!plugin || !jcr->plugin_ctx) {
883 io.pkt_size = sizeof(io);
884 io.pkt_end = sizeof(io);
887 io.buf = (char *)buf;
890 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
891 bfd->berrno = io.io_errno;
893 errno = b_errno_win32;
896 bfd->lerror = io.lerror;
898 return (ssize_t)io.status;
901 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
904 Plugin *plugin = (Plugin *)jcr->plugin;
907 Dmsg0(dbglvl, "plugin_bseek\n");
908 if (!plugin || !jcr->plugin_ctx) {
911 io.pkt_size = sizeof(io);
912 io.pkt_end = sizeof(io);
918 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
919 bfd->berrno = io.io_errno;
921 errno = b_errno_win32;
924 bfd->lerror = io.lerror;
926 return (boffset_t)io.offset;
929 /* ==============================================================
931 * Callbacks from the plugin
933 * ==============================================================
935 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
938 if (!value || !ctx) {
941 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
942 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
946 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
949 *((int *)value) = jcr->JobId;
950 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
953 *((char **)value) = my_name;
954 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
957 *((int *)value) = jcr->getJobLevel();
958 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
961 *((int *)value) = jcr->getJobType();
962 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
965 *((char **)value) = jcr->client_name;
966 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
969 *((char **)value) = jcr->Job;
970 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
973 *((int *)value) = jcr->JobStatus;
974 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
977 *((int *)value) = (int)jcr->mtime;
978 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
981 *((int *)value) = (int)jcr->accurate;
982 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
985 break; /* a write only variable, ignore read request */
989 *(void **)value = g_pVSSClient->GetVssObject();
994 case bVarVssDllHandle:
997 *(void **)value = g_pVSSClient->GetVssDllHandle();
1002 case bVarWorkingDir:
1003 *(void **)value = me->working_directory;
1009 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1012 if (!value || !ctx) {
1015 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1016 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1020 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1023 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1033 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1042 va_start(args, ctx);
1043 while ((event = va_arg(args, uint32_t))) {
1044 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1050 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1051 int type, utime_t mtime, const char *fmt, ...)
1058 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1063 va_start(arg_ptr, fmt);
1064 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1066 Jmsg(jcr, type, mtime, "%s", buf);
1070 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1071 int level, const char *fmt, ...)
1076 va_start(arg_ptr, fmt);
1077 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1079 d_msg(file, line, level, "%s", buf);
1083 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1087 return sm_malloc(file, line, size);
1089 return malloc(size);
1093 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1096 sm_free(file, line, mem);
1102 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1107 bctx = (bacula_ctx *)ctx->bContext;
1119 * Let the plugin define files/directories to be excluded
1120 * from the main backup.
1122 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1126 if (!is_ctx_good(ctx, jcr, bctx)) {
1132 if (!bctx->exclude) {
1133 bctx->exclude = new_exclude(jcr);
1134 new_options(jcr, bctx->exclude);
1136 set_incexe(jcr, bctx->exclude);
1137 add_file_to_fileset(jcr, file, true);
1138 Dmsg1(100, "Add exclude file=%s\n", file);
1143 * Let the plugin define files/directories to be excluded
1144 * from the main backup.
1146 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1150 if (!is_ctx_good(ctx, jcr, bctx)) {
1156 if (!bctx->include) {
1157 bctx->include = new_preinclude(jcr);
1158 new_options(jcr, bctx->include);
1160 set_incexe(jcr, bctx->include);
1161 add_file_to_fileset(jcr, file, true);
1162 Dmsg1(100, "Add include file=%s\n", file);
1166 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1170 if (!is_ctx_good(ctx, jcr, bctx)) {
1176 add_options_to_fileset(jcr, opts);
1177 Dmsg1(1000, "Add options=%s\n", opts);
1181 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1185 if (!is_ctx_good(ctx, jcr, bctx)) {
1191 add_regex_to_fileset(jcr, item, type);
1192 Dmsg1(100, "Add regex=%s\n", item);
1196 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1200 if (!is_ctx_good(ctx, jcr, bctx)) {
1206 add_wild_to_fileset(jcr, item, type);
1207 Dmsg1(100, "Add wild=%s\n", item);
1211 static bRC baculaNewOptions(bpContext *ctx)
1215 if (!is_ctx_good(ctx, jcr, bctx)) {
1218 (void)new_options(jcr, NULL);
1222 static bRC baculaNewInclude(bpContext *ctx)
1226 if (!is_ctx_good(ctx, jcr, bctx)) {
1229 (void)new_include(jcr);
1236 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1237 int (*plugin_bclose)(JCR *jcr) = NULL;
1238 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1239 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1240 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1242 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1247 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1252 int main(int argc, char *argv[])
1254 char plugin_dir[1000];
1259 strcpy(my_name, "test-fd");
1261 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1262 load_fd_plugins(plugin_dir);
1270 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1271 generate_plugin_event(jcr1, bEventJobEnd);
1272 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1274 generate_plugin_event(jcr2, bEventJobEnd);
1279 Dmsg0(dbglvl, "bacula: OK ...\n");
1280 close_memory_pool();
1281 sm_dump(false); /* unit test */
1285 #endif /* TEST_PROGRAM */