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 bool is_plugin_compatible(Plugin *plugin);
71 * These will be plugged into the global pointer structure for
74 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
75 static int my_plugin_bclose(BFILE *bfd);
76 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
77 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
78 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
82 static bInfo binfo = {
84 FD_PLUGIN_INTERFACE_VERSION
87 /* Bacula entry points */
88 static bFuncs bfuncs = {
90 FD_PLUGIN_INTERFACE_VERSION,
102 * Bacula private context
105 JCR *jcr; /* jcr for plugin */
106 bRC rc; /* last return code */
107 bool disabled; /* set if plugin disabled */
108 findFILESET *fileset; /* pointer to exclude files */
111 static bool is_plugin_disabled(JCR *jcr)
114 if (!jcr->plugin_ctx) {
117 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
118 return b_ctx->disabled;
123 * Create a plugin event
125 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
131 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
132 return; /* Return if no plugins loaded */
135 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
136 event.eventType = eventType;
138 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
140 /* Pass event to every plugin */
141 foreach_alist(plugin, plugin_list) {
143 jcr->plugin_ctx = &plugin_ctx_list[i++];
144 jcr->plugin = plugin;
145 if (is_plugin_disabled(jcr)) {
148 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
155 jcr->plugin_ctx = NULL;
160 * Check if file was seen for accurate
162 bool plugin_check_file(JCR *jcr, char *fname)
168 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
169 return false; /* Return if no plugins loaded */
172 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
174 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
176 /* Pass event to every plugin */
177 foreach_alist(plugin, plugin_list) {
178 jcr->plugin_ctx = &plugin_ctx_list[i++];
179 jcr->plugin = plugin;
180 if (is_plugin_disabled(jcr)) {
183 if (plug_func(plugin)->checkFile == NULL) {
186 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
187 if (rc == bRC_Seen) {
193 jcr->plugin_ctx = NULL;
194 return rc == bRC_Seen;
199 * Sequence of calls for a backup:
200 * 1. plugin_save() here is called with ff_pkt
201 * 2. we find the plugin requested on the command string
202 * 3. we generate a bEventBackupCommand event to the specified plugin
203 * and pass it the command string.
204 * 4. we make a startPluginBackup call to the plugin, which gives
205 * us the data we need in save_pkt
206 * 5. we call Bacula's save_file() subroutine to save the specified
207 * file. The plugin will be called at pluginIO() to supply the
210 * Sequence of calls for restore:
211 * See subroutine plugin_name_stream() below.
213 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
219 char *cmd = ff_pkt->top_fname;
222 POOL_MEM fname(PM_FNAME);
223 POOL_MEM link(PM_FNAME);
225 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
226 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
227 return 1; /* Return if no plugins loaded */
230 jcr->cmd_plugin = true;
231 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
232 event.eventType = bEventBackupCommand;
234 /* Handle plugin command here backup */
235 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
236 if (!(p = strchr(cmd, ':'))) {
237 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
245 /* Note, we stop the loop on the first plugin that matches the name */
246 foreach_alist(plugin, plugin_list) {
247 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
248 if (strncmp(plugin->file, cmd, len) != 0) {
253 * We put the current plugin pointer, and the plugin context
254 * into the jcr, because during save_file(), the plugin
255 * will be called many times and these values are needed.
257 jcr->plugin_ctx = &plugin_ctx_list[i];
258 jcr->plugin = plugin;
259 if (is_plugin_disabled(jcr)) {
263 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
264 /* Send the backup command to the right plugin*/
265 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
268 /* Loop getting filenames to backup then saving them */
269 while (!jcr->is_job_canceled()) {
270 memset(&sp, 0, sizeof(sp));
271 sp.pkt_size = sizeof(sp);
272 sp.pkt_end = sizeof(sp);
275 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
277 /* Get the file save parameters. I.e. the stat pkt ... */
278 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
282 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
286 jcr->plugin_sp = &sp;
289 * Copy fname and link because save_file() zaps them. This
290 * avoids zaping the plugin's strings.
292 ff_pkt->type = sp.type;
293 if (sp.type == FT_RESTORE_FIRST) {
294 if (!sp.object_name) {
295 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
299 ff_pkt->fname = cmd; /* full plugin string */
300 ff_pkt->object_name = sp.object_name;
301 ff_pkt->object_index = sp.index; /* restore object index */
302 ff_pkt->object_compression = 0; /* no compression for now */
303 ff_pkt->object = sp.object;
304 ff_pkt->object_len = sp.object_len;
307 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
311 pm_strcpy(fname, sp.fname);
312 pm_strcpy(link, sp.link);
313 ff_pkt->fname = fname.c_str();
314 ff_pkt->link = link.c_str();
317 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
318 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
320 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
322 /* Call Bacula core code to backup the plugin's file */
323 save_file(jcr, ff_pkt, true);
324 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
325 if (rc == bRC_More || rc == bRC_OK) {
326 accurate_mark_file_as_seen(jcr, fname.c_str());
328 if (rc == bRC_More) {
335 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
338 jcr->cmd_plugin = false;
340 jcr->plugin_ctx = NULL;
345 * Send plugin name start/end record to SD
347 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
350 int index = jcr->JobFiles;
351 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
354 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
357 if (jcr->is_job_canceled()) {
362 index++; /* JobFiles not incremented yet */
364 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
365 /* Send stream header */
366 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
367 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
371 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
374 /* Send data -- not much */
375 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
377 /* Send end of data */
378 stat = sd->fsend("0 0");
381 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
385 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
386 sd->signal(BNET_EOD); /* indicate end of plugin name data */
391 * Plugin name stream found during restore. The record passed in
392 * argument name was generated in send_plugin_name() above.
394 * Returns: true if start of stream
395 * false if end of steam
397 bool plugin_name_stream(JCR *jcr, char *name)
401 bool start, portable;
405 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
407 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
408 skip_nonspaces(&p); /* skip over jcr->JobFiles */
412 /* Start of plugin data */
413 skip_nonspaces(&p); /* skip start/end flag */
415 portable = *p == '1';
416 skip_nonspaces(&p); /* skip portable flag */
421 * End of plugin data, notify plugin, then clear flags
423 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
425 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
427 jcr->plugin_ctx = NULL;
431 if (!plugin_ctx_list) {
436 * After this point, we are dealing with a restore start
439 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
440 if (!(p = strchr(cmd, ':'))) {
441 Jmsg1(jcr, M_ERROR, 0,
442 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
451 * Search for correct plugin as specified on the command
453 foreach_alist(plugin, plugin_list) {
455 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
456 if (strncmp(plugin->file, cmd, len) != 0) {
460 jcr->plugin_ctx = &plugin_ctx_list[i];
461 jcr->plugin = plugin;
462 if (is_plugin_disabled(jcr)) {
465 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
466 event.eventType = bEventRestoreCommand;
467 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
468 &event, cmd) != bRC_OK) {
471 /* ***FIXME**** check error code */
472 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
475 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
482 * Tell the plugin to create the file. Return values are
483 * This is called only during Restore
486 * CF_SKIP -- skip processing this file
487 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
488 * CF_CREATED -- created, but no content to extract (typically directories)
491 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
493 bpContext *plugin_ctx = jcr->plugin_ctx;
494 Plugin *plugin = jcr->plugin;
495 struct restore_pkt rp;
499 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
503 rp.pkt_size = sizeof(rp);
504 rp.pkt_end = sizeof(rp);
505 rp.stream = attr->stream;
506 rp.data_stream = attr->data_stream;
507 rp.type = attr->type;
508 rp.file_index = attr->file_index;
509 rp.LinkFI = attr->LinkFI;
511 rp.statp = attr->statp; /* structure assignment */
512 rp.attrEx = attr->attrEx;
513 rp.ofname = attr->ofname;
514 rp.olname = attr->olname;
515 rp.where = jcr->where;
516 rp.RegexWhere = jcr->RegexWhere;
517 rp.replace = jcr->replace;
518 rp.create_status = CF_ERROR;
519 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
520 rp.stream, rp.type, rp.LinkFI, rp.ofname);
522 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
524 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
526 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
530 if (rp.create_status == CF_ERROR) {
531 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
535 /* Created link or directory? */
536 if (rp.create_status == CF_CREATED) {
537 return rp.create_status; /* yes, no need to bopen */
540 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
541 Dmsg0(dbglvl, "call bopen\n");
542 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
543 Dmsg1(50, "bopen status=%d\n", stat);
546 be.set_errno(bfd->berrno);
547 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
548 attr->ofname, be.bstrerror());
549 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
553 if (!is_bopen(bfd)) {
554 Dmsg0(000, "===== BFD is not open!!!!\n");
560 * Reset the file attributes after all file I/O is done -- this allows
561 * the previous access time/dates to be set properly, and it also allows
562 * us to properly set directory permissions.
563 * Not currently Implemented.
565 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
567 Dmsg0(dbglvl, "plugin_set_attributes\n");
571 pm_strcpy(attr->ofname, "*none*");
576 * Print to file the plugin info.
578 void dump_fd_plugin(Plugin *plugin, FILE *fp)
583 pInfo *info = (pInfo *)plugin->pinfo;
584 fprintf(fp, "\tversion=%d\n", info->version);
585 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
586 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
587 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
588 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
589 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
590 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
594 * This entry point is called internally by Bacula to ensure
595 * that the plugin IO calls come into this code.
597 void load_fd_plugins(const char *plugin_dir)
602 Dmsg0(dbglvl, "plugin dir is NULL\n");
606 plugin_list = New(alist(10, not_owned_by_alist));
607 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
608 is_plugin_compatible)) {
609 /* Either none found, or some error */
610 if (plugin_list->size() == 0) {
613 Dmsg0(dbglvl, "No plugins loaded\n");
618 /* Plug entry points called from findlib */
619 plugin_bopen = my_plugin_bopen;
620 plugin_bclose = my_plugin_bclose;
621 plugin_bread = my_plugin_bread;
622 plugin_bwrite = my_plugin_bwrite;
623 plugin_blseek = my_plugin_blseek;
626 * Verify that the plugin is acceptable, and print information
629 foreach_alist(plugin, plugin_list) {
630 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
631 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
634 dbg_plugin_add_hook(dump_fd_plugin);
638 * Check if a plugin is compatible. Called by the load_plugin function
639 * to allow us to verify the plugin.
641 static bool is_plugin_compatible(Plugin *plugin)
643 pInfo *info = (pInfo *)plugin->pinfo;
644 Dmsg0(50, "is_plugin_compatible called\n");
645 if (debug_level >= 50) {
646 dump_fd_plugin(plugin, stdin);
648 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
649 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
650 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
651 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
652 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
656 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
657 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
658 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
659 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
660 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
663 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
664 strcmp(info->plugin_license, "GPLv2") != 0) {
665 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
666 plugin->file, info->plugin_license);
667 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
668 plugin->file, info->plugin_license);
677 * Create a new instance of each plugin for this Job
678 * Note, plugin_list can exist but jcr->plugin_ctx_list can
679 * be NULL if no plugins were loaded.
681 void new_plugins(JCR *jcr)
687 Dmsg0(dbglvl, "plugin list is NULL\n");
690 if (jcr->is_job_canceled() || jcr->JobId == 0) {
694 int num = plugin_list->size();
697 Dmsg0(dbglvl, "No plugins loaded\n");
701 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
703 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
704 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
705 foreach_alist(plugin, plugin_list) {
706 /* Start a new instance of each plugin */
707 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
708 memset(b_ctx, 0, sizeof(bacula_ctx));
710 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
711 plugin_ctx_list[i].pContext = NULL;
712 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
713 b_ctx->disabled = true;
719 * Free the plugin instances for this Job
721 void free_plugins(JCR *jcr)
726 if (!plugin_list || !jcr->plugin_ctx_list) {
727 return; /* no plugins, nothing to do */
730 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
731 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
732 foreach_alist(plugin, plugin_list) {
733 /* Free the plugin instance */
734 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
735 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
737 free(plugin_ctx_list);
738 jcr->plugin_ctx_list = NULL;
741 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
744 Plugin *plugin = (Plugin *)jcr->plugin;
747 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
748 if (!plugin || !jcr->plugin_ctx) {
751 io.pkt_size = sizeof(io);
752 io.pkt_end = sizeof(io);
761 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
762 bfd->berrno = io.io_errno;
764 errno = b_errno_win32;
767 bfd->lerror = io.lerror;
769 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
773 static int my_plugin_bclose(BFILE *bfd)
776 Plugin *plugin = (Plugin *)jcr->plugin;
779 Dmsg0(dbglvl, "===== plugin_bclose\n");
780 if (!plugin || !jcr->plugin_ctx) {
783 io.pkt_size = sizeof(io);
784 io.pkt_end = sizeof(io);
790 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
791 bfd->berrno = io.io_errno;
793 errno = b_errno_win32;
796 bfd->lerror = io.lerror;
798 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
802 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
805 Plugin *plugin = (Plugin *)jcr->plugin;
808 Dmsg0(dbglvl, "plugin_bread\n");
809 if (!plugin || !jcr->plugin_ctx) {
812 io.pkt_size = sizeof(io);
813 io.pkt_end = sizeof(io);
816 io.buf = (char *)buf;
819 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
820 bfd->berrno = io.io_errno;
822 errno = b_errno_win32;
825 bfd->lerror = io.lerror;
827 return (ssize_t)io.status;
830 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
833 Plugin *plugin = (Plugin *)jcr->plugin;
836 Dmsg0(dbglvl, "plugin_bwrite\n");
837 if (!plugin || !jcr->plugin_ctx) {
840 io.pkt_size = sizeof(io);
841 io.pkt_end = sizeof(io);
844 io.buf = (char *)buf;
847 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
848 bfd->berrno = io.io_errno;
850 errno = b_errno_win32;
853 bfd->lerror = io.lerror;
855 return (ssize_t)io.status;
858 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
861 Plugin *plugin = (Plugin *)jcr->plugin;
864 Dmsg0(dbglvl, "plugin_bseek\n");
865 if (!plugin || !jcr->plugin_ctx) {
868 io.pkt_size = sizeof(io);
869 io.pkt_end = sizeof(io);
875 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
876 bfd->berrno = io.io_errno;
878 errno = b_errno_win32;
881 bfd->lerror = io.lerror;
883 return (boffset_t)io.offset;
886 /* ==============================================================
888 * Callbacks from the plugin
890 * ==============================================================
892 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
895 if (!value || !ctx) {
898 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
899 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
903 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
906 *((int *)value) = jcr->JobId;
907 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
910 *((char **)value) = my_name;
911 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
914 *((int *)value) = jcr->getJobLevel();
915 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
918 *((int *)value) = jcr->getJobType();
919 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
922 *((char **)value) = jcr->client_name;
923 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
926 *((char **)value) = jcr->Job;
927 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
930 *((int *)value) = jcr->JobStatus;
931 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
934 *((int *)value) = (int)jcr->mtime;
935 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
938 *((int *)value) = (int)jcr->accurate;
939 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
942 break; /* a write only variable, ignore read request */
946 *(void **)value = g_pVSSClient->GetVssObject();
951 case bVarVssDllHandle:
954 *(void **)value = g_pVSSClient->GetVssDllHandle();
960 *(void **)value = me->working_directory;
966 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
969 if (!value || !ctx) {
972 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
973 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
977 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
980 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
990 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1000 while ((event = va_arg(args, uint32_t))) {
1001 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1007 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1008 int type, utime_t mtime, const char *fmt, ...)
1015 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1020 va_start(arg_ptr, fmt);
1021 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1023 Jmsg(jcr, type, mtime, "%s", buf);
1027 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1028 int level, const char *fmt, ...)
1033 va_start(arg_ptr, fmt);
1034 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1036 d_msg(file, line, level, "%s", buf);
1040 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1044 return sm_malloc(file, line, size);
1046 return malloc(size);
1050 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1053 sm_free(file, line, mem);
1060 * Let the plugin define files/directories to be excluded
1061 * from the main backup.
1063 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1073 bctx = (bacula_ctx *)ctx->bContext;
1081 if (!bctx->fileset) {
1082 bctx->fileset = new_exclude(jcr);
1084 add_file_to_fileset(jcr, file, bctx->fileset, true);
1091 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1092 int (*plugin_bclose)(JCR *jcr) = NULL;
1093 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1094 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1095 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1097 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1102 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1107 int main(int argc, char *argv[])
1109 char plugin_dir[1000];
1114 strcpy(my_name, "test-fd");
1116 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1117 load_fd_plugins(plugin_dir);
1125 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1126 generate_plugin_event(jcr1, bEventJobEnd);
1127 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1129 generate_plugin_event(jcr2, bEventJobEnd);
1134 Dmsg0(dbglvl, "bacula: OK ...\n");
1135 close_memory_pool();
1140 #endif /* TEST_PROGRAM */