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 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()) {
484 rp.pkt_size = sizeof(rp);
485 rp.pkt_end = sizeof(rp);
486 rp.stream = attr->stream;
487 rp.data_stream = attr->data_stream;
488 rp.type = attr->type;
489 rp.file_index = attr->file_index;
490 rp.LinkFI = attr->LinkFI;
492 rp.statp = attr->statp; /* structure assignment */
493 rp.attrEx = attr->attrEx;
494 rp.ofname = attr->ofname;
495 rp.olname = attr->olname;
496 rp.where = jcr->where;
497 rp.RegexWhere = jcr->RegexWhere;
498 rp.replace = jcr->replace;
499 rp.create_status = CF_ERROR;
500 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
501 rp.stream, rp.type, rp.LinkFI, rp.ofname);
503 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
505 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
507 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
511 if (rp.create_status == CF_ERROR) {
512 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
516 /* Created link or directory? */
517 if (rp.create_status == CF_CREATED) {
518 return rp.create_status; /* yes, no need to bopen */
520 if (rp.type == FT_RESTORE_FIRST) {
524 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
525 Dmsg0(dbglvl, "call bopen\n");
526 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
527 Dmsg1(50, "bopen status=%d\n", stat);
530 be.set_errno(bfd->berrno);
531 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
532 attr->ofname, be.bstrerror());
533 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
537 if (!is_bopen(bfd)) {
538 Dmsg0(000, "===== BFD is not open!!!!\n");
544 * Reset the file attributes after all file I/O is done -- this allows
545 * the previous access time/dates to be set properly, and it also allows
546 * us to properly set directory permissions.
547 * Not currently Implemented.
549 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
551 Dmsg0(dbglvl, "plugin_set_attributes\n");
555 pm_strcpy(attr->ofname, "*none*");
560 * Print to file the plugin info.
562 void dump_fd_plugin(Plugin *plugin, FILE *fp)
567 pInfo *info = (pInfo *)plugin->pinfo;
568 fprintf(fp, "\tversion=%d\n", info->version);
569 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
570 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
571 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
572 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
573 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
574 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
578 * This entry point is called internally by Bacula to ensure
579 * that the plugin IO calls come into this code.
581 void load_fd_plugins(const char *plugin_dir)
586 Dmsg0(dbglvl, "plugin dir is NULL\n");
590 plugin_list = New(alist(10, not_owned_by_alist));
591 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
592 is_plugin_compatible)) {
593 /* Either none found, or some error */
594 if (plugin_list->size() == 0) {
597 Dmsg0(dbglvl, "No plugins loaded\n");
602 /* Plug entry points called from findlib */
603 plugin_bopen = my_plugin_bopen;
604 plugin_bclose = my_plugin_bclose;
605 plugin_bread = my_plugin_bread;
606 plugin_bwrite = my_plugin_bwrite;
607 plugin_blseek = my_plugin_blseek;
610 * Verify that the plugin is acceptable, and print information
613 foreach_alist(plugin, plugin_list) {
614 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
615 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
618 dbg_plugin_add_hook(dump_fd_plugin);
622 * Check if a plugin is compatible. Called by the load_plugin function
623 * to allow us to verify the plugin.
625 static bool is_plugin_compatible(Plugin *plugin)
627 pInfo *info = (pInfo *)plugin->pinfo;
628 Dmsg0(50, "is_plugin_compatible called\n");
629 if (debug_level >= 50) {
630 dump_fd_plugin(plugin, stdin);
632 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
633 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
634 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
635 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
636 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
640 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
641 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
642 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
643 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
644 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
647 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
648 strcmp(info->plugin_license, "GPLv2") != 0) {
649 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
650 plugin->file, info->plugin_license);
651 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
652 plugin->file, info->plugin_license);
661 * Create a new instance of each plugin for this Job
662 * Note, plugin_list can exist but jcr->plugin_ctx_list can
663 * be NULL if no plugins were loaded.
665 void new_plugins(JCR *jcr)
671 Dmsg0(dbglvl, "plugin list is NULL\n");
674 if (jcr->is_job_canceled() || jcr->JobId == 0) {
678 int num = plugin_list->size();
681 Dmsg0(dbglvl, "No plugins loaded\n");
685 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
687 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
688 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
689 foreach_alist(plugin, plugin_list) {
690 /* Start a new instance of each plugin */
691 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
692 memset(b_ctx, 0, sizeof(bacula_ctx));
694 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
695 plugin_ctx_list[i].pContext = NULL;
696 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
697 b_ctx->disabled = true;
703 * Free the plugin instances for this Job
705 void free_plugins(JCR *jcr)
710 if (!plugin_list || !jcr->plugin_ctx_list) {
711 return; /* no plugins, nothing to do */
714 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
715 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
716 foreach_alist(plugin, plugin_list) {
717 /* Free the plugin instance */
718 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
719 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
721 free(plugin_ctx_list);
722 jcr->plugin_ctx_list = NULL;
725 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
728 Plugin *plugin = (Plugin *)jcr->plugin;
731 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
732 if (!plugin || !jcr->plugin_ctx) {
735 io.pkt_size = sizeof(io);
736 io.pkt_end = sizeof(io);
745 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
746 bfd->berrno = io.io_errno;
748 errno = b_errno_win32;
751 bfd->lerror = io.lerror;
753 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
757 static int my_plugin_bclose(BFILE *bfd)
760 Plugin *plugin = (Plugin *)jcr->plugin;
763 Dmsg0(dbglvl, "===== plugin_bclose\n");
764 if (!plugin || !jcr->plugin_ctx) {
767 io.pkt_size = sizeof(io);
768 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(dbglvl, "plugin_bclose stat=%d\n", io.status);
786 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
789 Plugin *plugin = (Plugin *)jcr->plugin;
792 Dmsg0(dbglvl, "plugin_bread\n");
793 if (!plugin || !jcr->plugin_ctx) {
796 io.pkt_size = sizeof(io);
797 io.pkt_end = sizeof(io);
800 io.buf = (char *)buf;
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 return (ssize_t)io.status;
814 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
817 Plugin *plugin = (Plugin *)jcr->plugin;
820 Dmsg0(dbglvl, "plugin_bwrite\n");
821 if (!plugin || !jcr->plugin_ctx) {
824 io.pkt_size = sizeof(io);
825 io.pkt_end = sizeof(io);
828 io.buf = (char *)buf;
831 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
832 bfd->berrno = io.io_errno;
834 errno = b_errno_win32;
837 bfd->lerror = io.lerror;
839 return (ssize_t)io.status;
842 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
845 Plugin *plugin = (Plugin *)jcr->plugin;
848 Dmsg0(dbglvl, "plugin_bseek\n");
849 if (!plugin || !jcr->plugin_ctx) {
852 io.pkt_size = sizeof(io);
853 io.pkt_end = sizeof(io);
859 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
860 bfd->berrno = io.io_errno;
862 errno = b_errno_win32;
865 bfd->lerror = io.lerror;
867 return (boffset_t)io.offset;
870 /* ==============================================================
872 * Callbacks from the plugin
874 * ==============================================================
876 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
879 if (!value || !ctx) {
882 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
883 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
887 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
890 *((int *)value) = jcr->JobId;
891 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
894 *((char **)value) = my_name;
895 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
898 *((int *)value) = jcr->getJobLevel();
899 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
902 *((int *)value) = jcr->getJobType();
903 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
906 *((char **)value) = jcr->client_name;
907 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
910 *((char **)value) = jcr->Job;
911 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
914 *((int *)value) = jcr->JobStatus;
915 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
918 *((int *)value) = (int)jcr->mtime;
919 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
922 *((int *)value) = (int)jcr->accurate;
923 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
926 break; /* a write only variable, ignore read request */
930 *(void **)value = g_pVSSClient->GetVssObject();
935 case bVarVssDllHandle:
938 *(void **)value = g_pVSSClient->GetVssDllHandle();
947 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
950 if (!value || !ctx) {
953 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
954 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
958 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
961 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
971 static bRC baculaRegisterEvents(bpContext *ctx, ...)
981 while ((event = va_arg(args, uint32_t))) {
982 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
988 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
989 int type, utime_t mtime, const char *fmt, ...)
996 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1001 va_start(arg_ptr, fmt);
1002 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1004 Jmsg(jcr, type, mtime, "%s", buf);
1008 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1009 int level, const char *fmt, ...)
1014 va_start(arg_ptr, fmt);
1015 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1017 d_msg(file, line, level, "%s", buf);
1021 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1025 return sm_malloc(file, line, size);
1027 return malloc(size);
1031 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1034 sm_free(file, line, mem);
1041 * Let the plugin define files/directories to be excluded
1042 * from the main backup.
1044 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1054 bctx = (bacula_ctx *)ctx->bContext;
1062 if (!bctx->fileset) {
1063 bctx->fileset = new_exclude(jcr);
1065 add_file_to_fileset(jcr, file, bctx->fileset, true);
1072 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1073 int (*plugin_bclose)(JCR *jcr) = NULL;
1074 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1075 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1076 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1078 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1083 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1088 int main(int argc, char *argv[])
1090 char plugin_dir[1000];
1095 strcpy(my_name, "test-fd");
1097 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1098 load_fd_plugins(plugin_dir);
1106 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1107 generate_plugin_event(jcr1, bEventJobEnd);
1108 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1110 generate_plugin_event(jcr2, bEventJobEnd);
1115 Dmsg0(dbglvl, "bacula: OK ...\n");
1116 close_memory_pool();
1121 #endif /* TEST_PROGRAM */