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 bool is_plugin_compatible(Plugin *plugin);
68 * These will be plugged into the global pointer structure for
71 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
72 static int my_plugin_bclose(BFILE *bfd);
73 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
74 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
75 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
79 static bInfo binfo = {
81 FD_PLUGIN_INTERFACE_VERSION
84 /* Bacula entry points */
85 static bFuncs bfuncs = {
87 FD_PLUGIN_INTERFACE_VERSION,
98 * Bacula private context
101 JCR *jcr; /* jcr for plugin */
102 bRC rc; /* last return code */
103 bool disabled; /* set if plugin disabled */
106 static bool is_plugin_disabled(JCR *jcr)
109 if (!jcr->plugin_ctx) {
112 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
113 return b_ctx->disabled;
118 * Create a plugin event
120 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
126 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
127 return; /* Return if no plugins loaded */
130 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
131 event.eventType = eventType;
133 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
135 /* Pass event to every plugin */
136 foreach_alist(plugin, plugin_list) {
138 jcr->plugin_ctx = &plugin_ctx_list[i++];
139 jcr->plugin = plugin;
140 if (is_plugin_disabled(jcr)) {
143 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
150 jcr->plugin_ctx = NULL;
155 * Check if file was seen for accurate
157 bool plugin_check_file(JCR *jcr, char *fname)
163 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
164 return false; /* Return if no plugins loaded */
167 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
169 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
171 /* Pass event to every plugin */
172 foreach_alist(plugin, plugin_list) {
173 jcr->plugin_ctx = &plugin_ctx_list[i++];
174 jcr->plugin = plugin;
175 if (is_plugin_disabled(jcr)) {
178 if (plug_func(plugin)->checkFile == NULL) {
181 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
182 if (rc == bRC_Seen) {
188 jcr->plugin_ctx = NULL;
189 return rc == bRC_Seen;
194 * Sequence of calls for a backup:
195 * 1. plugin_save() here is called with ff_pkt
196 * 2. we find the plugin requested on the command string
197 * 3. we generate a bEventBackupCommand event to the specified plugin
198 * and pass it the command string.
199 * 4. we make a startPluginBackup call to the plugin, which gives
200 * us the data we need in save_pkt
201 * 5. we call Bacula's save_file() subroutine to save the specified
202 * file. The plugin will be called at pluginIO() to supply the
205 * Sequence of calls for restore:
206 * See subroutine plugin_name_stream() below.
208 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
214 char *cmd = ff_pkt->top_fname;
217 POOL_MEM fname(PM_FNAME);
218 POOL_MEM link(PM_FNAME);
220 if (!plugin_list || !jcr->plugin_ctx_list || job_canceled(jcr)) {
221 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
222 return 1; /* Return if no plugins loaded */
225 jcr->cmd_plugin = true;
226 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
227 event.eventType = bEventBackupCommand;
229 /* Handle plugin command here backup */
230 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
231 if (!(p = strchr(cmd, ':'))) {
232 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
240 /* Note, we stop the loop on the first plugin that matches the name */
241 foreach_alist(plugin, plugin_list) {
242 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
243 if (strncmp(plugin->file, cmd, len) != 0) {
248 * We put the current plugin pointer, and the plugin context
249 * into the jcr, because during save_file(), the plugin
250 * will be called many times and these values are needed.
252 jcr->plugin_ctx = &plugin_ctx_list[i];
253 jcr->plugin = plugin;
254 if (is_plugin_disabled(jcr)) {
258 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
259 /* Send the backup command to the right plugin*/
260 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
263 /* Loop getting filenames to backup then saving them */
264 while (!job_canceled(jcr)) {
265 memset(&sp, 0, sizeof(sp));
266 sp.pkt_size = sizeof(sp);
267 sp.pkt_end = sizeof(sp);
270 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
272 /* Get the file save parameters */
273 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
276 if (sp.type == 0 || sp.fname == NULL) {
277 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
281 jcr->plugin_sp = &sp;
284 * Copy fname and link because save_file() zaps them. This
285 * avoids zaping the plugin's strings.
287 pm_strcpy(fname, sp.fname);
288 pm_strcpy(link, sp.link);
289 ff_pkt->fname = fname.c_str();
290 ff_pkt->link = link.c_str();
291 ff_pkt->type = sp.type;
292 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
293 Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
294 save_file(jcr, ff_pkt, true);
295 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
296 if (rc == bRC_More || rc == bRC_OK) {
297 accurate_mark_file_as_seen(jcr, fname.c_str());
299 if (rc == bRC_More) {
306 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
309 jcr->cmd_plugin = false;
311 jcr->plugin_ctx = NULL;
316 * Send plugin name start/end record to SD
318 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
321 int index = jcr->JobFiles;
322 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
325 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
328 if (job_canceled(jcr)) {
333 index++; /* JobFiles not incremented yet */
335 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
336 /* Send stream header */
337 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
338 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
342 Dmsg1(50, "send: %s\n", sd->msg);
345 /* Send data -- not much */
346 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
348 /* Send end of data */
349 stat = sd->fsend("0 0");
352 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
356 Dmsg1(dbglvl, "send: %s\n", sd->msg);
357 sd->signal(BNET_EOD); /* indicate end of plugin name data */
362 * Plugin name stream found during restore. The record passed in
363 * argument name was generated in send_plugin_name() above.
365 * Returns: true if start of stream
366 * false if end of steam
368 bool plugin_name_stream(JCR *jcr, char *name)
372 bool start, portable;
376 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
378 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
379 skip_nonspaces(&p); /* skip over jcr->JobFiles */
383 /* Start of plugin data */
384 skip_nonspaces(&p); /* skip start/end flag */
386 portable = *p == '1';
387 skip_nonspaces(&p); /* skip portable flag */
392 * End of plugin data, notify plugin, then clear flags
394 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
396 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
398 jcr->plugin_ctx = NULL;
402 if (!plugin_ctx_list) {
407 * After this point, we are dealing with a restore start
410 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
411 if (!(p = strchr(cmd, ':'))) {
412 Jmsg1(jcr, M_ERROR, 0,
413 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
422 * Search for correct plugin as specified on the command
424 foreach_alist(plugin, plugin_list) {
426 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
427 if (strncmp(plugin->file, cmd, len) != 0) {
431 jcr->plugin_ctx = &plugin_ctx_list[i];
432 jcr->plugin = plugin;
433 if (is_plugin_disabled(jcr)) {
436 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
437 event.eventType = bEventRestoreCommand;
438 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
439 &event, cmd) != bRC_OK) {
442 /* ***FIXME**** check error code */
443 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
446 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
453 * Tell the plugin to create the file. Return values are
454 * This is called only during Restore
457 * CF_SKIP -- skip processing this file
458 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
459 * CF_CREATED -- created, but no content to extract (typically directories)
462 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
464 bpContext *plugin_ctx = jcr->plugin_ctx;
465 Plugin *plugin = jcr->plugin;
466 struct restore_pkt rp;
470 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || job_canceled(jcr)) {
473 rp.pkt_size = sizeof(rp);
474 rp.pkt_end = sizeof(rp);
475 rp.stream = attr->stream;
476 rp.data_stream = attr->data_stream;
477 rp.type = attr->type;
478 rp.file_index = attr->file_index;
479 rp.LinkFI = attr->LinkFI;
481 rp.statp = attr->statp; /* structure assignment */
482 rp.attrEx = attr->attrEx;
483 rp.ofname = attr->ofname;
484 rp.olname = attr->olname;
485 rp.where = jcr->where;
486 rp.RegexWhere = jcr->RegexWhere;
487 rp.replace = jcr->replace;
488 rp.create_status = CF_ERROR;
489 Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
490 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
492 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
496 if (rp.create_status == CF_ERROR) {
497 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
501 /* Created link or directory? */
502 if (rp.create_status == CF_CREATED) {
503 return rp.create_status; /* yes, no need to bopen */
506 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
507 Dmsg0(dbglvl, "call bopen\n");
508 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
509 Dmsg1(50, "bopen status=%d\n", stat);
512 be.set_errno(bfd->berrno);
513 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
514 attr->ofname, be.bstrerror());
515 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
519 if (!is_bopen(bfd)) {
520 Dmsg0(000, "===== BFD is not open!!!!\n");
526 * Reset the file attributes after all file I/O is done -- this allows
527 * the previous access time/dates to be set properly, and it also allows
528 * us to properly set directory permissions.
529 * Not currently Implemented.
531 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
533 Dmsg0(dbglvl, "plugin_set_attributes\n");
537 pm_strcpy(attr->ofname, "*none*");
542 * Print to file the plugin info.
544 void dump_fd_plugin(Plugin *plugin, FILE *fp)
549 pInfo *info = (pInfo *)plugin->pinfo;
550 fprintf(fp, "\tversion=%d\n", info->version);
551 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
552 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
553 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
554 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
555 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
556 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
560 * This entry point is called internally by Bacula to ensure
561 * that the plugin IO calls come into this code.
563 void load_fd_plugins(const char *plugin_dir)
568 Dmsg0(dbglvl, "plugin dir is NULL\n");
572 plugin_list = New(alist(10, not_owned_by_alist));
573 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
574 is_plugin_compatible)) {
575 /* Either none found, or some error */
576 if (plugin_list->size() == 0) {
579 Dmsg0(dbglvl, "No plugins loaded\n");
584 /* Plug entry points called from findlib */
585 plugin_bopen = my_plugin_bopen;
586 plugin_bclose = my_plugin_bclose;
587 plugin_bread = my_plugin_bread;
588 plugin_bwrite = my_plugin_bwrite;
589 plugin_blseek = my_plugin_blseek;
592 * Verify that the plugin is acceptable, and print information
595 foreach_alist(plugin, plugin_list) {
596 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
597 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
600 dbg_plugin_add_hook(dump_fd_plugin);
604 * Check if a plugin is compatible. Called by the load_plugin function
605 * to allow us to verify the plugin.
607 static bool is_plugin_compatible(Plugin *plugin)
609 pInfo *info = (pInfo *)plugin->pinfo;
610 Dmsg0(50, "is_plugin_compatible called\n");
611 if (debug_level >= 50) {
612 dump_fd_plugin(plugin, stdin);
614 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
615 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
616 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
617 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
618 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
622 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
623 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
624 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
625 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
626 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
629 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
630 strcmp(info->plugin_license, "GPLv2") != 0) {
631 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
632 plugin->file, info->plugin_license);
633 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
634 plugin->file, info->plugin_license);
643 * Create a new instance of each plugin for this Job
644 * Note, plugin_list can exist but jcr->plugin_ctx_list can
645 * be NULL if no plugins were loaded.
647 void new_plugins(JCR *jcr)
653 Dmsg0(dbglvl, "plugin list is NULL\n");
656 if (job_canceled(jcr)) {
660 int num = plugin_list->size();
663 Dmsg0(dbglvl, "No plugins loaded\n");
667 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
669 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
670 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
671 foreach_alist(plugin, plugin_list) {
672 /* Start a new instance of each plugin */
673 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
674 memset(b_ctx, 0, sizeof(bacula_ctx));
676 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
677 plugin_ctx_list[i].pContext = NULL;
678 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
679 b_ctx->disabled = true;
685 * Free the plugin instances for this Job
687 void free_plugins(JCR *jcr)
692 if (!plugin_list || !jcr->plugin_ctx_list) {
693 return; /* no plugins, nothing to do */
696 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
697 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
698 foreach_alist(plugin, plugin_list) {
699 /* Free the plugin instance */
700 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
701 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
703 free(plugin_ctx_list);
704 jcr->plugin_ctx_list = NULL;
707 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
710 Plugin *plugin = (Plugin *)jcr->plugin;
713 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
714 if (!plugin || !jcr->plugin_ctx) {
717 io.pkt_size = sizeof(io);
718 io.pkt_end = sizeof(io);
727 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
728 bfd->berrno = io.io_errno;
730 errno = b_errno_win32;
733 bfd->lerror = io.lerror;
735 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
739 static int my_plugin_bclose(BFILE *bfd)
742 Plugin *plugin = (Plugin *)jcr->plugin;
745 Dmsg0(dbglvl, "===== plugin_bclose\n");
746 if (!plugin || !jcr->plugin_ctx) {
749 io.pkt_size = sizeof(io);
750 io.pkt_end = sizeof(io);
756 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
757 bfd->berrno = io.io_errno;
759 errno = b_errno_win32;
762 bfd->lerror = io.lerror;
764 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
768 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
771 Plugin *plugin = (Plugin *)jcr->plugin;
774 Dmsg0(dbglvl, "plugin_bread\n");
775 if (!plugin || !jcr->plugin_ctx) {
778 io.pkt_size = sizeof(io);
779 io.pkt_end = sizeof(io);
782 io.buf = (char *)buf;
785 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
786 bfd->berrno = io.io_errno;
788 errno = b_errno_win32;
791 bfd->lerror = io.lerror;
793 return (ssize_t)io.status;
796 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
799 Plugin *plugin = (Plugin *)jcr->plugin;
802 Dmsg0(dbglvl, "plugin_bwrite\n");
803 if (!plugin || !jcr->plugin_ctx) {
806 io.pkt_size = sizeof(io);
807 io.pkt_end = sizeof(io);
810 io.buf = (char *)buf;
813 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
814 bfd->berrno = io.io_errno;
816 errno = b_errno_win32;
819 bfd->lerror = io.lerror;
821 return (ssize_t)io.status;
824 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
827 Plugin *plugin = (Plugin *)jcr->plugin;
830 Dmsg0(dbglvl, "plugin_bseek\n");
831 if (!plugin || !jcr->plugin_ctx) {
834 io.pkt_size = sizeof(io);
835 io.pkt_end = sizeof(io);
841 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
842 bfd->berrno = io.io_errno;
844 errno = b_errno_win32;
847 bfd->lerror = io.lerror;
849 return (boffset_t)io.offset;
852 /* ==============================================================
854 * Callbacks from the plugin
856 * ==============================================================
858 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
861 if (!value || !ctx) {
864 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
865 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
866 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
870 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
873 *((int *)value) = jcr->JobId;
874 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
877 *((char **)value) = my_name;
878 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
881 *((int *)value) = jcr->getJobLevel();
882 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
885 *((int *)value) = jcr->getJobType();
886 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
889 *((char **)value) = jcr->client_name;
890 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
893 *((char **)value) = jcr->Job;
894 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
897 *((int *)value) = jcr->JobStatus;
898 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
901 *((int *)value) = (int)jcr->mtime;
902 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
905 *((int *)value) = (int)jcr->accurate;
906 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
909 break; /* a write only variable, ignore read request */
914 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
917 if (!value || !ctx) {
920 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
921 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
922 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
926 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
929 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
939 static bRC baculaRegisterEvents(bpContext *ctx, ...)
949 while ((event = va_arg(args, uint32_t))) {
950 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
956 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
957 int type, utime_t mtime, const char *fmt, ...)
964 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
969 va_start(arg_ptr, fmt);
970 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
972 Jmsg(jcr, type, mtime, "%s", buf);
976 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
977 int level, const char *fmt, ...)
982 va_start(arg_ptr, fmt);
983 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
985 d_msg(file, line, level, "%s", buf);
989 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
993 return sm_malloc(file, line, size);
999 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1002 sm_free(file, line, mem);
1012 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1013 int (*plugin_bclose)(JCR *jcr) = NULL;
1014 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1015 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1016 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1018 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1023 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1028 int main(int argc, char *argv[])
1030 char plugin_dir[1000];
1035 strcpy(my_name, "test-fd");
1037 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1038 load_fd_plugins(plugin_dir);
1046 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1047 generate_plugin_event(jcr1, bEventJobEnd);
1048 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1050 generate_plugin_event(jcr2, bEventJobEnd);
1055 Dmsg0(dbglvl, "bacula: OK ...\n");
1056 close_memory_pool();
1061 #endif /* TEST_PROGRAM */