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) {
280 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in 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 ff_pkt->type = sp.type;
291 if (sp.type == FT_RESTORE_FIRST) {
292 if (!sp.object_name) {
293 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
297 pm_strcpy(fname, sp.object_name);
298 ff_pkt->fname = fname.c_str();
299 ff_pkt->LinkFI = sp.index; /* restore object index */
300 ff_pkt->object = sp.object;
301 ff_pkt->object_len = sp.object_len;
304 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
308 pm_strcpy(fname, sp.fname);
309 pm_strcpy(link, sp.link);
310 ff_pkt->fname = fname.c_str();
311 ff_pkt->link = link.c_str();
314 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
315 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
317 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
319 /* Call Bacula core code to backup the plugin's file */
320 save_file(jcr, ff_pkt, true);
321 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
322 if (rc == bRC_More || rc == bRC_OK) {
323 accurate_mark_file_as_seen(jcr, fname.c_str());
325 if (rc == bRC_More) {
332 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
335 jcr->cmd_plugin = false;
337 jcr->plugin_ctx = NULL;
342 * Send plugin name start/end record to SD
344 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
347 int index = jcr->JobFiles;
348 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
351 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
354 if (jcr->is_job_canceled()) {
359 index++; /* JobFiles not incremented yet */
361 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
362 /* Send stream header */
363 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
364 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
368 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
371 /* Send data -- not much */
372 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
374 /* Send end of data */
375 stat = sd->fsend("0 0");
378 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
382 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
383 sd->signal(BNET_EOD); /* indicate end of plugin name data */
388 * Plugin name stream found during restore. The record passed in
389 * argument name was generated in send_plugin_name() above.
391 * Returns: true if start of stream
392 * false if end of steam
394 bool plugin_name_stream(JCR *jcr, char *name)
398 bool start, portable;
402 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
404 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
405 skip_nonspaces(&p); /* skip over jcr->JobFiles */
409 /* Start of plugin data */
410 skip_nonspaces(&p); /* skip start/end flag */
412 portable = *p == '1';
413 skip_nonspaces(&p); /* skip portable flag */
418 * End of plugin data, notify plugin, then clear flags
420 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
422 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
424 jcr->plugin_ctx = NULL;
428 if (!plugin_ctx_list) {
433 * After this point, we are dealing with a restore start
436 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
437 if (!(p = strchr(cmd, ':'))) {
438 Jmsg1(jcr, M_ERROR, 0,
439 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
448 * Search for correct plugin as specified on the command
450 foreach_alist(plugin, plugin_list) {
452 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
453 if (strncmp(plugin->file, cmd, len) != 0) {
457 jcr->plugin_ctx = &plugin_ctx_list[i];
458 jcr->plugin = plugin;
459 if (is_plugin_disabled(jcr)) {
462 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
463 event.eventType = bEventRestoreCommand;
464 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
465 &event, cmd) != bRC_OK) {
468 /* ***FIXME**** check error code */
469 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
472 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
479 * Tell the plugin to create the file. Return values are
480 * This is called only during Restore
483 * CF_SKIP -- skip processing this file
484 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
485 * CF_CREATED -- created, but no content to extract (typically directories)
488 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
490 bpContext *plugin_ctx = jcr->plugin_ctx;
491 Plugin *plugin = jcr->plugin;
492 struct restore_pkt rp;
496 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
500 rp.pkt_size = sizeof(rp);
501 rp.pkt_end = sizeof(rp);
502 rp.stream = attr->stream;
503 rp.data_stream = attr->data_stream;
504 rp.type = attr->type;
505 rp.file_index = attr->file_index;
506 rp.LinkFI = attr->LinkFI;
508 rp.statp = attr->statp; /* structure assignment */
509 rp.attrEx = attr->attrEx;
510 rp.ofname = attr->ofname;
511 rp.olname = attr->olname;
512 rp.where = jcr->where;
513 rp.RegexWhere = jcr->RegexWhere;
514 rp.replace = jcr->replace;
515 rp.create_status = CF_ERROR;
516 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
517 rp.stream, rp.type, rp.LinkFI, rp.ofname);
519 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
521 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
523 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
527 if (rp.create_status == CF_ERROR) {
528 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
532 /* Created link or directory? */
533 if (rp.create_status == CF_CREATED) {
534 return rp.create_status; /* yes, no need to bopen */
537 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
538 Dmsg0(dbglvl, "call bopen\n");
539 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
540 Dmsg1(50, "bopen status=%d\n", stat);
543 be.set_errno(bfd->berrno);
544 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
545 attr->ofname, be.bstrerror());
546 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
550 if (!is_bopen(bfd)) {
551 Dmsg0(000, "===== BFD is not open!!!!\n");
557 * Reset the file attributes after all file I/O is done -- this allows
558 * the previous access time/dates to be set properly, and it also allows
559 * us to properly set directory permissions.
560 * Not currently Implemented.
562 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
564 Dmsg0(dbglvl, "plugin_set_attributes\n");
568 pm_strcpy(attr->ofname, "*none*");
573 * Print to file the plugin info.
575 void dump_fd_plugin(Plugin *plugin, FILE *fp)
580 pInfo *info = (pInfo *)plugin->pinfo;
581 fprintf(fp, "\tversion=%d\n", info->version);
582 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
583 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
584 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
585 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
586 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
587 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
591 * This entry point is called internally by Bacula to ensure
592 * that the plugin IO calls come into this code.
594 void load_fd_plugins(const char *plugin_dir)
599 Dmsg0(dbglvl, "plugin dir is NULL\n");
603 plugin_list = New(alist(10, not_owned_by_alist));
604 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
605 is_plugin_compatible)) {
606 /* Either none found, or some error */
607 if (plugin_list->size() == 0) {
610 Dmsg0(dbglvl, "No plugins loaded\n");
615 /* Plug entry points called from findlib */
616 plugin_bopen = my_plugin_bopen;
617 plugin_bclose = my_plugin_bclose;
618 plugin_bread = my_plugin_bread;
619 plugin_bwrite = my_plugin_bwrite;
620 plugin_blseek = my_plugin_blseek;
623 * Verify that the plugin is acceptable, and print information
626 foreach_alist(plugin, plugin_list) {
627 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
628 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
631 dbg_plugin_add_hook(dump_fd_plugin);
635 * Check if a plugin is compatible. Called by the load_plugin function
636 * to allow us to verify the plugin.
638 static bool is_plugin_compatible(Plugin *plugin)
640 pInfo *info = (pInfo *)plugin->pinfo;
641 Dmsg0(50, "is_plugin_compatible called\n");
642 if (debug_level >= 50) {
643 dump_fd_plugin(plugin, stdin);
645 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
646 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
647 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
648 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
649 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
653 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
654 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
655 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
656 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
657 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
660 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
661 strcmp(info->plugin_license, "GPLv2") != 0) {
662 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
663 plugin->file, info->plugin_license);
664 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
665 plugin->file, info->plugin_license);
674 * Create a new instance of each plugin for this Job
675 * Note, plugin_list can exist but jcr->plugin_ctx_list can
676 * be NULL if no plugins were loaded.
678 void new_plugins(JCR *jcr)
684 Dmsg0(dbglvl, "plugin list is NULL\n");
687 if (jcr->is_job_canceled() || jcr->JobId == 0) {
691 int num = plugin_list->size();
694 Dmsg0(dbglvl, "No plugins loaded\n");
698 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
700 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
701 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
702 foreach_alist(plugin, plugin_list) {
703 /* Start a new instance of each plugin */
704 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
705 memset(b_ctx, 0, sizeof(bacula_ctx));
707 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
708 plugin_ctx_list[i].pContext = NULL;
709 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
710 b_ctx->disabled = true;
716 * Free the plugin instances for this Job
718 void free_plugins(JCR *jcr)
723 if (!plugin_list || !jcr->plugin_ctx_list) {
724 return; /* no plugins, nothing to do */
727 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
728 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
729 foreach_alist(plugin, plugin_list) {
730 /* Free the plugin instance */
731 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
732 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
734 free(plugin_ctx_list);
735 jcr->plugin_ctx_list = NULL;
738 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
741 Plugin *plugin = (Plugin *)jcr->plugin;
744 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
745 if (!plugin || !jcr->plugin_ctx) {
748 io.pkt_size = sizeof(io);
749 io.pkt_end = sizeof(io);
758 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
759 bfd->berrno = io.io_errno;
761 errno = b_errno_win32;
764 bfd->lerror = io.lerror;
766 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
770 static int my_plugin_bclose(BFILE *bfd)
773 Plugin *plugin = (Plugin *)jcr->plugin;
776 Dmsg0(dbglvl, "===== plugin_bclose\n");
777 if (!plugin || !jcr->plugin_ctx) {
780 io.pkt_size = sizeof(io);
781 io.pkt_end = sizeof(io);
787 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
788 bfd->berrno = io.io_errno;
790 errno = b_errno_win32;
793 bfd->lerror = io.lerror;
795 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
799 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
802 Plugin *plugin = (Plugin *)jcr->plugin;
805 Dmsg0(dbglvl, "plugin_bread\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 ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
830 Plugin *plugin = (Plugin *)jcr->plugin;
833 Dmsg0(dbglvl, "plugin_bwrite\n");
834 if (!plugin || !jcr->plugin_ctx) {
837 io.pkt_size = sizeof(io);
838 io.pkt_end = sizeof(io);
841 io.buf = (char *)buf;
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 (ssize_t)io.status;
855 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
858 Plugin *plugin = (Plugin *)jcr->plugin;
861 Dmsg0(dbglvl, "plugin_bseek\n");
862 if (!plugin || !jcr->plugin_ctx) {
865 io.pkt_size = sizeof(io);
866 io.pkt_end = sizeof(io);
872 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
873 bfd->berrno = io.io_errno;
875 errno = b_errno_win32;
878 bfd->lerror = io.lerror;
880 return (boffset_t)io.offset;
883 /* ==============================================================
885 * Callbacks from the plugin
887 * ==============================================================
889 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
892 if (!value || !ctx) {
895 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
896 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
900 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
903 *((int *)value) = jcr->JobId;
904 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
907 *((char **)value) = my_name;
908 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
911 *((int *)value) = jcr->getJobLevel();
912 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
915 *((int *)value) = jcr->getJobType();
916 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
919 *((char **)value) = jcr->client_name;
920 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
923 *((char **)value) = jcr->Job;
924 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
927 *((int *)value) = jcr->JobStatus;
928 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
931 *((int *)value) = (int)jcr->mtime;
932 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
935 *((int *)value) = (int)jcr->accurate;
936 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
939 break; /* a write only variable, ignore read request */
943 *(void **)value = g_pVSSClient->GetVssObject();
948 case bVarVssDllHandle:
951 *(void **)value = g_pVSSClient->GetVssDllHandle();
960 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
963 if (!value || !ctx) {
966 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
967 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
971 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
974 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
984 static bRC baculaRegisterEvents(bpContext *ctx, ...)
994 while ((event = va_arg(args, uint32_t))) {
995 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1001 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1002 int type, utime_t mtime, const char *fmt, ...)
1009 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1014 va_start(arg_ptr, fmt);
1015 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1017 Jmsg(jcr, type, mtime, "%s", buf);
1021 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1022 int level, const char *fmt, ...)
1027 va_start(arg_ptr, fmt);
1028 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1030 d_msg(file, line, level, "%s", buf);
1034 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1038 return sm_malloc(file, line, size);
1040 return malloc(size);
1044 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1047 sm_free(file, line, mem);
1054 * Let the plugin define files/directories to be excluded
1055 * from the main backup.
1057 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1067 bctx = (bacula_ctx *)ctx->bContext;
1075 if (!bctx->fileset) {
1076 bctx->fileset = new_exclude(jcr);
1078 add_file_to_fileset(jcr, file, bctx->fileset, true);
1085 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1086 int (*plugin_bclose)(JCR *jcr) = NULL;
1087 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1088 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1089 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1091 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1096 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1101 int main(int argc, char *argv[])
1103 char plugin_dir[1000];
1108 strcpy(my_name, "test-fd");
1110 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1111 load_fd_plugins(plugin_dir);
1119 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1120 generate_plugin_event(jcr1, bEventJobEnd);
1121 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1123 generate_plugin_event(jcr2, bEventJobEnd);
1128 Dmsg0(dbglvl, "bacula: OK ...\n");
1129 close_memory_pool();
1134 #endif /* TEST_PROGRAM */