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);
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(JCR *jcr)
128 if (!jcr->plugin_ctx) {
131 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
132 return b_ctx->disabled;
137 * Create a plugin event
139 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
148 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
149 return; /* Return if no plugins loaded */
152 /* Some events are sent to only a particular plugin */
154 case bEventPluginCommand:
155 name = (char *)value;
156 if (!get_plugin_name(jcr, name, &len)) {
164 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
165 event.eventType = eventType;
167 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
169 /* Pass event to every plugin (except if name is set) */
170 foreach_alist(plugin, plugin_list) {
171 if (name && strncmp(plugin->file, name, len) != 0) {
175 jcr->plugin_ctx = &plugin_ctx_list[i++];
176 jcr->plugin = plugin;
177 if (is_plugin_disabled(jcr)) {
180 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
187 jcr->plugin_ctx = NULL;
192 * Check if file was seen for accurate
194 bool plugin_check_file(JCR *jcr, char *fname)
200 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
201 return false; /* Return if no plugins loaded */
204 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
206 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
208 /* Pass event to every plugin */
209 foreach_alist(plugin, plugin_list) {
210 jcr->plugin_ctx = &plugin_ctx_list[i++];
211 jcr->plugin = plugin;
212 if (is_plugin_disabled(jcr)) {
215 if (plug_func(plugin)->checkFile == NULL) {
218 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
219 if (rc == bRC_Seen) {
225 jcr->plugin_ctx = NULL;
226 return rc == bRC_Seen;
229 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
236 /* Handle plugin command here backup */
237 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
238 if (!(p = strchr(cmd, ':'))) {
239 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
251 * Sequence of calls for a backup:
252 * 1. plugin_save() here is called with ff_pkt
253 * 2. we find the plugin requested on the command string
254 * 3. we generate a bEventBackupCommand event to the specified plugin
255 * and pass it the command string.
256 * 4. we make a startPluginBackup call to the plugin, which gives
257 * us the data we need in save_pkt
258 * 5. we call Bacula's save_file() subroutine to save the specified
259 * file. The plugin will be called at pluginIO() to supply the
262 * Sequence of calls for restore:
263 * See subroutine plugin_name_stream() below.
265 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
270 char *cmd = ff_pkt->top_fname;
273 POOL_MEM fname(PM_FNAME);
274 POOL_MEM link(PM_FNAME);
276 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
277 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
278 return 1; /* Return if no plugins loaded */
281 jcr->cmd_plugin = true;
282 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
283 event.eventType = bEventBackupCommand;
285 if (!get_plugin_name(jcr, cmd, &len)) {
289 /* Note, we stop the loop on the first plugin that matches the name */
290 foreach_alist(plugin, plugin_list) {
291 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
292 if (strncmp(plugin->file, cmd, len) != 0) {
297 * We put the current plugin pointer, and the plugin context
298 * into the jcr, because during save_file(), the plugin
299 * will be called many times and these values are needed.
301 jcr->plugin_ctx = &plugin_ctx_list[i];
302 jcr->plugin = plugin;
303 if (is_plugin_disabled(jcr)) {
307 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
308 /* Send the backup command to the right plugin*/
309 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
312 /* Loop getting filenames to backup then saving them */
313 while (!jcr->is_job_canceled()) {
314 memset(&sp, 0, sizeof(sp));
315 sp.pkt_size = sizeof(sp);
316 sp.pkt_end = sizeof(sp);
319 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
321 /* Get the file save parameters. I.e. the stat pkt ... */
322 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
326 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
330 jcr->plugin_sp = &sp;
333 * Copy fname and link because save_file() zaps them. This
334 * avoids zaping the plugin's strings.
336 ff_pkt->type = sp.type;
337 if (sp.type == FT_RESTORE_FIRST) {
338 if (!sp.object_name) {
339 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
343 ff_pkt->fname = cmd; /* full plugin string */
344 ff_pkt->object_name = sp.object_name;
345 ff_pkt->object_index = sp.index; /* restore object index */
346 ff_pkt->object_compression = 0; /* no compression for now */
347 ff_pkt->object = sp.object;
348 ff_pkt->object_len = sp.object_len;
351 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
355 pm_strcpy(fname, sp.fname);
356 pm_strcpy(link, sp.link);
357 ff_pkt->fname = fname.c_str();
358 ff_pkt->link = link.c_str();
361 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
362 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
364 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
366 /* Call Bacula core code to backup the plugin's file */
367 save_file(jcr, ff_pkt, true);
368 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
369 if (rc == bRC_More || rc == bRC_OK) {
370 accurate_mark_file_as_seen(jcr, fname.c_str());
372 if (rc == bRC_More) {
379 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
382 jcr->cmd_plugin = false;
384 jcr->plugin_ctx = NULL;
389 * Send plugin name start/end record to SD
391 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
394 int index = jcr->JobFiles;
395 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
398 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
401 if (jcr->is_job_canceled()) {
406 index++; /* JobFiles not incremented yet */
408 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
409 /* Send stream header */
410 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
411 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
415 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
418 /* Send data -- not much */
419 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
421 /* Send end of data */
422 stat = sd->fsend("0 0");
425 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
429 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
430 sd->signal(BNET_EOD); /* indicate end of plugin name data */
435 * Plugin name stream found during restore. The record passed in
436 * argument name was generated in send_plugin_name() above.
438 * Returns: true if start of stream
439 * false if end of steam
441 bool plugin_name_stream(JCR *jcr, char *name)
445 bool start, portable;
449 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
451 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
452 skip_nonspaces(&p); /* skip over jcr->JobFiles */
456 /* Start of plugin data */
457 skip_nonspaces(&p); /* skip start/end flag */
459 portable = *p == '1';
460 skip_nonspaces(&p); /* skip portable flag */
465 * End of plugin data, notify plugin, then clear flags
467 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
469 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
471 jcr->plugin_ctx = NULL;
475 if (!plugin_ctx_list) {
480 * After this point, we are dealing with a restore start
483 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
484 if (!(p = strchr(cmd, ':'))) {
485 Jmsg1(jcr, M_ERROR, 0,
486 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
495 * Search for correct plugin as specified on the command
497 foreach_alist(plugin, plugin_list) {
499 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
500 if (strncmp(plugin->file, cmd, len) != 0) {
504 jcr->plugin_ctx = &plugin_ctx_list[i];
505 jcr->plugin = plugin;
506 if (is_plugin_disabled(jcr)) {
509 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
510 event.eventType = bEventRestoreCommand;
511 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
512 &event, cmd) != bRC_OK) {
515 /* ***FIXME**** check error code */
516 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
519 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
526 * Tell the plugin to create the file. Return values are
527 * This is called only during Restore
530 * CF_SKIP -- skip processing this file
531 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
532 * CF_CREATED -- created, but no content to extract (typically directories)
535 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
537 bpContext *plugin_ctx = jcr->plugin_ctx;
538 Plugin *plugin = jcr->plugin;
539 struct restore_pkt rp;
543 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
547 rp.pkt_size = sizeof(rp);
548 rp.pkt_end = sizeof(rp);
549 rp.stream = attr->stream;
550 rp.data_stream = attr->data_stream;
551 rp.type = attr->type;
552 rp.file_index = attr->file_index;
553 rp.LinkFI = attr->LinkFI;
555 rp.statp = attr->statp; /* structure assignment */
556 rp.attrEx = attr->attrEx;
557 rp.ofname = attr->ofname;
558 rp.olname = attr->olname;
559 rp.where = jcr->where;
560 rp.RegexWhere = jcr->RegexWhere;
561 rp.replace = jcr->replace;
562 rp.create_status = CF_ERROR;
563 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
564 rp.stream, rp.type, rp.LinkFI, rp.ofname);
566 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
568 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
570 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
574 if (rp.create_status == CF_ERROR) {
575 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
579 /* Created link or directory? */
580 if (rp.create_status == CF_CREATED) {
581 return rp.create_status; /* yes, no need to bopen */
584 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
585 Dmsg0(dbglvl, "call bopen\n");
586 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
587 Dmsg1(50, "bopen status=%d\n", stat);
590 be.set_errno(bfd->berrno);
591 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
592 attr->ofname, be.bstrerror());
593 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
597 if (!is_bopen(bfd)) {
598 Dmsg0(000, "===== BFD is not open!!!!\n");
604 * Reset the file attributes after all file I/O is done -- this allows
605 * the previous access time/dates to be set properly, and it also allows
606 * us to properly set directory permissions.
607 * Not currently Implemented.
609 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
611 Dmsg0(dbglvl, "plugin_set_attributes\n");
615 pm_strcpy(attr->ofname, "*none*");
620 * Print to file the plugin info.
622 void dump_fd_plugin(Plugin *plugin, FILE *fp)
627 pInfo *info = (pInfo *)plugin->pinfo;
628 fprintf(fp, "\tversion=%d\n", info->version);
629 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
630 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
631 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
632 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
633 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
634 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
638 * This entry point is called internally by Bacula to ensure
639 * that the plugin IO calls come into this code.
641 void load_fd_plugins(const char *plugin_dir)
646 Dmsg0(dbglvl, "plugin dir is NULL\n");
650 plugin_list = New(alist(10, not_owned_by_alist));
651 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
652 is_plugin_compatible)) {
653 /* Either none found, or some error */
654 if (plugin_list->size() == 0) {
657 Dmsg0(dbglvl, "No plugins loaded\n");
662 /* Plug entry points called from findlib */
663 plugin_bopen = my_plugin_bopen;
664 plugin_bclose = my_plugin_bclose;
665 plugin_bread = my_plugin_bread;
666 plugin_bwrite = my_plugin_bwrite;
667 plugin_blseek = my_plugin_blseek;
670 * Verify that the plugin is acceptable, and print information
673 foreach_alist(plugin, plugin_list) {
674 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
675 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
678 dbg_plugin_add_hook(dump_fd_plugin);
682 * Check if a plugin is compatible. Called by the load_plugin function
683 * to allow us to verify the plugin.
685 static bool is_plugin_compatible(Plugin *plugin)
687 pInfo *info = (pInfo *)plugin->pinfo;
688 Dmsg0(50, "is_plugin_compatible called\n");
689 if (debug_level >= 50) {
690 dump_fd_plugin(plugin, stdin);
692 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
693 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
694 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
695 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
696 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
700 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
701 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
702 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
703 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
704 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
707 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
708 strcmp(info->plugin_license, "GPLv2") != 0) {
709 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
710 plugin->file, info->plugin_license);
711 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
712 plugin->file, info->plugin_license);
721 * Create a new instance of each plugin for this Job
722 * Note, plugin_list can exist but jcr->plugin_ctx_list can
723 * be NULL if no plugins were loaded.
725 void new_plugins(JCR *jcr)
731 Dmsg0(dbglvl, "plugin list is NULL\n");
734 if (jcr->is_job_canceled() || jcr->JobId == 0) {
738 int num = plugin_list->size();
741 Dmsg0(dbglvl, "No plugins loaded\n");
745 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
747 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
748 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
749 foreach_alist(plugin, plugin_list) {
750 /* Start a new instance of each plugin */
751 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
752 memset(b_ctx, 0, sizeof(bacula_ctx));
754 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
755 plugin_ctx_list[i].pContext = NULL;
756 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
757 b_ctx->disabled = true;
763 * Free the plugin instances for this Job
765 void free_plugins(JCR *jcr)
770 if (!plugin_list || !jcr->plugin_ctx_list) {
771 return; /* no plugins, nothing to do */
774 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
775 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
776 foreach_alist(plugin, plugin_list) {
777 /* Free the plugin instance */
778 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
779 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
781 free(plugin_ctx_list);
782 jcr->plugin_ctx_list = NULL;
785 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
788 Plugin *plugin = (Plugin *)jcr->plugin;
791 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
792 if (!plugin || !jcr->plugin_ctx) {
795 io.pkt_size = sizeof(io);
796 io.pkt_end = sizeof(io);
805 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
806 bfd->berrno = io.io_errno;
808 errno = b_errno_win32;
811 bfd->lerror = io.lerror;
813 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
817 static int my_plugin_bclose(BFILE *bfd)
820 Plugin *plugin = (Plugin *)jcr->plugin;
823 Dmsg0(dbglvl, "===== plugin_bclose\n");
824 if (!plugin || !jcr->plugin_ctx) {
827 io.pkt_size = sizeof(io);
828 io.pkt_end = sizeof(io);
834 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
835 bfd->berrno = io.io_errno;
837 errno = b_errno_win32;
840 bfd->lerror = io.lerror;
842 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
846 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
849 Plugin *plugin = (Plugin *)jcr->plugin;
852 Dmsg0(dbglvl, "plugin_bread\n");
853 if (!plugin || !jcr->plugin_ctx) {
856 io.pkt_size = sizeof(io);
857 io.pkt_end = sizeof(io);
860 io.buf = (char *)buf;
863 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
864 bfd->berrno = io.io_errno;
866 errno = b_errno_win32;
869 bfd->lerror = io.lerror;
871 return (ssize_t)io.status;
874 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
877 Plugin *plugin = (Plugin *)jcr->plugin;
880 Dmsg0(dbglvl, "plugin_bwrite\n");
881 if (!plugin || !jcr->plugin_ctx) {
884 io.pkt_size = sizeof(io);
885 io.pkt_end = sizeof(io);
888 io.buf = (char *)buf;
891 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
892 bfd->berrno = io.io_errno;
894 errno = b_errno_win32;
897 bfd->lerror = io.lerror;
899 return (ssize_t)io.status;
902 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
905 Plugin *plugin = (Plugin *)jcr->plugin;
908 Dmsg0(dbglvl, "plugin_bseek\n");
909 if (!plugin || !jcr->plugin_ctx) {
912 io.pkt_size = sizeof(io);
913 io.pkt_end = sizeof(io);
919 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
920 bfd->berrno = io.io_errno;
922 errno = b_errno_win32;
925 bfd->lerror = io.lerror;
927 return (boffset_t)io.offset;
930 /* ==============================================================
932 * Callbacks from the plugin
934 * ==============================================================
936 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
939 if (!value || !ctx) {
942 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
943 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
947 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
950 *((int *)value) = jcr->JobId;
951 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
954 *((char **)value) = my_name;
955 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
958 *((int *)value) = jcr->getJobLevel();
959 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
962 *((int *)value) = jcr->getJobType();
963 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
966 *((char **)value) = jcr->client_name;
967 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
970 *((char **)value) = jcr->Job;
971 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
974 *((int *)value) = jcr->JobStatus;
975 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
978 *((int *)value) = (int)jcr->mtime;
979 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
982 *((int *)value) = (int)jcr->accurate;
983 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
986 break; /* a write only variable, ignore read request */
990 *(void **)value = g_pVSSClient->GetVssObject();
995 case bVarVssDllHandle:
998 *(void **)value = g_pVSSClient->GetVssDllHandle();
1003 case bVarWorkingDir:
1004 *(void **)value = me->working_directory;
1010 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1013 if (!value || !ctx) {
1016 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1017 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1021 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1024 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1034 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1043 va_start(args, ctx);
1044 while ((event = va_arg(args, uint32_t))) {
1045 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1051 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1052 int type, utime_t mtime, const char *fmt, ...)
1059 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1064 va_start(arg_ptr, fmt);
1065 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1067 Jmsg(jcr, type, mtime, "%s", buf);
1071 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1072 int level, const char *fmt, ...)
1077 va_start(arg_ptr, fmt);
1078 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1080 d_msg(file, line, level, "%s", buf);
1084 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1088 return sm_malloc(file, line, size);
1090 return malloc(size);
1094 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1097 sm_free(file, line, mem);
1103 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1108 bctx = (bacula_ctx *)ctx->bContext;
1120 * Let the plugin define files/directories to be excluded
1121 * from the main backup.
1123 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1127 if (!is_ctx_good(ctx, jcr, bctx)) {
1133 if (!bctx->exclude) {
1134 bctx->exclude = new_exclude(jcr);
1135 new_options(jcr, bctx->exclude);
1137 set_incexe(jcr, bctx->exclude);
1138 add_file_to_fileset(jcr, file, true);
1139 Dmsg1(100, "Add exclude file=%s\n", file);
1144 * Let the plugin define files/directories to be excluded
1145 * from the main backup.
1147 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1151 if (!is_ctx_good(ctx, jcr, bctx)) {
1157 if (!bctx->include) {
1158 bctx->include = new_preinclude(jcr);
1159 new_options(jcr, bctx->include);
1161 set_incexe(jcr, bctx->include);
1162 add_file_to_fileset(jcr, file, true);
1163 Dmsg1(100, "Add include file=%s\n", file);
1167 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1171 if (!is_ctx_good(ctx, jcr, bctx)) {
1177 add_options_to_fileset(jcr, opts);
1178 Dmsg1(1000, "Add options=%s\n", opts);
1182 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1186 if (!is_ctx_good(ctx, jcr, bctx)) {
1192 add_regex_to_fileset(jcr, item, type);
1193 Dmsg1(100, "Add regex=%s\n", item);
1197 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1201 if (!is_ctx_good(ctx, jcr, bctx)) {
1207 add_wild_to_fileset(jcr, item, type);
1208 Dmsg1(100, "Add wild=%s\n", item);
1212 static bRC baculaNewOptions(bpContext *ctx)
1216 if (!is_ctx_good(ctx, jcr, bctx)) {
1219 (void)new_options(jcr, NULL);
1223 static bRC baculaNewInclude(bpContext *ctx)
1227 if (!is_ctx_good(ctx, jcr, bctx)) {
1230 (void)new_include(jcr);
1237 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1238 int (*plugin_bclose)(JCR *jcr) = NULL;
1239 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1240 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1241 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1243 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1248 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1253 int main(int argc, char *argv[])
1255 char plugin_dir[1000];
1260 strcpy(my_name, "test-fd");
1262 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1263 load_fd_plugins(plugin_dir);
1271 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1272 generate_plugin_event(jcr1, bEventJobEnd);
1273 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1275 generate_plugin_event(jcr2, bEventJobEnd);
1280 Dmsg0(dbglvl, "bacula: OK ...\n");
1281 close_memory_pool();
1286 #endif /* TEST_PROGRAM */