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 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
296 Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
297 save_file(jcr, ff_pkt, true);
298 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
299 if (rc == bRC_More || rc == bRC_OK) {
300 accurate_mark_file_as_seen(jcr, fname.c_str());
302 if (rc == bRC_More) {
309 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
312 jcr->cmd_plugin = false;
314 jcr->plugin_ctx = NULL;
319 * Send plugin name start/end record to SD
321 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
324 int index = jcr->JobFiles;
325 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
328 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
331 if (jcr->is_job_canceled()) {
336 index++; /* JobFiles not incremented yet */
338 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
339 /* Send stream header */
340 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
341 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
345 Dmsg1(50, "send: %s\n", sd->msg);
348 /* Send data -- not much */
349 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
351 /* Send end of data */
352 stat = sd->fsend("0 0");
355 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
359 Dmsg1(dbglvl, "send: %s\n", sd->msg);
360 sd->signal(BNET_EOD); /* indicate end of plugin name data */
365 * Plugin name stream found during restore. The record passed in
366 * argument name was generated in send_plugin_name() above.
368 * Returns: true if start of stream
369 * false if end of steam
371 bool plugin_name_stream(JCR *jcr, char *name)
375 bool start, portable;
379 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
381 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
382 skip_nonspaces(&p); /* skip over jcr->JobFiles */
386 /* Start of plugin data */
387 skip_nonspaces(&p); /* skip start/end flag */
389 portable = *p == '1';
390 skip_nonspaces(&p); /* skip portable flag */
395 * End of plugin data, notify plugin, then clear flags
397 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
399 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
401 jcr->plugin_ctx = NULL;
405 if (!plugin_ctx_list) {
410 * After this point, we are dealing with a restore start
413 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
414 if (!(p = strchr(cmd, ':'))) {
415 Jmsg1(jcr, M_ERROR, 0,
416 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
425 * Search for correct plugin as specified on the command
427 foreach_alist(plugin, plugin_list) {
429 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
430 if (strncmp(plugin->file, cmd, len) != 0) {
434 jcr->plugin_ctx = &plugin_ctx_list[i];
435 jcr->plugin = plugin;
436 if (is_plugin_disabled(jcr)) {
439 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
440 event.eventType = bEventRestoreCommand;
441 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
442 &event, cmd) != bRC_OK) {
445 /* ***FIXME**** check error code */
446 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
449 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
456 * Tell the plugin to create the file. Return values are
457 * This is called only during Restore
460 * CF_SKIP -- skip processing this file
461 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
462 * CF_CREATED -- created, but no content to extract (typically directories)
465 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
467 bpContext *plugin_ctx = jcr->plugin_ctx;
468 Plugin *plugin = jcr->plugin;
469 struct restore_pkt rp;
473 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
476 rp.pkt_size = sizeof(rp);
477 rp.pkt_end = sizeof(rp);
478 rp.stream = attr->stream;
479 rp.data_stream = attr->data_stream;
480 rp.type = attr->type;
481 rp.file_index = attr->file_index;
482 rp.LinkFI = attr->LinkFI;
484 rp.statp = attr->statp; /* structure assignment */
485 rp.attrEx = attr->attrEx;
486 rp.ofname = attr->ofname;
487 rp.olname = attr->olname;
488 rp.where = jcr->where;
489 rp.RegexWhere = jcr->RegexWhere;
490 rp.replace = jcr->replace;
491 rp.create_status = CF_ERROR;
492 Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
493 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
495 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
499 if (rp.create_status == CF_ERROR) {
500 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
504 /* Created link or directory? */
505 if (rp.create_status == CF_CREATED) {
506 return rp.create_status; /* yes, no need to bopen */
509 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
510 Dmsg0(dbglvl, "call bopen\n");
511 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
512 Dmsg1(50, "bopen status=%d\n", stat);
515 be.set_errno(bfd->berrno);
516 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
517 attr->ofname, be.bstrerror());
518 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
522 if (!is_bopen(bfd)) {
523 Dmsg0(000, "===== BFD is not open!!!!\n");
529 * Reset the file attributes after all file I/O is done -- this allows
530 * the previous access time/dates to be set properly, and it also allows
531 * us to properly set directory permissions.
532 * Not currently Implemented.
534 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
536 Dmsg0(dbglvl, "plugin_set_attributes\n");
540 pm_strcpy(attr->ofname, "*none*");
545 * Print to file the plugin info.
547 void dump_fd_plugin(Plugin *plugin, FILE *fp)
552 pInfo *info = (pInfo *)plugin->pinfo;
553 fprintf(fp, "\tversion=%d\n", info->version);
554 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
555 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
556 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
557 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
558 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
559 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
563 * This entry point is called internally by Bacula to ensure
564 * that the plugin IO calls come into this code.
566 void load_fd_plugins(const char *plugin_dir)
571 Dmsg0(dbglvl, "plugin dir is NULL\n");
575 plugin_list = New(alist(10, not_owned_by_alist));
576 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
577 is_plugin_compatible)) {
578 /* Either none found, or some error */
579 if (plugin_list->size() == 0) {
582 Dmsg0(dbglvl, "No plugins loaded\n");
587 /* Plug entry points called from findlib */
588 plugin_bopen = my_plugin_bopen;
589 plugin_bclose = my_plugin_bclose;
590 plugin_bread = my_plugin_bread;
591 plugin_bwrite = my_plugin_bwrite;
592 plugin_blseek = my_plugin_blseek;
595 * Verify that the plugin is acceptable, and print information
598 foreach_alist(plugin, plugin_list) {
599 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
600 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
603 dbg_plugin_add_hook(dump_fd_plugin);
607 * Check if a plugin is compatible. Called by the load_plugin function
608 * to allow us to verify the plugin.
610 static bool is_plugin_compatible(Plugin *plugin)
612 pInfo *info = (pInfo *)plugin->pinfo;
613 Dmsg0(50, "is_plugin_compatible called\n");
614 if (debug_level >= 50) {
615 dump_fd_plugin(plugin, stdin);
617 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
618 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
619 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
620 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
621 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
625 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
626 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
627 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
628 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
629 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
632 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
633 strcmp(info->plugin_license, "GPLv2") != 0) {
634 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
635 plugin->file, info->plugin_license);
636 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
637 plugin->file, info->plugin_license);
646 * Create a new instance of each plugin for this Job
647 * Note, plugin_list can exist but jcr->plugin_ctx_list can
648 * be NULL if no plugins were loaded.
650 void new_plugins(JCR *jcr)
656 Dmsg0(dbglvl, "plugin list is NULL\n");
659 if (jcr->is_job_canceled()) {
663 int num = plugin_list->size();
666 Dmsg0(dbglvl, "No plugins loaded\n");
670 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
672 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
673 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
674 foreach_alist(plugin, plugin_list) {
675 /* Start a new instance of each plugin */
676 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
677 memset(b_ctx, 0, sizeof(bacula_ctx));
679 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
680 plugin_ctx_list[i].pContext = NULL;
681 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
682 b_ctx->disabled = true;
688 * Free the plugin instances for this Job
690 void free_plugins(JCR *jcr)
695 if (!plugin_list || !jcr->plugin_ctx_list) {
696 return; /* no plugins, nothing to do */
699 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
700 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
701 foreach_alist(plugin, plugin_list) {
702 /* Free the plugin instance */
703 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
704 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
706 free(plugin_ctx_list);
707 jcr->plugin_ctx_list = NULL;
710 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
713 Plugin *plugin = (Plugin *)jcr->plugin;
716 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
717 if (!plugin || !jcr->plugin_ctx) {
720 io.pkt_size = sizeof(io);
721 io.pkt_end = sizeof(io);
730 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
731 bfd->berrno = io.io_errno;
733 errno = b_errno_win32;
736 bfd->lerror = io.lerror;
738 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
742 static int my_plugin_bclose(BFILE *bfd)
745 Plugin *plugin = (Plugin *)jcr->plugin;
748 Dmsg0(dbglvl, "===== plugin_bclose\n");
749 if (!plugin || !jcr->plugin_ctx) {
752 io.pkt_size = sizeof(io);
753 io.pkt_end = sizeof(io);
759 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
760 bfd->berrno = io.io_errno;
762 errno = b_errno_win32;
765 bfd->lerror = io.lerror;
767 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
771 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
774 Plugin *plugin = (Plugin *)jcr->plugin;
777 Dmsg0(dbglvl, "plugin_bread\n");
778 if (!plugin || !jcr->plugin_ctx) {
781 io.pkt_size = sizeof(io);
782 io.pkt_end = sizeof(io);
785 io.buf = (char *)buf;
788 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
789 bfd->berrno = io.io_errno;
791 errno = b_errno_win32;
794 bfd->lerror = io.lerror;
796 return (ssize_t)io.status;
799 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
802 Plugin *plugin = (Plugin *)jcr->plugin;
805 Dmsg0(dbglvl, "plugin_bwrite\n");
806 if (!plugin || !jcr->plugin_ctx) {
809 io.pkt_size = sizeof(io);
810 io.pkt_end = sizeof(io);
813 io.buf = (char *)buf;
816 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
817 bfd->berrno = io.io_errno;
819 errno = b_errno_win32;
822 bfd->lerror = io.lerror;
824 return (ssize_t)io.status;
827 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
830 Plugin *plugin = (Plugin *)jcr->plugin;
833 Dmsg0(dbglvl, "plugin_bseek\n");
834 if (!plugin || !jcr->plugin_ctx) {
837 io.pkt_size = sizeof(io);
838 io.pkt_end = sizeof(io);
844 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
845 bfd->berrno = io.io_errno;
847 errno = b_errno_win32;
850 bfd->lerror = io.lerror;
852 return (boffset_t)io.offset;
855 /* ==============================================================
857 * Callbacks from the plugin
859 * ==============================================================
861 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
864 if (!value || !ctx) {
867 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
868 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
872 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
875 *((int *)value) = jcr->JobId;
876 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
879 *((char **)value) = my_name;
880 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
883 *((int *)value) = jcr->getJobLevel();
884 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
887 *((int *)value) = jcr->getJobType();
888 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
891 *((char **)value) = jcr->client_name;
892 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
895 *((char **)value) = jcr->Job;
896 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
899 *((int *)value) = jcr->JobStatus;
900 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
903 *((int *)value) = (int)jcr->mtime;
904 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
907 *((int *)value) = (int)jcr->accurate;
908 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
911 break; /* a write only variable, ignore read request */
915 *(void **)value = g_pVSSClient->GetVssObject();
920 case bVarVssDllHandle:
923 *(void **)value = g_pVSSClient->GetVssDllHandle();
932 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
935 if (!value || !ctx) {
938 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
939 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
943 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
946 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
956 static bRC baculaRegisterEvents(bpContext *ctx, ...)
966 while ((event = va_arg(args, uint32_t))) {
967 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
973 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
974 int type, utime_t mtime, const char *fmt, ...)
981 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
986 va_start(arg_ptr, fmt);
987 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
989 Jmsg(jcr, type, mtime, "%s", buf);
993 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
994 int level, const char *fmt, ...)
999 va_start(arg_ptr, fmt);
1000 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1002 d_msg(file, line, level, "%s", buf);
1006 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1010 return sm_malloc(file, line, size);
1012 return malloc(size);
1016 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1019 sm_free(file, line, mem);
1026 * Let the plugin define files/directories to be excluded
1027 * from the main backup.
1029 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1039 bctx = (bacula_ctx *)ctx->bContext;
1047 if (!bctx->fileset) {
1048 bctx->fileset = new_exclude(jcr);
1050 add_file_to_fileset(jcr, file, bctx->fileset, true);
1057 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1058 int (*plugin_bclose)(JCR *jcr) = NULL;
1059 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1060 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1061 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1063 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1068 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1073 int main(int argc, char *argv[])
1075 char plugin_dir[1000];
1080 strcpy(my_name, "test-fd");
1082 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1083 load_fd_plugins(plugin_dir);
1091 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1092 generate_plugin_event(jcr1, bEventJobEnd);
1093 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1095 generate_plugin_event(jcr2, bEventJobEnd);
1100 Dmsg0(dbglvl, "bacula: OK ...\n");
1101 close_memory_pool();
1106 #endif /* TEST_PROGRAM */