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 baculaAddOptions(bpContext *ctx, const char *opts);
70 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
71 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
72 static bRC baculaNewOptions(bpContext *ctx);
73 static bRC baculaNewInclude(bpContext *ctx);
74 static bool is_plugin_compatible(Plugin *plugin);
75 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
78 * These will be plugged into the global pointer structure for
81 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
82 static int my_plugin_bclose(BFILE *bfd);
83 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
84 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
85 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
89 static bInfo binfo = {
91 FD_PLUGIN_INTERFACE_VERSION
94 /* Bacula entry points */
95 static bFuncs bfuncs = {
97 FD_PLUGIN_INTERFACE_VERSION,
115 * Bacula private context
118 JCR *jcr; /* jcr for plugin */
119 bRC rc; /* last return code */
120 bool disabled; /* set if plugin disabled */
121 findINCEXE *exclude; /* pointer to exclude files */
122 findINCEXE *include; /* pointer to include/exclude files */
125 static bool is_plugin_disabled(JCR *jcr)
128 if (!jcr->plugin_ctx) {
131 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
132 return b_ctx->disabled;
137 * Create a plugin event
139 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
148 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
149 return; /* Return if no plugins loaded */
152 /* Some events are sent to only a particular plugin */
154 case bEventPluginCommand:
155 name = (char *)value;
156 if (!get_plugin_name(jcr, name, &len)) {
164 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
165 event.eventType = eventType;
167 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
169 /* Pass event to every plugin (except if name is set) */
170 foreach_alist(plugin, plugin_list) {
171 if (name && strncmp(plugin->file, name, len) != 0) {
175 jcr->plugin_ctx = &plugin_ctx_list[i++];
176 jcr->plugin = plugin;
177 if (is_plugin_disabled(jcr)) {
180 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
187 jcr->plugin_ctx = NULL;
192 * Check if file was seen for accurate
194 bool plugin_check_file(JCR *jcr, char *fname)
200 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
201 return false; /* Return if no plugins loaded */
204 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
206 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
208 /* Pass event to every plugin */
209 foreach_alist(plugin, plugin_list) {
210 jcr->plugin_ctx = &plugin_ctx_list[i++];
211 jcr->plugin = plugin;
212 if (is_plugin_disabled(jcr)) {
215 if (plug_func(plugin)->checkFile == NULL) {
218 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
219 if (rc == bRC_Seen) {
225 jcr->plugin_ctx = NULL;
226 return rc == bRC_Seen;
229 /* Get the first part of the the plugin command
230 * systemstate:/@SYSTEMSTATE/
232 * => can use strncmp(plugin_name, cmd, ret);
234 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
241 /* Handle plugin command here backup */
242 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
243 if (!(p = strchr(cmd, ':'))) {
244 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
256 * Sequence of calls for a backup:
257 * 1. plugin_save() here is called with ff_pkt
258 * 2. we find the plugin requested on the command string
259 * 3. we generate a bEventBackupCommand event to the specified plugin
260 * and pass it the command string.
261 * 4. we make a startPluginBackup call to the plugin, which gives
262 * us the data we need in save_pkt
263 * 5. we call Bacula's save_file() subroutine to save the specified
264 * file. The plugin will be called at pluginIO() to supply the
267 * Sequence of calls for restore:
268 * See subroutine plugin_name_stream() below.
270 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
275 char *cmd = ff_pkt->top_fname;
278 POOL_MEM fname(PM_FNAME);
279 POOL_MEM link(PM_FNAME);
281 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
282 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
283 return 1; /* Return if no plugins loaded */
286 jcr->cmd_plugin = true;
287 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
288 event.eventType = bEventBackupCommand;
290 if (!get_plugin_name(jcr, cmd, &len)) {
294 /* Note, we stop the loop on the first plugin that matches the name */
295 foreach_alist(plugin, plugin_list) {
296 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
297 if (strncmp(plugin->file, cmd, len) != 0) {
302 * We put the current plugin pointer, and the plugin context
303 * into the jcr, because during save_file(), the plugin
304 * will be called many times and these values are needed.
306 jcr->plugin_ctx = &plugin_ctx_list[i];
307 jcr->plugin = plugin;
308 if (is_plugin_disabled(jcr)) {
312 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
313 /* Send the backup command to the right plugin*/
314 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
317 /* Loop getting filenames to backup then saving them */
318 while (!jcr->is_job_canceled()) {
319 memset(&sp, 0, sizeof(sp));
320 sp.pkt_size = sizeof(sp);
321 sp.pkt_end = sizeof(sp);
324 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
326 /* Get the file save parameters. I.e. the stat pkt ... */
327 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
331 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
335 jcr->plugin_sp = &sp;
338 * Copy fname and link because save_file() zaps them. This
339 * avoids zaping the plugin's strings.
341 ff_pkt->type = sp.type;
342 if (sp.type == FT_RESTORE_FIRST) {
343 if (!sp.object_name) {
344 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
348 ff_pkt->fname = cmd; /* full plugin string */
349 ff_pkt->object_name = sp.object_name;
350 ff_pkt->object_index = sp.index; /* restore object index */
351 ff_pkt->object_compression = 0; /* no compression for now */
352 ff_pkt->object = sp.object;
353 ff_pkt->object_len = sp.object_len;
356 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
360 pm_strcpy(fname, sp.fname);
361 pm_strcpy(link, sp.link);
362 ff_pkt->fname = fname.c_str();
363 ff_pkt->link = link.c_str();
366 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
367 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
369 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
371 /* Call Bacula core code to backup the plugin's file */
372 save_file(jcr, ff_pkt, true);
373 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
374 if (rc == bRC_More || rc == bRC_OK) {
375 accurate_mark_file_as_seen(jcr, fname.c_str());
377 if (rc == bRC_More) {
384 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
387 jcr->cmd_plugin = false;
389 jcr->plugin_ctx = NULL;
394 * Send plugin name start/end record to SD
396 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
399 int index = jcr->JobFiles;
400 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
403 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
406 if (jcr->is_job_canceled()) {
411 index++; /* JobFiles not incremented yet */
413 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
414 /* Send stream header */
415 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
416 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
420 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
423 /* Send data -- not much */
424 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
426 /* Send end of data */
427 stat = sd->fsend("0 0");
430 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
434 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
435 sd->signal(BNET_EOD); /* indicate end of plugin name data */
440 * Plugin name stream found during restore. The record passed in
441 * argument name was generated in send_plugin_name() above.
443 * Returns: true if start of stream
444 * false if end of steam
446 bool plugin_name_stream(JCR *jcr, char *name)
450 bool start, portable;
454 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
456 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
457 skip_nonspaces(&p); /* skip over jcr->JobFiles */
461 /* Start of plugin data */
462 skip_nonspaces(&p); /* skip start/end flag */
464 portable = *p == '1';
465 skip_nonspaces(&p); /* skip portable flag */
470 * End of plugin data, notify plugin, then clear flags
472 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
474 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
476 jcr->plugin_ctx = NULL;
480 if (!plugin_ctx_list) {
485 * After this point, we are dealing with a restore start
487 if (!get_plugin_name(jcr, cmd, &len)) {
492 * Search for correct plugin as specified on the command
494 foreach_alist(plugin, plugin_list) {
496 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
497 if (strncmp(plugin->file, cmd, len) != 0) {
501 jcr->plugin_ctx = &plugin_ctx_list[i];
502 jcr->plugin = plugin;
503 if (is_plugin_disabled(jcr)) {
506 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
507 event.eventType = bEventRestoreCommand;
508 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
509 &event, cmd) != bRC_OK) {
512 /* ***FIXME**** check error code */
513 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
516 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
523 * Tell the plugin to create the file. Return values are
524 * This is called only during Restore
527 * CF_SKIP -- skip processing this file
528 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
529 * CF_CREATED -- created, but no content to extract (typically directories)
532 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
534 bpContext *plugin_ctx = jcr->plugin_ctx;
535 Plugin *plugin = jcr->plugin;
536 struct restore_pkt rp;
540 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
544 rp.pkt_size = sizeof(rp);
545 rp.pkt_end = sizeof(rp);
546 rp.stream = attr->stream;
547 rp.data_stream = attr->data_stream;
548 rp.type = attr->type;
549 rp.file_index = attr->file_index;
550 rp.LinkFI = attr->LinkFI;
552 rp.statp = attr->statp; /* structure assignment */
553 rp.attrEx = attr->attrEx;
554 rp.ofname = attr->ofname;
555 rp.olname = attr->olname;
556 rp.where = jcr->where;
557 rp.RegexWhere = jcr->RegexWhere;
558 rp.replace = jcr->replace;
559 rp.create_status = CF_ERROR;
560 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
561 rp.stream, rp.type, rp.LinkFI, rp.ofname);
563 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
565 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
567 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
571 if (rp.create_status == CF_ERROR) {
572 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
576 /* Created link or directory? */
577 if (rp.create_status == CF_CREATED) {
578 return rp.create_status; /* yes, no need to bopen */
581 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
582 Dmsg0(dbglvl, "call bopen\n");
583 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
584 Dmsg1(50, "bopen status=%d\n", stat);
587 be.set_errno(bfd->berrno);
588 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
589 attr->ofname, be.bstrerror());
590 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
594 if (!is_bopen(bfd)) {
595 Dmsg0(000, "===== BFD is not open!!!!\n");
601 * Reset the file attributes after all file I/O is done -- this allows
602 * the previous access time/dates to be set properly, and it also allows
603 * us to properly set directory permissions.
604 * Not currently Implemented.
606 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
608 Dmsg0(dbglvl, "plugin_set_attributes\n");
612 pm_strcpy(attr->ofname, "*none*");
617 * Print to file the plugin info.
619 void dump_fd_plugin(Plugin *plugin, FILE *fp)
624 pInfo *info = (pInfo *)plugin->pinfo;
625 fprintf(fp, "\tversion=%d\n", info->version);
626 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
627 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
628 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
629 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
630 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
631 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
635 * This entry point is called internally by Bacula to ensure
636 * that the plugin IO calls come into this code.
638 void load_fd_plugins(const char *plugin_dir)
643 Dmsg0(dbglvl, "plugin dir is NULL\n");
647 plugin_list = New(alist(10, not_owned_by_alist));
648 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
649 is_plugin_compatible)) {
650 /* Either none found, or some error */
651 if (plugin_list->size() == 0) {
654 Dmsg0(dbglvl, "No plugins loaded\n");
659 /* Plug entry points called from findlib */
660 plugin_bopen = my_plugin_bopen;
661 plugin_bclose = my_plugin_bclose;
662 plugin_bread = my_plugin_bread;
663 plugin_bwrite = my_plugin_bwrite;
664 plugin_blseek = my_plugin_blseek;
667 * Verify that the plugin is acceptable, and print information
670 foreach_alist(plugin, plugin_list) {
671 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
672 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
675 dbg_plugin_add_hook(dump_fd_plugin);
679 * Check if a plugin is compatible. Called by the load_plugin function
680 * to allow us to verify the plugin.
682 static bool is_plugin_compatible(Plugin *plugin)
684 pInfo *info = (pInfo *)plugin->pinfo;
685 Dmsg0(50, "is_plugin_compatible called\n");
686 if (debug_level >= 50) {
687 dump_fd_plugin(plugin, stdin);
689 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
690 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
691 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
692 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
693 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
697 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
698 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
699 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
700 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
701 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
704 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
705 strcmp(info->plugin_license, "GPLv2") != 0) {
706 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
707 plugin->file, info->plugin_license);
708 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
709 plugin->file, info->plugin_license);
718 * Create a new instance of each plugin for this Job
719 * Note, plugin_list can exist but jcr->plugin_ctx_list can
720 * be NULL if no plugins were loaded.
722 void new_plugins(JCR *jcr)
728 Dmsg0(dbglvl, "plugin list is NULL\n");
731 if (jcr->is_job_canceled() || jcr->JobId == 0) {
735 int num = plugin_list->size();
738 Dmsg0(dbglvl, "No plugins loaded\n");
742 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
744 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
745 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
746 foreach_alist(plugin, plugin_list) {
747 /* Start a new instance of each plugin */
748 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
749 memset(b_ctx, 0, sizeof(bacula_ctx));
751 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
752 plugin_ctx_list[i].pContext = NULL;
753 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
754 b_ctx->disabled = true;
760 * Free the plugin instances for this Job
762 void free_plugins(JCR *jcr)
767 if (!plugin_list || !jcr->plugin_ctx_list) {
768 return; /* no plugins, nothing to do */
771 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
772 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
773 foreach_alist(plugin, plugin_list) {
774 /* Free the plugin instance */
775 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
776 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
778 free(plugin_ctx_list);
779 jcr->plugin_ctx_list = NULL;
782 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
785 Plugin *plugin = (Plugin *)jcr->plugin;
788 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
789 if (!plugin || !jcr->plugin_ctx) {
792 io.pkt_size = sizeof(io);
793 io.pkt_end = sizeof(io);
802 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
803 bfd->berrno = io.io_errno;
805 errno = b_errno_win32;
808 bfd->lerror = io.lerror;
810 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
814 static int my_plugin_bclose(BFILE *bfd)
817 Plugin *plugin = (Plugin *)jcr->plugin;
820 Dmsg0(dbglvl, "===== plugin_bclose\n");
821 if (!plugin || !jcr->plugin_ctx) {
824 io.pkt_size = sizeof(io);
825 io.pkt_end = sizeof(io);
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 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
843 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
846 Plugin *plugin = (Plugin *)jcr->plugin;
849 Dmsg0(dbglvl, "plugin_bread\n");
850 if (!plugin || !jcr->plugin_ctx) {
853 io.pkt_size = sizeof(io);
854 io.pkt_end = sizeof(io);
857 io.buf = (char *)buf;
860 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
861 bfd->berrno = io.io_errno;
863 errno = b_errno_win32;
866 bfd->lerror = io.lerror;
868 return (ssize_t)io.status;
871 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
874 Plugin *plugin = (Plugin *)jcr->plugin;
877 Dmsg0(dbglvl, "plugin_bwrite\n");
878 if (!plugin || !jcr->plugin_ctx) {
881 io.pkt_size = sizeof(io);
882 io.pkt_end = sizeof(io);
885 io.buf = (char *)buf;
888 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
889 bfd->berrno = io.io_errno;
891 errno = b_errno_win32;
894 bfd->lerror = io.lerror;
896 return (ssize_t)io.status;
899 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
902 Plugin *plugin = (Plugin *)jcr->plugin;
905 Dmsg0(dbglvl, "plugin_bseek\n");
906 if (!plugin || !jcr->plugin_ctx) {
909 io.pkt_size = sizeof(io);
910 io.pkt_end = sizeof(io);
916 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
917 bfd->berrno = io.io_errno;
919 errno = b_errno_win32;
922 bfd->lerror = io.lerror;
924 return (boffset_t)io.offset;
927 /* ==============================================================
929 * Callbacks from the plugin
931 * ==============================================================
933 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
936 if (!value || !ctx) {
939 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
940 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
944 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
947 *((int *)value) = jcr->JobId;
948 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
951 *((char **)value) = my_name;
952 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
955 *((int *)value) = jcr->getJobLevel();
956 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
959 *((int *)value) = jcr->getJobType();
960 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
963 *((char **)value) = jcr->client_name;
964 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
967 *((char **)value) = jcr->Job;
968 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
971 *((int *)value) = jcr->JobStatus;
972 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
975 *((int *)value) = (int)jcr->mtime;
976 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
979 *((int *)value) = (int)jcr->accurate;
980 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
983 break; /* a write only variable, ignore read request */
987 *(void **)value = g_pVSSClient->GetVssObject();
992 case bVarVssDllHandle:
995 *(void **)value = g_pVSSClient->GetVssDllHandle();
1000 case bVarWorkingDir:
1001 *(void **)value = me->working_directory;
1007 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1010 if (!value || !ctx) {
1013 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1014 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1018 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1021 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1031 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1040 va_start(args, ctx);
1041 while ((event = va_arg(args, uint32_t))) {
1042 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1048 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1049 int type, utime_t mtime, const char *fmt, ...)
1056 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1061 va_start(arg_ptr, fmt);
1062 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1064 Jmsg(jcr, type, mtime, "%s", buf);
1068 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1069 int level, const char *fmt, ...)
1074 va_start(arg_ptr, fmt);
1075 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1077 d_msg(file, line, level, "%s", buf);
1081 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1085 return sm_malloc(file, line, size);
1087 return malloc(size);
1091 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1094 sm_free(file, line, mem);
1100 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1105 bctx = (bacula_ctx *)ctx->bContext;
1117 * Let the plugin define files/directories to be excluded
1118 * from the main backup.
1120 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1124 if (!is_ctx_good(ctx, jcr, bctx)) {
1130 if (!bctx->exclude) {
1131 bctx->exclude = new_exclude(jcr);
1132 new_options(jcr, bctx->exclude);
1134 set_incexe(jcr, bctx->exclude);
1135 add_file_to_fileset(jcr, file, true);
1136 Dmsg1(100, "Add exclude file=%s\n", file);
1141 * Let the plugin define files/directories to be excluded
1142 * from the main backup.
1144 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1148 if (!is_ctx_good(ctx, jcr, bctx)) {
1154 if (!bctx->include) {
1155 bctx->include = new_preinclude(jcr);
1156 new_options(jcr, bctx->include);
1158 set_incexe(jcr, bctx->include);
1159 add_file_to_fileset(jcr, file, true);
1160 Dmsg1(100, "Add include file=%s\n", file);
1164 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1168 if (!is_ctx_good(ctx, jcr, bctx)) {
1174 add_options_to_fileset(jcr, opts);
1175 Dmsg1(1000, "Add options=%s\n", opts);
1179 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1183 if (!is_ctx_good(ctx, jcr, bctx)) {
1189 add_regex_to_fileset(jcr, item, type);
1190 Dmsg1(100, "Add regex=%s\n", item);
1194 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1198 if (!is_ctx_good(ctx, jcr, bctx)) {
1204 add_wild_to_fileset(jcr, item, type);
1205 Dmsg1(100, "Add wild=%s\n", item);
1209 static bRC baculaNewOptions(bpContext *ctx)
1213 if (!is_ctx_good(ctx, jcr, bctx)) {
1216 (void)new_options(jcr, NULL);
1220 static bRC baculaNewInclude(bpContext *ctx)
1224 if (!is_ctx_good(ctx, jcr, bctx)) {
1227 (void)new_include(jcr);
1234 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1235 int (*plugin_bclose)(JCR *jcr) = NULL;
1236 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1237 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1238 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1240 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1245 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1250 int main(int argc, char *argv[])
1252 char plugin_dir[1000];
1257 strcpy(my_name, "test-fd");
1259 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1260 load_fd_plugins(plugin_dir);
1268 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1269 generate_plugin_event(jcr1, bEventJobEnd);
1270 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1272 generate_plugin_event(jcr2, bEventJobEnd);
1277 Dmsg0(dbglvl, "bacula: OK ...\n");
1278 close_memory_pool();
1283 #endif /* TEST_PROGRAM */