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 three of the GNU Affero 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 Affero 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
40 const int dbglvl = 150;
42 const char *plugin_type = "-fd.dll";
44 const char *plugin_type = "-fd.so";
47 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
48 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
50 /* Function pointers to be set here */
51 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
52 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
53 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
54 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
55 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
58 /* Forward referenced functions */
59 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
60 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
61 static bRC baculaRegisterEvents(bpContext *ctx, ...);
62 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
63 int type, utime_t mtime, const char *fmt, ...);
64 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
65 int level, const char *fmt, ...);
66 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
68 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
69 static bRC baculaAddExclude(bpContext *ctx, const char *file);
70 static bRC baculaAddInclude(bpContext *ctx, const char *file);
71 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
72 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
73 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
74 static bRC baculaNewOptions(bpContext *ctx);
75 static bRC baculaNewInclude(bpContext *ctx);
76 static bool is_plugin_compatible(Plugin *plugin);
77 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
78 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
81 * These will be plugged into the global pointer structure for
84 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
85 static int my_plugin_bclose(BFILE *bfd);
86 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
87 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
88 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
92 static bInfo binfo = {
94 FD_PLUGIN_INTERFACE_VERSION
97 /* Bacula entry points */
98 static bFuncs bfuncs = {
100 FD_PLUGIN_INTERFACE_VERSION,
101 baculaRegisterEvents,
119 * Bacula private context
122 JCR *jcr; /* jcr for plugin */
123 bRC rc; /* last return code */
124 bool disabled; /* set if plugin disabled */
125 findINCEXE *exclude; /* pointer to exclude files */
126 findINCEXE *include; /* pointer to include/exclude files */
129 static bool is_plugin_disabled(bpContext *plugin_ctx)
135 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
136 return b_ctx->disabled;
139 static bool is_plugin_disabled(JCR *jcr)
141 return is_plugin_disabled(jcr->plugin_ctx);
145 * Create a plugin event
146 * When receiving bEventCancelCommand, this function is called by an other thread.
148 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
150 bpContext *plugin_ctx;
158 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
159 return; /* Return if no plugins loaded */
162 /* Some events are sent to only a particular plugin */
164 case bEventPluginCommand:
165 name = (char *)value;
166 if (!get_plugin_name(jcr, name, &len)) {
174 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
175 event.eventType = eventType;
177 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
179 /* Pass event to every plugin (except if name is set) */
180 foreach_alist(plugin, plugin_list) {
181 if (name && strncmp(plugin->file, name, len) != 0) {
185 plugin_ctx = &plugin_ctx_list[i++];
186 if (is_plugin_disabled(plugin_ctx)) {
189 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
198 * Check if file was seen for accurate
200 bool plugin_check_file(JCR *jcr, char *fname)
206 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
207 return false; /* Return if no plugins loaded */
210 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
212 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
214 /* Pass event to every plugin */
215 foreach_alist(plugin, plugin_list) {
216 jcr->plugin_ctx = &plugin_ctx_list[i++];
217 jcr->plugin = plugin;
218 if (is_plugin_disabled(jcr)) {
221 if (plug_func(plugin)->checkFile == NULL) {
224 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
225 if (rc == bRC_Seen) {
231 jcr->plugin_ctx = NULL;
232 return rc == bRC_Seen;
235 /* Get the first part of the the plugin command
236 * systemstate:/@SYSTEMSTATE/
238 * => can use strncmp(plugin_name, cmd, ret);
240 * The plugin command can contain only the plugin name
244 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
248 if (!cmd || (*cmd == '\0')) {
251 /* Handle plugin command here backup */
252 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
253 if ((p = strchr(cmd, ':')) == NULL) {
254 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
257 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
260 } else { /* plugin:argument */
271 * Sequence of calls for a backup:
272 * 1. plugin_save() here is called with ff_pkt
273 * 2. we find the plugin requested on the command string
274 * 3. we generate a bEventBackupCommand event to the specified plugin
275 * and pass it the command string.
276 * 4. we make a startPluginBackup call to the plugin, which gives
277 * us the data we need in save_pkt
278 * 5. we call Bacula's save_file() subroutine to save the specified
279 * file. The plugin will be called at pluginIO() to supply the
282 * Sequence of calls for restore:
283 * See subroutine plugin_name_stream() below.
285 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
290 char *cmd = ff_pkt->top_fname;
293 POOL_MEM fname(PM_FNAME);
294 POOL_MEM link(PM_FNAME);
296 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
297 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
298 return 1; /* Return if no plugins loaded */
301 jcr->cmd_plugin = true;
302 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
303 event.eventType = bEventBackupCommand;
305 if (!get_plugin_name(jcr, cmd, &len)) {
309 /* Note, we stop the loop on the first plugin that matches the name */
310 foreach_alist(plugin, plugin_list) {
311 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
312 if (strncmp(plugin->file, cmd, len) != 0) {
317 * We put the current plugin pointer, and the plugin context
318 * into the jcr, because during save_file(), the plugin
319 * will be called many times and these values are needed.
321 jcr->plugin_ctx = &plugin_ctx_list[i];
322 jcr->plugin = plugin;
323 if (is_plugin_disabled(jcr)) {
327 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
328 /* Send the backup command to the right plugin*/
329 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
332 /* Loop getting filenames to backup then saving them */
333 while (!jcr->is_job_canceled()) {
334 memset(&sp, 0, sizeof(sp));
335 sp.pkt_size = sizeof(sp);
336 sp.pkt_end = sizeof(sp);
339 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
341 /* Get the file save parameters. I.e. the stat pkt ... */
342 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
346 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
350 jcr->plugin_sp = &sp;
353 * Copy fname and link because save_file() zaps them. This
354 * avoids zaping the plugin's strings.
356 ff_pkt->type = sp.type;
357 if (sp.type == FT_RESTORE_FIRST) {
358 if (!sp.object_name) {
359 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
363 ff_pkt->fname = cmd; /* full plugin string */
364 ff_pkt->object_name = sp.object_name;
365 ff_pkt->object_index = sp.index; /* restore object index */
366 ff_pkt->object_compression = 0; /* no compression for now */
367 ff_pkt->object = sp.object;
368 ff_pkt->object_len = sp.object_len;
371 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
375 pm_strcpy(fname, sp.fname);
376 pm_strcpy(link, sp.link);
377 ff_pkt->fname = fname.c_str();
378 ff_pkt->link = link.c_str();
381 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
382 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
384 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
386 /* Call Bacula core code to backup the plugin's file */
387 save_file(jcr, ff_pkt, true);
388 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
389 if (rc == bRC_More || rc == bRC_OK) {
390 accurate_mark_file_as_seen(jcr, fname.c_str());
392 if (rc == bRC_More) {
399 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
402 jcr->cmd_plugin = false;
404 jcr->plugin_ctx = NULL;
409 * Send plugin name start/end record to SD
411 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
414 int index = jcr->JobFiles;
415 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
418 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
421 if (jcr->is_job_canceled()) {
426 index++; /* JobFiles not incremented yet */
428 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
429 /* Send stream header */
430 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
431 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
435 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
438 /* Send data -- not much */
439 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
441 /* Send end of data */
442 stat = sd->fsend("0 0");
445 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
449 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
450 sd->signal(BNET_EOD); /* indicate end of plugin name data */
455 * Plugin name stream found during restore. The record passed in
456 * argument name was generated in send_plugin_name() above.
458 * Returns: true if start of stream
459 * false if end of steam
461 bool plugin_name_stream(JCR *jcr, char *name)
465 bool start, portable;
469 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
471 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
472 skip_nonspaces(&p); /* skip over jcr->JobFiles */
476 /* Start of plugin data */
477 skip_nonspaces(&p); /* skip start/end flag */
479 portable = *p == '1';
480 skip_nonspaces(&p); /* skip portable flag */
485 * End of plugin data, notify plugin, then clear flags
487 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
489 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
491 jcr->plugin_ctx = NULL;
495 if (!plugin_ctx_list) {
500 * After this point, we are dealing with a restore start
502 if (!get_plugin_name(jcr, cmd, &len)) {
507 * Search for correct plugin as specified on the command
509 foreach_alist(plugin, plugin_list) {
511 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
512 if (strncmp(plugin->file, cmd, len) != 0) {
516 jcr->plugin_ctx = &plugin_ctx_list[i];
517 jcr->plugin = plugin;
518 if (is_plugin_disabled(jcr)) {
521 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
522 event.eventType = bEventRestoreCommand;
523 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
524 &event, cmd) != bRC_OK) {
527 /* ***FIXME**** check error code */
528 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
531 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
538 * Tell the plugin to create the file. Return values are
539 * This is called only during Restore
542 * CF_SKIP -- skip processing this file
543 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
544 * CF_CREATED -- created, but no content to extract (typically directories)
547 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
549 bpContext *plugin_ctx = jcr->plugin_ctx;
550 Plugin *plugin = jcr->plugin;
551 struct restore_pkt rp;
555 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
559 rp.pkt_size = sizeof(rp);
560 rp.pkt_end = sizeof(rp);
561 rp.stream = attr->stream;
562 rp.data_stream = attr->data_stream;
563 rp.type = attr->type;
564 rp.file_index = attr->file_index;
565 rp.LinkFI = attr->LinkFI;
567 rp.statp = attr->statp; /* structure assignment */
568 rp.attrEx = attr->attrEx;
569 rp.ofname = attr->ofname;
570 rp.olname = attr->olname;
571 rp.where = jcr->where;
572 rp.RegexWhere = jcr->RegexWhere;
573 rp.replace = jcr->replace;
574 rp.create_status = CF_ERROR;
575 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
576 rp.stream, rp.type, rp.LinkFI, rp.ofname);
578 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
580 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
582 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
586 if (rp.create_status == CF_SKIP) {
589 if (rp.create_status == CF_ERROR) {
590 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
594 /* Created link or directory? */
595 if (rp.create_status == CF_CREATED) {
596 return rp.create_status; /* yes, no need to bopen */
599 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
600 Dmsg0(dbglvl, "call bopen\n");
601 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
602 Dmsg1(50, "bopen status=%d\n", stat);
605 be.set_errno(bfd->berrno);
606 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
607 attr->ofname, be.bstrerror());
608 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
612 if (!is_bopen(bfd)) {
613 Dmsg0(000, "===== BFD is not open!!!!\n");
619 * Reset the file attributes after all file I/O is done -- this allows
620 * the previous access time/dates to be set properly, and it also allows
621 * us to properly set directory permissions.
622 * Not currently Implemented.
624 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
626 Dmsg0(dbglvl, "plugin_set_attributes\n");
630 pm_strcpy(attr->ofname, "*none*");
635 * Print to file the plugin info.
637 void dump_fd_plugin(Plugin *plugin, FILE *fp)
642 pInfo *info = (pInfo *)plugin->pinfo;
643 fprintf(fp, "\tversion=%d\n", info->version);
644 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
645 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
646 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
647 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
648 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
649 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
653 * This entry point is called internally by Bacula to ensure
654 * that the plugin IO calls come into this code.
656 void load_fd_plugins(const char *plugin_dir)
661 Dmsg0(dbglvl, "plugin dir is NULL\n");
665 plugin_list = New(alist(10, not_owned_by_alist));
666 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
667 is_plugin_compatible)) {
668 /* Either none found, or some error */
669 if (plugin_list->size() == 0) {
672 Dmsg0(dbglvl, "No plugins loaded\n");
677 /* Plug entry points called from findlib */
678 plugin_bopen = my_plugin_bopen;
679 plugin_bclose = my_plugin_bclose;
680 plugin_bread = my_plugin_bread;
681 plugin_bwrite = my_plugin_bwrite;
682 plugin_blseek = my_plugin_blseek;
685 * Verify that the plugin is acceptable, and print information
688 foreach_alist(plugin, plugin_list) {
689 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
690 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
693 dbg_plugin_add_hook(dump_fd_plugin);
697 * Check if a plugin is compatible. Called by the load_plugin function
698 * to allow us to verify the plugin.
700 static bool is_plugin_compatible(Plugin *plugin)
702 pInfo *info = (pInfo *)plugin->pinfo;
703 Dmsg0(50, "is_plugin_compatible called\n");
704 if (debug_level >= 50) {
705 dump_fd_plugin(plugin, stdin);
707 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
708 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
709 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
710 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
711 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
715 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
716 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
717 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
718 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
719 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
722 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
723 strcmp(info->plugin_license, "AGPLv3") != 0) {
724 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
725 plugin->file, info->plugin_license);
726 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
727 plugin->file, info->plugin_license);
736 * Create a new instance of each plugin for this Job
737 * Note, plugin_list can exist but jcr->plugin_ctx_list can
738 * be NULL if no plugins were loaded.
740 void new_plugins(JCR *jcr)
746 Dmsg0(dbglvl, "plugin list is NULL\n");
749 if (jcr->is_job_canceled() || jcr->JobId == 0) {
753 int num = plugin_list->size();
756 Dmsg0(dbglvl, "No plugins loaded\n");
760 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
762 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
763 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
764 foreach_alist(plugin, plugin_list) {
765 /* Start a new instance of each plugin */
766 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
767 memset(b_ctx, 0, sizeof(bacula_ctx));
769 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
770 plugin_ctx_list[i].pContext = NULL;
771 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
772 b_ctx->disabled = true;
778 * Free the plugin instances for this Job
780 void free_plugins(JCR *jcr)
785 if (!plugin_list || !jcr->plugin_ctx_list) {
786 return; /* no plugins, nothing to do */
789 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
790 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
791 foreach_alist(plugin, plugin_list) {
792 /* Free the plugin instance */
793 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
794 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
796 free(plugin_ctx_list);
797 jcr->plugin_ctx_list = NULL;
800 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
803 Plugin *plugin = (Plugin *)jcr->plugin;
806 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
807 if (!plugin || !jcr->plugin_ctx) {
810 io.pkt_size = sizeof(io);
811 io.pkt_end = sizeof(io);
820 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
821 bfd->berrno = io.io_errno;
823 errno = b_errno_win32;
826 bfd->lerror = io.lerror;
828 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
832 static int my_plugin_bclose(BFILE *bfd)
835 Plugin *plugin = (Plugin *)jcr->plugin;
838 Dmsg0(dbglvl, "===== plugin_bclose\n");
839 if (!plugin || !jcr->plugin_ctx) {
842 io.pkt_size = sizeof(io);
843 io.pkt_end = sizeof(io);
849 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
850 bfd->berrno = io.io_errno;
852 errno = b_errno_win32;
855 bfd->lerror = io.lerror;
857 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
861 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
864 Plugin *plugin = (Plugin *)jcr->plugin;
867 Dmsg0(dbglvl, "plugin_bread\n");
868 if (!plugin || !jcr->plugin_ctx) {
871 io.pkt_size = sizeof(io);
872 io.pkt_end = sizeof(io);
875 io.buf = (char *)buf;
878 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
879 bfd->berrno = io.io_errno;
881 errno = b_errno_win32;
884 bfd->lerror = io.lerror;
886 return (ssize_t)io.status;
889 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
892 Plugin *plugin = (Plugin *)jcr->plugin;
895 Dmsg0(dbglvl, "plugin_bwrite\n");
896 if (!plugin || !jcr->plugin_ctx) {
899 io.pkt_size = sizeof(io);
900 io.pkt_end = sizeof(io);
903 io.buf = (char *)buf;
906 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
907 bfd->berrno = io.io_errno;
909 errno = b_errno_win32;
912 bfd->lerror = io.lerror;
914 return (ssize_t)io.status;
917 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
920 Plugin *plugin = (Plugin *)jcr->plugin;
923 Dmsg0(dbglvl, "plugin_bseek\n");
924 if (!plugin || !jcr->plugin_ctx) {
927 io.pkt_size = sizeof(io);
928 io.pkt_end = sizeof(io);
934 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
935 bfd->berrno = io.io_errno;
937 errno = b_errno_win32;
940 bfd->lerror = io.lerror;
942 return (boffset_t)io.offset;
945 /* ==============================================================
947 * Callbacks from the plugin
949 * ==============================================================
951 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
958 switch (var) { /* General variables, no need of ctx */
960 *((char **)value) = my_name;
963 *(void **)value = me->working_directory;
966 *(char **)value = exepath;
972 if (!ctx) { /* Other variables need context */
976 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
983 *((int *)value) = jcr->JobId;
984 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
987 *((int *)value) = jcr->getJobLevel();
988 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
991 *((int *)value) = jcr->getJobType();
992 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
995 *((char **)value) = jcr->client_name;
996 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
999 *((char **)value) = jcr->Job;
1000 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1003 *((int *)value) = jcr->JobStatus;
1004 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1007 *((int *)value) = (int)jcr->mtime;
1008 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1011 *((int *)value) = (int)jcr->accurate;
1012 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1015 break; /* a write only variable, ignore read request */
1019 *(void **)value = g_pVSSClient->GetVssObject();
1024 case bVarVssDllHandle:
1027 *(void **)value = g_pVSSClient->GetVssDllHandle();
1033 *(char **)value = jcr->where;
1035 case bVarRegexWhere:
1036 *(char **)value = jcr->RegexWhere;
1039 case bVarFDName: /* get warning with g++ if we missed one */
1040 case bVarWorkingDir:
1047 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1050 if (!value || !ctx) {
1053 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1054 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1058 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1061 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1071 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1080 va_start(args, ctx);
1081 while ((event = va_arg(args, uint32_t))) {
1082 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1088 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1089 int type, utime_t mtime, const char *fmt, ...)
1096 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1101 va_start(arg_ptr, fmt);
1102 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1104 Jmsg(jcr, type, mtime, "%s", buf);
1108 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1109 int level, const char *fmt, ...)
1114 va_start(arg_ptr, fmt);
1115 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1117 d_msg(file, line, level, "%s", buf);
1121 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1125 return sm_malloc(file, line, size);
1127 return malloc(size);
1131 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1134 sm_free(file, line, mem);
1140 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1145 bctx = (bacula_ctx *)ctx->bContext;
1157 * Let the plugin define files/directories to be excluded
1158 * from the main backup.
1160 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1164 if (!is_ctx_good(ctx, jcr, bctx)) {
1170 if (!bctx->exclude) {
1171 bctx->exclude = new_exclude(jcr);
1172 new_options(jcr, bctx->exclude);
1174 set_incexe(jcr, bctx->exclude);
1175 add_file_to_fileset(jcr, file, true);
1176 Dmsg1(100, "Add exclude file=%s\n", file);
1181 * Let the plugin define files/directories to be excluded
1182 * from the main backup.
1184 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1188 if (!is_ctx_good(ctx, jcr, bctx)) {
1194 if (!bctx->include) {
1195 bctx->include = new_preinclude(jcr);
1196 new_options(jcr, bctx->include);
1198 set_incexe(jcr, bctx->include);
1199 add_file_to_fileset(jcr, file, true);
1200 Dmsg1(100, "Add include file=%s\n", file);
1204 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1208 if (!is_ctx_good(ctx, jcr, bctx)) {
1214 add_options_to_fileset(jcr, opts);
1215 Dmsg1(1000, "Add options=%s\n", opts);
1219 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1223 if (!is_ctx_good(ctx, jcr, bctx)) {
1229 add_regex_to_fileset(jcr, item, type);
1230 Dmsg1(100, "Add regex=%s\n", item);
1234 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1238 if (!is_ctx_good(ctx, jcr, bctx)) {
1244 add_wild_to_fileset(jcr, item, type);
1245 Dmsg1(100, "Add wild=%s\n", item);
1249 static bRC baculaNewOptions(bpContext *ctx)
1253 if (!is_ctx_good(ctx, jcr, bctx)) {
1256 (void)new_options(jcr, NULL);
1260 static bRC baculaNewInclude(bpContext *ctx)
1264 if (!is_ctx_good(ctx, jcr, bctx)) {
1267 (void)new_include(jcr);
1273 * Check if a file have to be backuped using Accurate code
1275 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1280 bRC ret = bRC_Error;
1282 if (!is_ctx_good(ctx, jcr, bctx)) {
1291 * Copy fname and link because save_file() zaps them. This
1292 * avoids zaping the plugin's strings.
1294 ff_pkt->type = sp->type;
1296 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1300 ff_pkt->fname = sp->fname;
1301 ff_pkt->link = sp->link;
1302 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1304 if (check_changes(jcr, ff_pkt)) {
1311 Dmsg1(100, "checkChanges=%i\n", ret);
1318 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1319 int (*plugin_bclose)(JCR *jcr) = NULL;
1320 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1321 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1322 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1324 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1329 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1334 int main(int argc, char *argv[])
1336 char plugin_dir[1000];
1341 strcpy(my_name, "test-fd");
1343 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1344 load_fd_plugins(plugin_dir);
1352 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1353 generate_plugin_event(jcr1, bEventJobEnd);
1354 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1356 generate_plugin_event(jcr2, bEventJobEnd);
1361 Dmsg0(dbglvl, "bacula: OK ...\n");
1362 close_memory_pool();
1363 sm_dump(false); /* unit test */
1367 #endif /* TEST_PROGRAM */