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
37 const int dbglvl = 150;
39 const char *plugin_type = "-fd.dll";
41 const char *plugin_type = "-fd.so";
44 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
46 /* Function pointers to be set here */
47 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
48 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
49 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
50 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
51 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
54 /* Forward referenced functions */
55 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
56 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
57 static bRC baculaRegisterEvents(bpContext *ctx, ...);
58 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
59 int type, utime_t mtime, const char *fmt, ...);
60 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
61 int level, const char *fmt, ...);
62 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
64 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
65 static bRC baculaAddExclude(bpContext *ctx, const char *file);
66 static bool is_plugin_compatible(Plugin *plugin);
69 * These will be plugged into the global pointer structure for
72 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
73 static int my_plugin_bclose(BFILE *bfd);
74 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
75 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
76 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
80 static bInfo binfo = {
82 FD_PLUGIN_INTERFACE_VERSION
85 /* Bacula entry points */
86 static bFuncs bfuncs = {
88 FD_PLUGIN_INTERFACE_VERSION,
100 * Bacula private context
103 JCR *jcr; /* jcr for plugin */
104 bRC rc; /* last return code */
105 bool disabled; /* set if plugin disabled */
106 findFILESET *fileset; /* pointer to exclude files */
109 static bool is_plugin_disabled(JCR *jcr)
112 if (!jcr->plugin_ctx) {
115 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
116 return b_ctx->disabled;
121 * Create a plugin event
123 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
129 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
130 return; /* Return if no plugins loaded */
133 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
134 event.eventType = eventType;
136 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
138 /* Pass event to every plugin */
139 foreach_alist(plugin, plugin_list) {
141 jcr->plugin_ctx = &plugin_ctx_list[i++];
142 jcr->plugin = plugin;
143 if (is_plugin_disabled(jcr)) {
146 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
153 jcr->plugin_ctx = NULL;
158 * Check if file was seen for accurate
160 bool plugin_check_file(JCR *jcr, char *fname)
166 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
167 return false; /* Return if no plugins loaded */
170 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
172 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
174 /* Pass event to every plugin */
175 foreach_alist(plugin, plugin_list) {
176 jcr->plugin_ctx = &plugin_ctx_list[i++];
177 jcr->plugin = plugin;
178 if (is_plugin_disabled(jcr)) {
181 if (plug_func(plugin)->checkFile == NULL) {
184 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
185 if (rc == bRC_Seen) {
191 jcr->plugin_ctx = NULL;
192 return rc == bRC_Seen;
197 * Sequence of calls for a backup:
198 * 1. plugin_save() here is called with ff_pkt
199 * 2. we find the plugin requested on the command string
200 * 3. we generate a bEventBackupCommand event to the specified plugin
201 * and pass it the command string.
202 * 4. we make a startPluginBackup call to the plugin, which gives
203 * us the data we need in save_pkt
204 * 5. we call Bacula's save_file() subroutine to save the specified
205 * file. The plugin will be called at pluginIO() to supply the
208 * Sequence of calls for restore:
209 * See subroutine plugin_name_stream() below.
211 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
217 char *cmd = ff_pkt->top_fname;
220 POOL_MEM fname(PM_FNAME);
221 POOL_MEM link(PM_FNAME);
223 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
224 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
225 return 1; /* Return if no plugins loaded */
228 jcr->cmd_plugin = true;
229 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
230 event.eventType = bEventBackupCommand;
232 /* Handle plugin command here backup */
233 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
234 if (!(p = strchr(cmd, ':'))) {
235 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
243 /* Note, we stop the loop on the first plugin that matches the name */
244 foreach_alist(plugin, plugin_list) {
245 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
246 if (strncmp(plugin->file, cmd, len) != 0) {
251 * We put the current plugin pointer, and the plugin context
252 * into the jcr, because during save_file(), the plugin
253 * will be called many times and these values are needed.
255 jcr->plugin_ctx = &plugin_ctx_list[i];
256 jcr->plugin = plugin;
257 if (is_plugin_disabled(jcr)) {
261 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
262 /* Send the backup command to the right plugin*/
263 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
266 /* Loop getting filenames to backup then saving them */
267 while (!jcr->is_job_canceled()) {
268 memset(&sp, 0, sizeof(sp));
269 sp.pkt_size = sizeof(sp);
270 sp.pkt_end = sizeof(sp);
273 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
275 /* Get the file save parameters. I.e. the stat pkt ... */
276 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
279 if (sp.type == 0 || sp.fname == NULL) {
280 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
284 jcr->plugin_sp = &sp;
287 * Copy fname and link because save_file() zaps them. This
288 * avoids zaping the plugin's strings.
290 pm_strcpy(fname, sp.fname);
291 pm_strcpy(link, sp.link);
292 ff_pkt->fname = fname.c_str();
293 ff_pkt->link = link.c_str();
294 ff_pkt->type = sp.type;
295 ff_pkt->object = sp.object;
296 ff_pkt->object_len = sp.object_len;
297 if (sp.type == FT_RESTORE_FIRST) {
298 ff_pkt->LinkFI = sp.index; /* restore object index */
300 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
301 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
303 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
305 save_file(jcr, ff_pkt, true);
306 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
307 if (rc == bRC_More || rc == bRC_OK) {
308 accurate_mark_file_as_seen(jcr, fname.c_str());
310 if (rc == bRC_More) {
317 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
320 jcr->cmd_plugin = false;
322 jcr->plugin_ctx = NULL;
327 * Send plugin name start/end record to SD
329 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
332 int index = jcr->JobFiles;
333 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
336 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
339 if (jcr->is_job_canceled()) {
344 index++; /* JobFiles not incremented yet */
346 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
347 /* Send stream header */
348 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
349 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
353 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
356 /* Send data -- not much */
357 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
359 /* Send end of data */
360 stat = sd->fsend("0 0");
363 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
367 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
368 sd->signal(BNET_EOD); /* indicate end of plugin name data */
373 * Plugin name stream found during restore. The record passed in
374 * argument name was generated in send_plugin_name() above.
376 * Returns: true if start of stream
377 * false if end of steam
379 bool plugin_name_stream(JCR *jcr, char *name)
383 bool start, portable;
387 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
389 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
390 skip_nonspaces(&p); /* skip over jcr->JobFiles */
394 /* Start of plugin data */
395 skip_nonspaces(&p); /* skip start/end flag */
397 portable = *p == '1';
398 skip_nonspaces(&p); /* skip portable flag */
403 * End of plugin data, notify plugin, then clear flags
405 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
407 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
409 jcr->plugin_ctx = NULL;
413 if (!plugin_ctx_list) {
418 * After this point, we are dealing with a restore start
421 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
422 if (!(p = strchr(cmd, ':'))) {
423 Jmsg1(jcr, M_ERROR, 0,
424 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
433 * Search for correct plugin as specified on the command
435 foreach_alist(plugin, plugin_list) {
437 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
438 if (strncmp(plugin->file, cmd, len) != 0) {
442 jcr->plugin_ctx = &plugin_ctx_list[i];
443 jcr->plugin = plugin;
444 if (is_plugin_disabled(jcr)) {
447 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
448 event.eventType = bEventRestoreCommand;
449 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
450 &event, cmd) != bRC_OK) {
453 /* ***FIXME**** check error code */
454 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
457 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
464 * Tell the plugin to create the file. Return values are
465 * This is called only during Restore
468 * CF_SKIP -- skip processing this file
469 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
470 * CF_CREATED -- created, but no content to extract (typically directories)
473 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
475 bpContext *plugin_ctx = jcr->plugin_ctx;
476 Plugin *plugin = jcr->plugin;
477 struct restore_pkt rp;
481 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
485 rp.pkt_size = sizeof(rp);
486 rp.pkt_end = sizeof(rp);
487 rp.stream = attr->stream;
488 rp.data_stream = attr->data_stream;
489 rp.type = attr->type;
490 rp.file_index = attr->file_index;
491 rp.LinkFI = attr->LinkFI;
493 rp.statp = attr->statp; /* structure assignment */
494 rp.attrEx = attr->attrEx;
495 rp.ofname = attr->ofname;
496 rp.olname = attr->olname;
497 rp.where = jcr->where;
498 rp.RegexWhere = jcr->RegexWhere;
499 rp.replace = jcr->replace;
500 rp.create_status = CF_ERROR;
501 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
502 rp.stream, rp.type, rp.LinkFI, rp.ofname);
504 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
506 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
508 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
512 if (rp.create_status == CF_ERROR) {
513 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
517 /* Created link or directory? */
518 if (rp.create_status == CF_CREATED) {
519 return rp.create_status; /* yes, no need to bopen */
522 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
523 Dmsg0(dbglvl, "call bopen\n");
524 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
525 Dmsg1(50, "bopen status=%d\n", stat);
528 be.set_errno(bfd->berrno);
529 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
530 attr->ofname, be.bstrerror());
531 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
535 if (!is_bopen(bfd)) {
536 Dmsg0(000, "===== BFD is not open!!!!\n");
542 * Reset the file attributes after all file I/O is done -- this allows
543 * the previous access time/dates to be set properly, and it also allows
544 * us to properly set directory permissions.
545 * Not currently Implemented.
547 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
549 Dmsg0(dbglvl, "plugin_set_attributes\n");
553 pm_strcpy(attr->ofname, "*none*");
558 * Print to file the plugin info.
560 void dump_fd_plugin(Plugin *plugin, FILE *fp)
565 pInfo *info = (pInfo *)plugin->pinfo;
566 fprintf(fp, "\tversion=%d\n", info->version);
567 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
568 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
569 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
570 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
571 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
572 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
576 * This entry point is called internally by Bacula to ensure
577 * that the plugin IO calls come into this code.
579 void load_fd_plugins(const char *plugin_dir)
584 Dmsg0(dbglvl, "plugin dir is NULL\n");
588 plugin_list = New(alist(10, not_owned_by_alist));
589 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
590 is_plugin_compatible)) {
591 /* Either none found, or some error */
592 if (plugin_list->size() == 0) {
595 Dmsg0(dbglvl, "No plugins loaded\n");
600 /* Plug entry points called from findlib */
601 plugin_bopen = my_plugin_bopen;
602 plugin_bclose = my_plugin_bclose;
603 plugin_bread = my_plugin_bread;
604 plugin_bwrite = my_plugin_bwrite;
605 plugin_blseek = my_plugin_blseek;
608 * Verify that the plugin is acceptable, and print information
611 foreach_alist(plugin, plugin_list) {
612 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
613 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
616 dbg_plugin_add_hook(dump_fd_plugin);
620 * Check if a plugin is compatible. Called by the load_plugin function
621 * to allow us to verify the plugin.
623 static bool is_plugin_compatible(Plugin *plugin)
625 pInfo *info = (pInfo *)plugin->pinfo;
626 Dmsg0(50, "is_plugin_compatible called\n");
627 if (debug_level >= 50) {
628 dump_fd_plugin(plugin, stdin);
630 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
631 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
632 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
633 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
634 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
638 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
639 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
640 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
641 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
642 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
645 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
646 strcmp(info->plugin_license, "GPLv2") != 0) {
647 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
648 plugin->file, info->plugin_license);
649 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
650 plugin->file, info->plugin_license);
659 * Create a new instance of each plugin for this Job
660 * Note, plugin_list can exist but jcr->plugin_ctx_list can
661 * be NULL if no plugins were loaded.
663 void new_plugins(JCR *jcr)
669 Dmsg0(dbglvl, "plugin list is NULL\n");
672 if (jcr->is_job_canceled() || jcr->JobId == 0) {
676 int num = plugin_list->size();
679 Dmsg0(dbglvl, "No plugins loaded\n");
683 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
685 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
686 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
687 foreach_alist(plugin, plugin_list) {
688 /* Start a new instance of each plugin */
689 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
690 memset(b_ctx, 0, sizeof(bacula_ctx));
692 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
693 plugin_ctx_list[i].pContext = NULL;
694 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
695 b_ctx->disabled = true;
701 * Free the plugin instances for this Job
703 void free_plugins(JCR *jcr)
708 if (!plugin_list || !jcr->plugin_ctx_list) {
709 return; /* no plugins, nothing to do */
712 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
713 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
714 foreach_alist(plugin, plugin_list) {
715 /* Free the plugin instance */
716 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
717 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
719 free(plugin_ctx_list);
720 jcr->plugin_ctx_list = NULL;
723 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
726 Plugin *plugin = (Plugin *)jcr->plugin;
729 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
730 if (!plugin || !jcr->plugin_ctx) {
733 io.pkt_size = sizeof(io);
734 io.pkt_end = sizeof(io);
743 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
744 bfd->berrno = io.io_errno;
746 errno = b_errno_win32;
749 bfd->lerror = io.lerror;
751 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
755 static int my_plugin_bclose(BFILE *bfd)
758 Plugin *plugin = (Plugin *)jcr->plugin;
761 Dmsg0(dbglvl, "===== plugin_bclose\n");
762 if (!plugin || !jcr->plugin_ctx) {
765 io.pkt_size = sizeof(io);
766 io.pkt_end = sizeof(io);
772 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
773 bfd->berrno = io.io_errno;
775 errno = b_errno_win32;
778 bfd->lerror = io.lerror;
780 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
784 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
787 Plugin *plugin = (Plugin *)jcr->plugin;
790 Dmsg0(dbglvl, "plugin_bread\n");
791 if (!plugin || !jcr->plugin_ctx) {
794 io.pkt_size = sizeof(io);
795 io.pkt_end = sizeof(io);
798 io.buf = (char *)buf;
801 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
802 bfd->berrno = io.io_errno;
804 errno = b_errno_win32;
807 bfd->lerror = io.lerror;
809 return (ssize_t)io.status;
812 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
815 Plugin *plugin = (Plugin *)jcr->plugin;
818 Dmsg0(dbglvl, "plugin_bwrite\n");
819 if (!plugin || !jcr->plugin_ctx) {
822 io.pkt_size = sizeof(io);
823 io.pkt_end = sizeof(io);
826 io.buf = (char *)buf;
829 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
830 bfd->berrno = io.io_errno;
832 errno = b_errno_win32;
835 bfd->lerror = io.lerror;
837 return (ssize_t)io.status;
840 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
843 Plugin *plugin = (Plugin *)jcr->plugin;
846 Dmsg0(dbglvl, "plugin_bseek\n");
847 if (!plugin || !jcr->plugin_ctx) {
850 io.pkt_size = sizeof(io);
851 io.pkt_end = sizeof(io);
857 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
858 bfd->berrno = io.io_errno;
860 errno = b_errno_win32;
863 bfd->lerror = io.lerror;
865 return (boffset_t)io.offset;
868 /* ==============================================================
870 * Callbacks from the plugin
872 * ==============================================================
874 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
877 if (!value || !ctx) {
880 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
881 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
885 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
888 *((int *)value) = jcr->JobId;
889 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
892 *((char **)value) = my_name;
893 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
896 *((int *)value) = jcr->getJobLevel();
897 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
900 *((int *)value) = jcr->getJobType();
901 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
904 *((char **)value) = jcr->client_name;
905 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
908 *((char **)value) = jcr->Job;
909 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
912 *((int *)value) = jcr->JobStatus;
913 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
916 *((int *)value) = (int)jcr->mtime;
917 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
920 *((int *)value) = (int)jcr->accurate;
921 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
924 break; /* a write only variable, ignore read request */
928 *(void **)value = g_pVSSClient->GetVssObject();
933 case bVarVssDllHandle:
936 *(void **)value = g_pVSSClient->GetVssDllHandle();
945 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
948 if (!value || !ctx) {
951 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
952 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
956 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
959 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
969 static bRC baculaRegisterEvents(bpContext *ctx, ...)
979 while ((event = va_arg(args, uint32_t))) {
980 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
986 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
987 int type, utime_t mtime, const char *fmt, ...)
994 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
999 va_start(arg_ptr, fmt);
1000 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1002 Jmsg(jcr, type, mtime, "%s", buf);
1006 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1007 int level, const char *fmt, ...)
1012 va_start(arg_ptr, fmt);
1013 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1015 d_msg(file, line, level, "%s", buf);
1019 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1023 return sm_malloc(file, line, size);
1025 return malloc(size);
1029 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1032 sm_free(file, line, mem);
1039 * Let the plugin define files/directories to be excluded
1040 * from the main backup.
1042 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1052 bctx = (bacula_ctx *)ctx->bContext;
1060 if (!bctx->fileset) {
1061 bctx->fileset = new_exclude(jcr);
1063 add_file_to_fileset(jcr, file, bctx->fileset, true);
1070 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1071 int (*plugin_bclose)(JCR *jcr) = NULL;
1072 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1073 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1074 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1076 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1081 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1086 int main(int argc, char *argv[])
1088 char plugin_dir[1000];
1093 strcpy(my_name, "test-fd");
1095 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1096 load_fd_plugins(plugin_dir);
1104 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1105 generate_plugin_event(jcr1, bEventJobEnd);
1106 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1108 generate_plugin_event(jcr2, bEventJobEnd);
1113 Dmsg0(dbglvl, "bacula: OK ...\n");
1114 close_memory_pool();
1119 #endif /* TEST_PROGRAM */