2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation, which is
11 listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main program to test loading and running Bacula plugins.
30 * Destined to become Bacula pluginloader, ...
32 * Kern Sibbald, October 2007
39 const int dbglvl = 150;
41 const char *plugin_type = "-fd.dll";
43 const char *plugin_type = "-fd.so";
46 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
48 /* Function pointers to be set here */
49 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
50 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
51 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
52 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
53 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
56 /* Forward referenced functions */
57 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
58 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
59 static bRC baculaRegisterEvents(bpContext *ctx, ...);
60 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
61 int type, utime_t mtime, const char *fmt, ...);
62 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
63 int level, const char *fmt, ...);
64 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
66 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
67 static bRC baculaAddExclude(bpContext *ctx, const char *file);
68 static bRC baculaAddInclude(bpContext *ctx, const char *file);
69 static bRC baculaAddIncludeOptions(bpContext *ctx, const char *opts);
70 static bRC baculaAddRegexToInclude(bpContext *ctx, const char *item, int type);
71 static bRC baculaAddWildToInclude(bpContext *ctx, const char *item, int type);
72 static bool is_plugin_compatible(Plugin *plugin);
75 * These will be plugged into the global pointer structure for
78 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
79 static int my_plugin_bclose(BFILE *bfd);
80 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
81 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
82 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
86 static bInfo binfo = {
88 FD_PLUGIN_INTERFACE_VERSION
91 /* Bacula entry points */
92 static bFuncs bfuncs = {
94 FD_PLUGIN_INTERFACE_VERSION,
104 baculaAddIncludeOptions,
105 baculaAddRegexToInclude,
106 baculaAddWildToInclude
110 * Bacula private context
113 JCR *jcr; /* jcr for plugin */
114 bRC rc; /* last return code */
115 bool disabled; /* set if plugin disabled */
116 findINCEXE *exclude; /* pointer to exclude files */
117 findINCEXE *include; /* pointer to include/exclude files */
120 static bool is_plugin_disabled(JCR *jcr)
123 if (!jcr->plugin_ctx) {
126 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
127 return b_ctx->disabled;
132 * Create a plugin event
134 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
140 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
141 return; /* Return if no plugins loaded */
144 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
145 event.eventType = eventType;
147 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
149 /* Pass event to every plugin */
150 foreach_alist(plugin, plugin_list) {
152 jcr->plugin_ctx = &plugin_ctx_list[i++];
153 jcr->plugin = plugin;
154 if (is_plugin_disabled(jcr)) {
157 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
164 jcr->plugin_ctx = NULL;
169 * Check if file was seen for accurate
171 bool plugin_check_file(JCR *jcr, char *fname)
177 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
178 return false; /* Return if no plugins loaded */
181 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
183 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
185 /* Pass event to every plugin */
186 foreach_alist(plugin, plugin_list) {
187 jcr->plugin_ctx = &plugin_ctx_list[i++];
188 jcr->plugin = plugin;
189 if (is_plugin_disabled(jcr)) {
192 if (plug_func(plugin)->checkFile == NULL) {
195 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
196 if (rc == bRC_Seen) {
202 jcr->plugin_ctx = NULL;
203 return rc == bRC_Seen;
208 * Sequence of calls for a backup:
209 * 1. plugin_save() here is called with ff_pkt
210 * 2. we find the plugin requested on the command string
211 * 3. we generate a bEventBackupCommand event to the specified plugin
212 * and pass it the command string.
213 * 4. we make a startPluginBackup call to the plugin, which gives
214 * us the data we need in save_pkt
215 * 5. we call Bacula's save_file() subroutine to save the specified
216 * file. The plugin will be called at pluginIO() to supply the
219 * Sequence of calls for restore:
220 * See subroutine plugin_name_stream() below.
222 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
228 char *cmd = ff_pkt->top_fname;
231 POOL_MEM fname(PM_FNAME);
232 POOL_MEM link(PM_FNAME);
234 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
235 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
236 return 1; /* Return if no plugins loaded */
239 jcr->cmd_plugin = true;
240 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
241 event.eventType = bEventBackupCommand;
243 /* Handle plugin command here backup */
244 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
245 if (!(p = strchr(cmd, ':'))) {
246 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
254 /* Note, we stop the loop on the first plugin that matches the name */
255 foreach_alist(plugin, plugin_list) {
256 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
257 if (strncmp(plugin->file, cmd, len) != 0) {
262 * We put the current plugin pointer, and the plugin context
263 * into the jcr, because during save_file(), the plugin
264 * will be called many times and these values are needed.
266 jcr->plugin_ctx = &plugin_ctx_list[i];
267 jcr->plugin = plugin;
268 if (is_plugin_disabled(jcr)) {
272 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
273 /* Send the backup command to the right plugin*/
274 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
277 /* Loop getting filenames to backup then saving them */
278 while (!jcr->is_job_canceled()) {
279 memset(&sp, 0, sizeof(sp));
280 sp.pkt_size = sizeof(sp);
281 sp.pkt_end = sizeof(sp);
284 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
286 /* Get the file save parameters. I.e. the stat pkt ... */
287 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
291 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
295 jcr->plugin_sp = &sp;
298 * Copy fname and link because save_file() zaps them. This
299 * avoids zaping the plugin's strings.
301 ff_pkt->type = sp.type;
302 if (sp.type == FT_RESTORE_FIRST) {
303 if (!sp.object_name) {
304 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
308 ff_pkt->fname = cmd; /* full plugin string */
309 ff_pkt->object_name = sp.object_name;
310 ff_pkt->object_index = sp.index; /* restore object index */
311 ff_pkt->object_compression = 0; /* no compression for now */
312 ff_pkt->object = sp.object;
313 ff_pkt->object_len = sp.object_len;
316 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
320 pm_strcpy(fname, sp.fname);
321 pm_strcpy(link, sp.link);
322 ff_pkt->fname = fname.c_str();
323 ff_pkt->link = link.c_str();
326 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
327 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
329 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
331 /* Call Bacula core code to backup the plugin's file */
332 save_file(jcr, ff_pkt, true);
333 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
334 if (rc == bRC_More || rc == bRC_OK) {
335 accurate_mark_file_as_seen(jcr, fname.c_str());
337 if (rc == bRC_More) {
344 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
347 jcr->cmd_plugin = false;
349 jcr->plugin_ctx = NULL;
354 * Send plugin name start/end record to SD
356 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
359 int index = jcr->JobFiles;
360 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
363 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
366 if (jcr->is_job_canceled()) {
371 index++; /* JobFiles not incremented yet */
373 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
374 /* Send stream header */
375 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
376 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
380 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
383 /* Send data -- not much */
384 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
386 /* Send end of data */
387 stat = sd->fsend("0 0");
390 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
394 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
395 sd->signal(BNET_EOD); /* indicate end of plugin name data */
400 * Plugin name stream found during restore. The record passed in
401 * argument name was generated in send_plugin_name() above.
403 * Returns: true if start of stream
404 * false if end of steam
406 bool plugin_name_stream(JCR *jcr, char *name)
410 bool start, portable;
414 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
416 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
417 skip_nonspaces(&p); /* skip over jcr->JobFiles */
421 /* Start of plugin data */
422 skip_nonspaces(&p); /* skip start/end flag */
424 portable = *p == '1';
425 skip_nonspaces(&p); /* skip portable flag */
430 * End of plugin data, notify plugin, then clear flags
432 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
434 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
436 jcr->plugin_ctx = NULL;
440 if (!plugin_ctx_list) {
445 * After this point, we are dealing with a restore start
448 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
449 if (!(p = strchr(cmd, ':'))) {
450 Jmsg1(jcr, M_ERROR, 0,
451 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
460 * Search for correct plugin as specified on the command
462 foreach_alist(plugin, plugin_list) {
464 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
465 if (strncmp(plugin->file, cmd, len) != 0) {
469 jcr->plugin_ctx = &plugin_ctx_list[i];
470 jcr->plugin = plugin;
471 if (is_plugin_disabled(jcr)) {
474 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
475 event.eventType = bEventRestoreCommand;
476 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
477 &event, cmd) != bRC_OK) {
480 /* ***FIXME**** check error code */
481 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
484 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
491 * Tell the plugin to create the file. Return values are
492 * This is called only during Restore
495 * CF_SKIP -- skip processing this file
496 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
497 * CF_CREATED -- created, but no content to extract (typically directories)
500 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
502 bpContext *plugin_ctx = jcr->plugin_ctx;
503 Plugin *plugin = jcr->plugin;
504 struct restore_pkt rp;
508 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
512 rp.pkt_size = sizeof(rp);
513 rp.pkt_end = sizeof(rp);
514 rp.stream = attr->stream;
515 rp.data_stream = attr->data_stream;
516 rp.type = attr->type;
517 rp.file_index = attr->file_index;
518 rp.LinkFI = attr->LinkFI;
520 rp.statp = attr->statp; /* structure assignment */
521 rp.attrEx = attr->attrEx;
522 rp.ofname = attr->ofname;
523 rp.olname = attr->olname;
524 rp.where = jcr->where;
525 rp.RegexWhere = jcr->RegexWhere;
526 rp.replace = jcr->replace;
527 rp.create_status = CF_ERROR;
528 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
529 rp.stream, rp.type, rp.LinkFI, rp.ofname);
531 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
533 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
535 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
539 if (rp.create_status == CF_ERROR) {
540 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
544 /* Created link or directory? */
545 if (rp.create_status == CF_CREATED) {
546 return rp.create_status; /* yes, no need to bopen */
549 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
550 Dmsg0(dbglvl, "call bopen\n");
551 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
552 Dmsg1(50, "bopen status=%d\n", stat);
555 be.set_errno(bfd->berrno);
556 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
557 attr->ofname, be.bstrerror());
558 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
562 if (!is_bopen(bfd)) {
563 Dmsg0(000, "===== BFD is not open!!!!\n");
569 * Reset the file attributes after all file I/O is done -- this allows
570 * the previous access time/dates to be set properly, and it also allows
571 * us to properly set directory permissions.
572 * Not currently Implemented.
574 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
576 Dmsg0(dbglvl, "plugin_set_attributes\n");
580 pm_strcpy(attr->ofname, "*none*");
585 * Print to file the plugin info.
587 void dump_fd_plugin(Plugin *plugin, FILE *fp)
592 pInfo *info = (pInfo *)plugin->pinfo;
593 fprintf(fp, "\tversion=%d\n", info->version);
594 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
595 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
596 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
597 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
598 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
599 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
603 * This entry point is called internally by Bacula to ensure
604 * that the plugin IO calls come into this code.
606 void load_fd_plugins(const char *plugin_dir)
611 Dmsg0(dbglvl, "plugin dir is NULL\n");
615 plugin_list = New(alist(10, not_owned_by_alist));
616 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
617 is_plugin_compatible)) {
618 /* Either none found, or some error */
619 if (plugin_list->size() == 0) {
622 Dmsg0(dbglvl, "No plugins loaded\n");
627 /* Plug entry points called from findlib */
628 plugin_bopen = my_plugin_bopen;
629 plugin_bclose = my_plugin_bclose;
630 plugin_bread = my_plugin_bread;
631 plugin_bwrite = my_plugin_bwrite;
632 plugin_blseek = my_plugin_blseek;
635 * Verify that the plugin is acceptable, and print information
638 foreach_alist(plugin, plugin_list) {
639 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
640 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
643 dbg_plugin_add_hook(dump_fd_plugin);
647 * Check if a plugin is compatible. Called by the load_plugin function
648 * to allow us to verify the plugin.
650 static bool is_plugin_compatible(Plugin *plugin)
652 pInfo *info = (pInfo *)plugin->pinfo;
653 Dmsg0(50, "is_plugin_compatible called\n");
654 if (debug_level >= 50) {
655 dump_fd_plugin(plugin, stdin);
657 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
658 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
659 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
660 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
661 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
665 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
666 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
667 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
668 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
669 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
672 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
673 strcmp(info->plugin_license, "GPLv2") != 0) {
674 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
675 plugin->file, info->plugin_license);
676 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
677 plugin->file, info->plugin_license);
686 * Create a new instance of each plugin for this Job
687 * Note, plugin_list can exist but jcr->plugin_ctx_list can
688 * be NULL if no plugins were loaded.
690 void new_plugins(JCR *jcr)
696 Dmsg0(dbglvl, "plugin list is NULL\n");
699 if (jcr->is_job_canceled() || jcr->JobId == 0) {
703 int num = plugin_list->size();
706 Dmsg0(dbglvl, "No plugins loaded\n");
710 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
712 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
713 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
714 foreach_alist(plugin, plugin_list) {
715 /* Start a new instance of each plugin */
716 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
717 memset(b_ctx, 0, sizeof(bacula_ctx));
719 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
720 plugin_ctx_list[i].pContext = NULL;
721 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
722 b_ctx->disabled = true;
728 * Free the plugin instances for this Job
730 void free_plugins(JCR *jcr)
735 if (!plugin_list || !jcr->plugin_ctx_list) {
736 return; /* no plugins, nothing to do */
739 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
740 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
741 foreach_alist(plugin, plugin_list) {
742 /* Free the plugin instance */
743 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
744 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
746 free(plugin_ctx_list);
747 jcr->plugin_ctx_list = NULL;
750 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
753 Plugin *plugin = (Plugin *)jcr->plugin;
756 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
757 if (!plugin || !jcr->plugin_ctx) {
760 io.pkt_size = sizeof(io);
761 io.pkt_end = sizeof(io);
770 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
771 bfd->berrno = io.io_errno;
773 errno = b_errno_win32;
776 bfd->lerror = io.lerror;
778 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
782 static int my_plugin_bclose(BFILE *bfd)
785 Plugin *plugin = (Plugin *)jcr->plugin;
788 Dmsg0(dbglvl, "===== plugin_bclose\n");
789 if (!plugin || !jcr->plugin_ctx) {
792 io.pkt_size = sizeof(io);
793 io.pkt_end = sizeof(io);
799 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
800 bfd->berrno = io.io_errno;
802 errno = b_errno_win32;
805 bfd->lerror = io.lerror;
807 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
811 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
814 Plugin *plugin = (Plugin *)jcr->plugin;
817 Dmsg0(dbglvl, "plugin_bread\n");
818 if (!plugin || !jcr->plugin_ctx) {
821 io.pkt_size = sizeof(io);
822 io.pkt_end = sizeof(io);
825 io.buf = (char *)buf;
828 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
829 bfd->berrno = io.io_errno;
831 errno = b_errno_win32;
834 bfd->lerror = io.lerror;
836 return (ssize_t)io.status;
839 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
842 Plugin *plugin = (Plugin *)jcr->plugin;
845 Dmsg0(dbglvl, "plugin_bwrite\n");
846 if (!plugin || !jcr->plugin_ctx) {
849 io.pkt_size = sizeof(io);
850 io.pkt_end = sizeof(io);
853 io.buf = (char *)buf;
856 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
857 bfd->berrno = io.io_errno;
859 errno = b_errno_win32;
862 bfd->lerror = io.lerror;
864 return (ssize_t)io.status;
867 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
870 Plugin *plugin = (Plugin *)jcr->plugin;
873 Dmsg0(dbglvl, "plugin_bseek\n");
874 if (!plugin || !jcr->plugin_ctx) {
877 io.pkt_size = sizeof(io);
878 io.pkt_end = sizeof(io);
884 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
885 bfd->berrno = io.io_errno;
887 errno = b_errno_win32;
890 bfd->lerror = io.lerror;
892 return (boffset_t)io.offset;
895 /* ==============================================================
897 * Callbacks from the plugin
899 * ==============================================================
901 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
904 if (!value || !ctx) {
907 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
908 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
912 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
915 *((int *)value) = jcr->JobId;
916 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
919 *((char **)value) = my_name;
920 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
923 *((int *)value) = jcr->getJobLevel();
924 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
927 *((int *)value) = jcr->getJobType();
928 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
931 *((char **)value) = jcr->client_name;
932 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
935 *((char **)value) = jcr->Job;
936 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
939 *((int *)value) = jcr->JobStatus;
940 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
943 *((int *)value) = (int)jcr->mtime;
944 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
947 *((int *)value) = (int)jcr->accurate;
948 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
951 break; /* a write only variable, ignore read request */
955 *(void **)value = g_pVSSClient->GetVssObject();
960 case bVarVssDllHandle:
963 *(void **)value = g_pVSSClient->GetVssDllHandle();
969 *(void **)value = me->working_directory;
975 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
978 if (!value || !ctx) {
981 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
982 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
986 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
989 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
999 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1008 va_start(args, ctx);
1009 while ((event = va_arg(args, uint32_t))) {
1010 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1016 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1017 int type, utime_t mtime, const char *fmt, ...)
1024 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1029 va_start(arg_ptr, fmt);
1030 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1032 Jmsg(jcr, type, mtime, "%s", buf);
1036 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1037 int level, const char *fmt, ...)
1042 va_start(arg_ptr, fmt);
1043 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1045 d_msg(file, line, level, "%s", buf);
1049 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1053 return sm_malloc(file, line, size);
1055 return malloc(size);
1059 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1062 sm_free(file, line, mem);
1068 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1073 bctx = (bacula_ctx *)ctx->bContext;
1085 * Let the plugin define files/directories to be excluded
1086 * from the main backup.
1088 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1092 if (!is_ctx_good(ctx, jcr, bctx)) {
1098 if (!bctx->exclude) {
1099 bctx->exclude = new_exclude(jcr);
1100 new_options(jcr, bctx->exclude);
1102 set_incexe(jcr, bctx->exclude);
1103 add_file_to_fileset(jcr, file, true);
1108 * Let the plugin define files/directories to be excluded
1109 * from the main backup.
1111 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1115 if (!is_ctx_good(ctx, jcr, bctx)) {
1121 if (!bctx->include) {
1122 bctx->include = new_preinclude(jcr);
1123 new_options(jcr, bctx->include);
1125 set_incexe(jcr, bctx->include);
1126 add_file_to_fileset(jcr, file, true);
1130 static bRC baculaAddIncludeOptions(bpContext *ctx, const char *opts)
1134 if (!is_ctx_good(ctx, jcr, bctx)) {
1140 if (!bctx->include) {
1141 bctx->include = new_preinclude(jcr);
1142 new_options(jcr, bctx->include);
1144 set_incexe(jcr, bctx->include);
1145 add_options_to_fileset(jcr, opts);
1149 static bRC baculaAddRegexToInclude(bpContext *ctx, const char *item, int type)
1153 if (!is_ctx_good(ctx, jcr, bctx)) {
1159 if (!bctx->include) {
1160 bctx->include = new_preinclude(jcr);
1161 new_options(jcr, bctx->include);
1163 set_incexe(jcr, bctx->include);
1164 add_regex_to_fileset(jcr, item, type);
1168 static bRC baculaAddWildToInclude(bpContext *ctx, const char *item, int type)
1172 if (!is_ctx_good(ctx, jcr, bctx)) {
1178 if (!bctx->include) {
1179 bctx->include = new_preinclude(jcr);
1180 new_options(jcr, bctx->include);
1182 set_incexe(jcr, bctx->include);
1183 add_wild_to_fileset(jcr, item, type);
1193 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1194 int (*plugin_bclose)(JCR *jcr) = NULL;
1195 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1196 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1197 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1199 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1204 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1209 int main(int argc, char *argv[])
1211 char plugin_dir[1000];
1216 strcpy(my_name, "test-fd");
1218 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1219 load_fd_plugins(plugin_dir);
1227 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1228 generate_plugin_event(jcr1, bEventJobEnd);
1229 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1231 generate_plugin_event(jcr2, bEventJobEnd);
1236 Dmsg0(dbglvl, "bacula: OK ...\n");
1237 close_memory_pool();
1242 #endif /* TEST_PROGRAM */