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 two of the GNU 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 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);
77 * These will be plugged into the global pointer structure for
80 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
81 static int my_plugin_bclose(BFILE *bfd);
82 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
83 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
84 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
88 static bInfo binfo = {
90 FD_PLUGIN_INTERFACE_VERSION
93 /* Bacula entry points */
94 static bFuncs bfuncs = {
96 FD_PLUGIN_INTERFACE_VERSION,
114 * Bacula private context
117 JCR *jcr; /* jcr for plugin */
118 bRC rc; /* last return code */
119 bool disabled; /* set if plugin disabled */
120 findINCEXE *exclude; /* pointer to exclude files */
121 findINCEXE *include; /* pointer to include/exclude files */
124 static bool is_plugin_disabled(JCR *jcr)
127 if (!jcr->plugin_ctx) {
130 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
131 return b_ctx->disabled;
136 * Create a plugin event
138 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
144 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
145 return; /* Return if no plugins loaded */
148 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
149 event.eventType = eventType;
151 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
153 /* Pass event to every plugin */
154 foreach_alist(plugin, plugin_list) {
156 jcr->plugin_ctx = &plugin_ctx_list[i++];
157 jcr->plugin = plugin;
158 if (is_plugin_disabled(jcr)) {
161 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
168 jcr->plugin_ctx = NULL;
173 * Check if file was seen for accurate
175 bool plugin_check_file(JCR *jcr, char *fname)
181 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
182 return false; /* Return if no plugins loaded */
185 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
187 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
189 /* Pass event to every plugin */
190 foreach_alist(plugin, plugin_list) {
191 jcr->plugin_ctx = &plugin_ctx_list[i++];
192 jcr->plugin = plugin;
193 if (is_plugin_disabled(jcr)) {
196 if (plug_func(plugin)->checkFile == NULL) {
199 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
200 if (rc == bRC_Seen) {
206 jcr->plugin_ctx = NULL;
207 return rc == bRC_Seen;
212 * Sequence of calls for a backup:
213 * 1. plugin_save() here is called with ff_pkt
214 * 2. we find the plugin requested on the command string
215 * 3. we generate a bEventBackupCommand event to the specified plugin
216 * and pass it the command string.
217 * 4. we make a startPluginBackup call to the plugin, which gives
218 * us the data we need in save_pkt
219 * 5. we call Bacula's save_file() subroutine to save the specified
220 * file. The plugin will be called at pluginIO() to supply the
223 * Sequence of calls for restore:
224 * See subroutine plugin_name_stream() below.
226 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
232 char *cmd = ff_pkt->top_fname;
235 POOL_MEM fname(PM_FNAME);
236 POOL_MEM link(PM_FNAME);
238 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
239 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
240 return 1; /* Return if no plugins loaded */
243 jcr->cmd_plugin = true;
244 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
245 event.eventType = bEventBackupCommand;
247 /* Handle plugin command here backup */
248 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
249 if (!(p = strchr(cmd, ':'))) {
250 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
258 /* Note, we stop the loop on the first plugin that matches the name */
259 foreach_alist(plugin, plugin_list) {
260 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
261 if (strncmp(plugin->file, cmd, len) != 0) {
266 * We put the current plugin pointer, and the plugin context
267 * into the jcr, because during save_file(), the plugin
268 * will be called many times and these values are needed.
270 jcr->plugin_ctx = &plugin_ctx_list[i];
271 jcr->plugin = plugin;
272 if (is_plugin_disabled(jcr)) {
276 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
277 /* Send the backup command to the right plugin*/
278 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
281 /* Loop getting filenames to backup then saving them */
282 while (!jcr->is_job_canceled()) {
283 memset(&sp, 0, sizeof(sp));
284 sp.pkt_size = sizeof(sp);
285 sp.pkt_end = sizeof(sp);
288 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
290 /* Get the file save parameters. I.e. the stat pkt ... */
291 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
295 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
299 jcr->plugin_sp = &sp;
302 * Copy fname and link because save_file() zaps them. This
303 * avoids zaping the plugin's strings.
305 ff_pkt->type = sp.type;
306 if (sp.type == FT_RESTORE_FIRST) {
307 if (!sp.object_name) {
308 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
312 ff_pkt->fname = cmd; /* full plugin string */
313 ff_pkt->object_name = sp.object_name;
314 ff_pkt->object_index = sp.index; /* restore object index */
315 ff_pkt->object_compression = 0; /* no compression for now */
316 ff_pkt->object = sp.object;
317 ff_pkt->object_len = sp.object_len;
320 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
324 pm_strcpy(fname, sp.fname);
325 pm_strcpy(link, sp.link);
326 ff_pkt->fname = fname.c_str();
327 ff_pkt->link = link.c_str();
330 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
331 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
333 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
335 /* Call Bacula core code to backup the plugin's file */
336 save_file(jcr, ff_pkt, true);
337 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
338 if (rc == bRC_More || rc == bRC_OK) {
339 accurate_mark_file_as_seen(jcr, fname.c_str());
341 if (rc == bRC_More) {
348 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
351 jcr->cmd_plugin = false;
353 jcr->plugin_ctx = NULL;
358 * Send plugin name start/end record to SD
360 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
363 int index = jcr->JobFiles;
364 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
367 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
370 if (jcr->is_job_canceled()) {
375 index++; /* JobFiles not incremented yet */
377 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
378 /* Send stream header */
379 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
380 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
384 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
387 /* Send data -- not much */
388 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
390 /* Send end of data */
391 stat = sd->fsend("0 0");
394 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
398 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
399 sd->signal(BNET_EOD); /* indicate end of plugin name data */
404 * Plugin name stream found during restore. The record passed in
405 * argument name was generated in send_plugin_name() above.
407 * Returns: true if start of stream
408 * false if end of steam
410 bool plugin_name_stream(JCR *jcr, char *name)
414 bool start, portable;
418 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
420 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
421 skip_nonspaces(&p); /* skip over jcr->JobFiles */
425 /* Start of plugin data */
426 skip_nonspaces(&p); /* skip start/end flag */
428 portable = *p == '1';
429 skip_nonspaces(&p); /* skip portable flag */
434 * End of plugin data, notify plugin, then clear flags
436 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
438 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
440 jcr->plugin_ctx = NULL;
444 if (!plugin_ctx_list) {
449 * After this point, we are dealing with a restore start
452 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
453 if (!(p = strchr(cmd, ':'))) {
454 Jmsg1(jcr, M_ERROR, 0,
455 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
464 * Search for correct plugin as specified on the command
466 foreach_alist(plugin, plugin_list) {
468 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
469 if (strncmp(plugin->file, cmd, len) != 0) {
473 jcr->plugin_ctx = &plugin_ctx_list[i];
474 jcr->plugin = plugin;
475 if (is_plugin_disabled(jcr)) {
478 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
479 event.eventType = bEventRestoreCommand;
480 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
481 &event, cmd) != bRC_OK) {
484 /* ***FIXME**** check error code */
485 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
488 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
495 * Tell the plugin to create the file. Return values are
496 * This is called only during Restore
499 * CF_SKIP -- skip processing this file
500 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
501 * CF_CREATED -- created, but no content to extract (typically directories)
504 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
506 bpContext *plugin_ctx = jcr->plugin_ctx;
507 Plugin *plugin = jcr->plugin;
508 struct restore_pkt rp;
512 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
516 rp.pkt_size = sizeof(rp);
517 rp.pkt_end = sizeof(rp);
518 rp.stream = attr->stream;
519 rp.data_stream = attr->data_stream;
520 rp.type = attr->type;
521 rp.file_index = attr->file_index;
522 rp.LinkFI = attr->LinkFI;
524 rp.statp = attr->statp; /* structure assignment */
525 rp.attrEx = attr->attrEx;
526 rp.ofname = attr->ofname;
527 rp.olname = attr->olname;
528 rp.where = jcr->where;
529 rp.RegexWhere = jcr->RegexWhere;
530 rp.replace = jcr->replace;
531 rp.create_status = CF_ERROR;
532 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
533 rp.stream, rp.type, rp.LinkFI, rp.ofname);
535 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
537 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
539 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
543 if (rp.create_status == CF_ERROR) {
544 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
548 /* Created link or directory? */
549 if (rp.create_status == CF_CREATED) {
550 return rp.create_status; /* yes, no need to bopen */
553 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
554 Dmsg0(dbglvl, "call bopen\n");
555 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
556 Dmsg1(50, "bopen status=%d\n", stat);
559 be.set_errno(bfd->berrno);
560 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
561 attr->ofname, be.bstrerror());
562 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
566 if (!is_bopen(bfd)) {
567 Dmsg0(000, "===== BFD is not open!!!!\n");
573 * Reset the file attributes after all file I/O is done -- this allows
574 * the previous access time/dates to be set properly, and it also allows
575 * us to properly set directory permissions.
576 * Not currently Implemented.
578 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
580 Dmsg0(dbglvl, "plugin_set_attributes\n");
584 pm_strcpy(attr->ofname, "*none*");
589 * Print to file the plugin info.
591 void dump_fd_plugin(Plugin *plugin, FILE *fp)
596 pInfo *info = (pInfo *)plugin->pinfo;
597 fprintf(fp, "\tversion=%d\n", info->version);
598 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
599 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
600 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
601 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
602 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
603 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
607 * This entry point is called internally by Bacula to ensure
608 * that the plugin IO calls come into this code.
610 void load_fd_plugins(const char *plugin_dir)
615 Dmsg0(dbglvl, "plugin dir is NULL\n");
619 plugin_list = New(alist(10, not_owned_by_alist));
620 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
621 is_plugin_compatible)) {
622 /* Either none found, or some error */
623 if (plugin_list->size() == 0) {
626 Dmsg0(dbglvl, "No plugins loaded\n");
631 /* Plug entry points called from findlib */
632 plugin_bopen = my_plugin_bopen;
633 plugin_bclose = my_plugin_bclose;
634 plugin_bread = my_plugin_bread;
635 plugin_bwrite = my_plugin_bwrite;
636 plugin_blseek = my_plugin_blseek;
639 * Verify that the plugin is acceptable, and print information
642 foreach_alist(plugin, plugin_list) {
643 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
644 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
647 dbg_plugin_add_hook(dump_fd_plugin);
651 * Check if a plugin is compatible. Called by the load_plugin function
652 * to allow us to verify the plugin.
654 static bool is_plugin_compatible(Plugin *plugin)
656 pInfo *info = (pInfo *)plugin->pinfo;
657 Dmsg0(50, "is_plugin_compatible called\n");
658 if (debug_level >= 50) {
659 dump_fd_plugin(plugin, stdin);
661 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
662 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
663 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
664 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
665 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
669 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
670 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
671 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
672 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
673 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
676 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
677 strcmp(info->plugin_license, "GPLv2") != 0) {
678 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
679 plugin->file, info->plugin_license);
680 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
681 plugin->file, info->plugin_license);
690 * Create a new instance of each plugin for this Job
691 * Note, plugin_list can exist but jcr->plugin_ctx_list can
692 * be NULL if no plugins were loaded.
694 void new_plugins(JCR *jcr)
700 Dmsg0(dbglvl, "plugin list is NULL\n");
703 if (jcr->is_job_canceled() || jcr->JobId == 0) {
707 int num = plugin_list->size();
710 Dmsg0(dbglvl, "No plugins loaded\n");
714 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
716 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
717 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
718 foreach_alist(plugin, plugin_list) {
719 /* Start a new instance of each plugin */
720 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
721 memset(b_ctx, 0, sizeof(bacula_ctx));
723 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
724 plugin_ctx_list[i].pContext = NULL;
725 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
726 b_ctx->disabled = true;
732 * Free the plugin instances for this Job
734 void free_plugins(JCR *jcr)
739 if (!plugin_list || !jcr->plugin_ctx_list) {
740 return; /* no plugins, nothing to do */
743 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
744 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
745 foreach_alist(plugin, plugin_list) {
746 /* Free the plugin instance */
747 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
748 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
750 free(plugin_ctx_list);
751 jcr->plugin_ctx_list = NULL;
754 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
757 Plugin *plugin = (Plugin *)jcr->plugin;
760 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
761 if (!plugin || !jcr->plugin_ctx) {
764 io.pkt_size = sizeof(io);
765 io.pkt_end = sizeof(io);
774 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
775 bfd->berrno = io.io_errno;
777 errno = b_errno_win32;
780 bfd->lerror = io.lerror;
782 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
786 static int my_plugin_bclose(BFILE *bfd)
789 Plugin *plugin = (Plugin *)jcr->plugin;
792 Dmsg0(dbglvl, "===== plugin_bclose\n");
793 if (!plugin || !jcr->plugin_ctx) {
796 io.pkt_size = sizeof(io);
797 io.pkt_end = sizeof(io);
803 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
804 bfd->berrno = io.io_errno;
806 errno = b_errno_win32;
809 bfd->lerror = io.lerror;
811 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
815 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
818 Plugin *plugin = (Plugin *)jcr->plugin;
821 Dmsg0(dbglvl, "plugin_bread\n");
822 if (!plugin || !jcr->plugin_ctx) {
825 io.pkt_size = sizeof(io);
826 io.pkt_end = sizeof(io);
829 io.buf = (char *)buf;
832 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
833 bfd->berrno = io.io_errno;
835 errno = b_errno_win32;
838 bfd->lerror = io.lerror;
840 return (ssize_t)io.status;
843 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
846 Plugin *plugin = (Plugin *)jcr->plugin;
849 Dmsg0(dbglvl, "plugin_bwrite\n");
850 if (!plugin || !jcr->plugin_ctx) {
853 io.pkt_size = sizeof(io);
854 io.pkt_end = sizeof(io);
857 io.buf = (char *)buf;
860 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
861 bfd->berrno = io.io_errno;
863 errno = b_errno_win32;
866 bfd->lerror = io.lerror;
868 return (ssize_t)io.status;
871 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
874 Plugin *plugin = (Plugin *)jcr->plugin;
877 Dmsg0(dbglvl, "plugin_bseek\n");
878 if (!plugin || !jcr->plugin_ctx) {
881 io.pkt_size = sizeof(io);
882 io.pkt_end = sizeof(io);
888 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
889 bfd->berrno = io.io_errno;
891 errno = b_errno_win32;
894 bfd->lerror = io.lerror;
896 return (boffset_t)io.offset;
899 /* ==============================================================
901 * Callbacks from the plugin
903 * ==============================================================
905 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
908 if (!value || !ctx) {
911 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
912 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
916 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
919 *((int *)value) = jcr->JobId;
920 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
923 *((char **)value) = my_name;
924 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
927 *((int *)value) = jcr->getJobLevel();
928 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
931 *((int *)value) = jcr->getJobType();
932 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
935 *((char **)value) = jcr->client_name;
936 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
939 *((char **)value) = jcr->Job;
940 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
943 *((int *)value) = jcr->JobStatus;
944 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
947 *((int *)value) = (int)jcr->mtime;
948 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
951 *((int *)value) = (int)jcr->accurate;
952 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
955 break; /* a write only variable, ignore read request */
959 *(void **)value = g_pVSSClient->GetVssObject();
964 case bVarVssDllHandle:
967 *(void **)value = g_pVSSClient->GetVssDllHandle();
973 *(void **)value = me->working_directory;
979 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
982 if (!value || !ctx) {
985 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
986 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
990 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
993 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1003 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1012 va_start(args, ctx);
1013 while ((event = va_arg(args, uint32_t))) {
1014 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1020 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1021 int type, utime_t mtime, const char *fmt, ...)
1028 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1033 va_start(arg_ptr, fmt);
1034 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1036 Jmsg(jcr, type, mtime, "%s", buf);
1040 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1041 int level, const char *fmt, ...)
1046 va_start(arg_ptr, fmt);
1047 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1049 d_msg(file, line, level, "%s", buf);
1053 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1057 return sm_malloc(file, line, size);
1059 return malloc(size);
1063 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1066 sm_free(file, line, mem);
1072 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1077 bctx = (bacula_ctx *)ctx->bContext;
1089 * Let the plugin define files/directories to be excluded
1090 * from the main backup.
1092 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1096 if (!is_ctx_good(ctx, jcr, bctx)) {
1102 if (!bctx->exclude) {
1103 bctx->exclude = new_exclude(jcr);
1104 new_options(jcr, bctx->exclude);
1106 set_incexe(jcr, bctx->exclude);
1107 add_file_to_fileset(jcr, file, true);
1108 Dmsg1(100, "Add exclude file=%s\n", file);
1113 * Let the plugin define files/directories to be excluded
1114 * from the main backup.
1116 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1120 if (!is_ctx_good(ctx, jcr, bctx)) {
1126 if (!bctx->include) {
1127 bctx->include = new_preinclude(jcr);
1128 new_options(jcr, bctx->include);
1130 set_incexe(jcr, bctx->include);
1131 add_file_to_fileset(jcr, file, true);
1132 Dmsg1(100, "Add include file=%s\n", file);
1136 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1140 if (!is_ctx_good(ctx, jcr, bctx)) {
1146 add_options_to_fileset(jcr, opts);
1147 Dmsg1(1000, "Add options=%s\n", opts);
1151 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1155 if (!is_ctx_good(ctx, jcr, bctx)) {
1161 add_regex_to_fileset(jcr, item, type);
1162 Dmsg1(100, "Add regex=%s\n", item);
1166 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1170 if (!is_ctx_good(ctx, jcr, bctx)) {
1176 add_wild_to_fileset(jcr, item, type);
1177 Dmsg1(100, "Add wild=%s\n", item);
1181 static bRC baculaNewOptions(bpContext *ctx)
1185 if (!is_ctx_good(ctx, jcr, bctx)) {
1188 (void)new_options(jcr, NULL);
1192 static bRC baculaNewInclude(bpContext *ctx)
1196 if (!is_ctx_good(ctx, jcr, bctx)) {
1199 (void)new_include(jcr);
1206 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1207 int (*plugin_bclose)(JCR *jcr) = NULL;
1208 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1209 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1210 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1212 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1217 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1222 int main(int argc, char *argv[])
1224 char plugin_dir[1000];
1229 strcpy(my_name, "test-fd");
1231 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1232 load_fd_plugins(plugin_dir);
1240 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1241 generate_plugin_event(jcr1, bEventJobEnd);
1242 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1244 generate_plugin_event(jcr2, bEventJobEnd);
1249 Dmsg0(dbglvl, "bacula: OK ...\n");
1250 close_memory_pool();
1255 #endif /* TEST_PROGRAM */