2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 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 return 1; /* Return if no plugins loaded */
224 jcr->cmd_plugin = true;
225 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
226 event.eventType = bEventBackupCommand;
228 /* Handle plugin command here backup */
229 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
230 if (!(p = strchr(cmd, ':'))) {
231 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
239 /* Note, we stop the loop on the first plugin that matches the name */
240 foreach_alist(plugin, plugin_list) {
241 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
242 if (strncmp(plugin->file, cmd, len) != 0) {
247 * We put the current plugin pointer, and the plugin context
248 * into the jcr, because during save_file(), the plugin
249 * will be called many times and these values are needed.
251 jcr->plugin_ctx = &plugin_ctx_list[i];
252 jcr->plugin = plugin;
253 if (is_plugin_disabled(jcr)) {
257 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
258 /* Send the backup command to the right plugin*/
259 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
262 /* Loop getting filenames to backup then saving them */
263 while (!job_canceled(jcr)) {
264 memset(&sp, 0, sizeof(sp));
265 sp.pkt_size = sizeof(sp);
266 sp.pkt_end = sizeof(sp);
269 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
271 /* Get the file save parameters */
272 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
275 if (sp.type == 0 || sp.fname == NULL) {
276 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
280 jcr->plugin_sp = &sp;
283 * Copy fname and link because save_file() zaps them. This
284 * avoids zaping the plugin's strings.
286 pm_strcpy(fname, sp.fname);
287 pm_strcpy(link, sp.link);
288 ff_pkt->fname = fname.c_str();
289 ff_pkt->link = link.c_str();
290 ff_pkt->type = sp.type;
291 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
292 Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
293 save_file(jcr, ff_pkt, true);
294 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
295 if (rc == bRC_More || rc == bRC_OK) {
296 accurate_mark_file_as_seen(jcr, fname.c_str());
298 if (rc == bRC_More) {
305 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
308 jcr->cmd_plugin = false;
310 jcr->plugin_ctx = NULL;
315 * Send plugin name start/end record to SD
317 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
320 int index = jcr->JobFiles;
321 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
324 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
327 if (job_canceled(jcr)) {
332 index++; /* JobFiles not incremented yet */
334 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
335 /* Send stream header */
336 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
337 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
341 Dmsg1(50, "send: %s\n", sd->msg);
344 /* Send data -- not much */
345 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
347 /* Send end of data */
348 stat = sd->fsend("0 0");
351 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
355 Dmsg1(dbglvl, "send: %s\n", sd->msg);
356 sd->signal(BNET_EOD); /* indicate end of plugin name data */
361 * Plugin name stream found during restore. The record passed in
362 * argument name was generated in send_plugin_name() above.
364 * Returns: true if start of stream
365 * false if end of steam
367 bool plugin_name_stream(JCR *jcr, char *name)
371 bool start, portable;
375 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
377 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
378 skip_nonspaces(&p); /* skip over jcr->JobFiles */
382 /* Start of plugin data */
383 skip_nonspaces(&p); /* skip start/end flag */
385 portable = *p == '1';
386 skip_nonspaces(&p); /* skip portable flag */
391 * End of plugin data, notify plugin, then clear flags
393 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
395 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
397 jcr->plugin_ctx = NULL;
401 if (!plugin_ctx_list) {
406 * After this point, we are dealing with a restore start
409 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
410 if (!(p = strchr(cmd, ':'))) {
411 Jmsg1(jcr, M_ERROR, 0,
412 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
421 * Search for correct plugin as specified on the command
423 foreach_alist(plugin, plugin_list) {
425 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
426 if (strncmp(plugin->file, cmd, len) != 0) {
430 jcr->plugin_ctx = &plugin_ctx_list[i];
431 jcr->plugin = plugin;
432 if (is_plugin_disabled(jcr)) {
435 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
436 event.eventType = bEventRestoreCommand;
437 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
438 &event, cmd) != bRC_OK) {
441 /* ***FIXME**** check error code */
442 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
445 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
452 * Tell the plugin to create the file. Return values are
453 * This is called only during Restore
456 * CF_SKIP -- skip processing this file
457 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
458 * CF_CREATED -- created, but no content to extract (typically directories)
461 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
463 bpContext *plugin_ctx = jcr->plugin_ctx;
464 Plugin *plugin = jcr->plugin;
465 struct restore_pkt rp;
469 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || job_canceled(jcr)) {
472 rp.pkt_size = sizeof(rp);
473 rp.pkt_end = sizeof(rp);
474 rp.stream = attr->stream;
475 rp.data_stream = attr->data_stream;
476 rp.type = attr->type;
477 rp.file_index = attr->file_index;
478 rp.LinkFI = attr->LinkFI;
480 rp.statp = attr->statp; /* structure assignment */
481 rp.attrEx = attr->attrEx;
482 rp.ofname = attr->ofname;
483 rp.olname = attr->olname;
484 rp.where = jcr->where;
485 rp.RegexWhere = jcr->RegexWhere;
486 rp.replace = jcr->replace;
487 rp.create_status = CF_ERROR;
488 Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
489 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
491 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
495 if (rp.create_status == CF_ERROR) {
496 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
500 /* Created link or directory? */
501 if (rp.create_status == CF_CREATED) {
502 return rp.create_status; /* yes, no need to bopen */
505 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
506 Dmsg0(dbglvl, "call bopen\n");
507 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
508 Dmsg1(50, "bopen status=%d\n", stat);
511 be.set_errno(bfd->berrno);
512 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
513 attr->ofname, be.bstrerror());
514 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
518 if (!is_bopen(bfd)) {
519 Dmsg0(000, "===== BFD is not open!!!!\n");
525 * Reset the file attributes after all file I/O is done -- this allows
526 * the previous access time/dates to be set properly, and it also allows
527 * us to properly set directory permissions.
528 * Not currently Implemented.
530 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
532 Dmsg0(dbglvl, "plugin_set_attributes\n");
536 pm_strcpy(attr->ofname, "*none*");
541 * Print to file the plugin info.
543 void dump_fd_plugin(Plugin *plugin, FILE *fp)
548 pInfo *info = (pInfo *)plugin->pinfo;
549 fprintf(fp, "\tversion=%d\n", info->version);
550 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
551 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
552 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
553 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
554 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
555 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
559 * This entry point is called internally by Bacula to ensure
560 * that the plugin IO calls come into this code.
562 void load_fd_plugins(const char *plugin_dir)
567 Dmsg0(dbglvl, "plugin dir is NULL\n");
571 plugin_list = New(alist(10, not_owned_by_alist));
572 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
573 is_plugin_compatible)) {
574 /* Either none found, or some error */
575 if (plugin_list->size() == 0) {
578 Dmsg0(dbglvl, "No plugins loaded\n");
583 /* Plug entry points called from findlib */
584 plugin_bopen = my_plugin_bopen;
585 plugin_bclose = my_plugin_bclose;
586 plugin_bread = my_plugin_bread;
587 plugin_bwrite = my_plugin_bwrite;
588 plugin_blseek = my_plugin_blseek;
591 * Verify that the plugin is acceptable, and print information
594 foreach_alist(plugin, plugin_list) {
595 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
596 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
599 dbg_plugin_add_hook(dump_fd_plugin);
603 * Check if a plugin is compatible. Called by the load_plugin function
604 * to allow us to verify the plugin.
606 static bool is_plugin_compatible(Plugin *plugin)
608 pInfo *info = (pInfo *)plugin->pinfo;
609 Dmsg0(50, "is_plugin_compatible called\n");
610 if (debug_level >= 50) {
611 dump_fd_plugin(plugin, stdin);
613 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
614 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
615 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
616 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
617 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
621 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
622 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
623 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
624 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
625 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
628 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
629 strcmp(info->plugin_license, "GPLv2") != 0) {
630 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
631 plugin->file, info->plugin_license);
632 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
633 plugin->file, info->plugin_license);
642 * Create a new instance of each plugin for this Job
643 * Note, plugin_list can exist but jcr->plugin_ctx_list can
644 * be NULL if no plugins were loaded.
646 void new_plugins(JCR *jcr)
652 Dmsg0(dbglvl, "plugin list is NULL\n");
655 if (job_canceled(jcr)) {
659 int num = plugin_list->size();
662 Dmsg0(dbglvl, "No plugins loaded\n");
666 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
668 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
669 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
670 foreach_alist(plugin, plugin_list) {
671 /* Start a new instance of each plugin */
672 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
673 memset(b_ctx, 0, sizeof(bacula_ctx));
675 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
676 plugin_ctx_list[i].pContext = NULL;
677 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
678 b_ctx->disabled = true;
684 * Free the plugin instances for this Job
686 void free_plugins(JCR *jcr)
691 if (!plugin_list || !jcr->plugin_ctx_list) {
692 return; /* no plugins, nothing to do */
695 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
696 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
697 foreach_alist(plugin, plugin_list) {
698 /* Free the plugin instance */
699 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
700 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
702 free(plugin_ctx_list);
703 jcr->plugin_ctx_list = NULL;
706 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
709 Plugin *plugin = (Plugin *)jcr->plugin;
712 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
713 io.pkt_size = sizeof(io);
714 io.pkt_end = sizeof(io);
723 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
724 bfd->berrno = io.io_errno;
726 errno = b_errno_win32;
729 bfd->lerror = io.lerror;
731 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
735 static int my_plugin_bclose(BFILE *bfd)
738 Plugin *plugin = (Plugin *)jcr->plugin;
740 Dmsg0(dbglvl, "===== plugin_bclose\n");
741 io.pkt_size = sizeof(io);
742 io.pkt_end = sizeof(io);
748 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
749 bfd->berrno = io.io_errno;
751 errno = b_errno_win32;
754 bfd->lerror = io.lerror;
756 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
760 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
763 Plugin *plugin = (Plugin *)jcr->plugin;
765 Dmsg0(dbglvl, "plugin_bread\n");
766 io.pkt_size = sizeof(io);
767 io.pkt_end = sizeof(io);
770 io.buf = (char *)buf;
773 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
774 bfd->berrno = io.io_errno;
776 errno = b_errno_win32;
779 bfd->lerror = io.lerror;
781 return (ssize_t)io.status;
784 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
787 Plugin *plugin = (Plugin *)jcr->plugin;
789 Dmsg0(dbglvl, "plugin_bwrite\n");
790 io.pkt_size = sizeof(io);
791 io.pkt_end = sizeof(io);
794 io.buf = (char *)buf;
797 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
798 bfd->berrno = io.io_errno;
800 errno = b_errno_win32;
803 bfd->lerror = io.lerror;
805 return (ssize_t)io.status;
808 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
811 Plugin *plugin = (Plugin *)jcr->plugin;
813 Dmsg0(dbglvl, "plugin_bseek\n");
814 io.pkt_size = sizeof(io);
815 io.pkt_end = sizeof(io);
821 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
822 bfd->berrno = io.io_errno;
824 errno = b_errno_win32;
827 bfd->lerror = io.lerror;
829 return (boffset_t)io.offset;
832 /* ==============================================================
834 * Callbacks from the plugin
836 * ==============================================================
838 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
841 if (!value || !ctx) {
844 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
845 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
846 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
850 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
853 *((int *)value) = jcr->JobId;
854 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
857 *((char **)value) = my_name;
858 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
861 *((int *)value) = jcr->get_JobLevel();
862 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->get_JobLevel());
865 *((int *)value) = jcr->get_JobType();
866 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->get_JobType());
869 *((char **)value) = jcr->client_name;
870 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
873 *((char **)value) = jcr->Job;
874 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
877 *((int *)value) = jcr->JobStatus;
878 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
881 *((int *)value) = (int)jcr->mtime;
882 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
885 *((int *)value) = (int)jcr->accurate;
886 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
889 break; /* a write only variable, ignore read request */
894 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
897 if (!value || !ctx) {
900 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
901 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
902 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
906 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
909 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
919 static bRC baculaRegisterEvents(bpContext *ctx, ...)
929 while ((event = va_arg(args, uint32_t))) {
930 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
936 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
937 int type, utime_t mtime, const char *fmt, ...)
944 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
949 va_start(arg_ptr, fmt);
950 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
952 Jmsg(jcr, type, mtime, "%s", buf);
956 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
957 int level, const char *fmt, ...)
962 va_start(arg_ptr, fmt);
963 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
965 d_msg(file, line, level, "%s", buf);
969 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
973 return sm_malloc(file, line, size);
979 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
982 sm_free(file, line, mem);
992 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
993 int (*plugin_bclose)(JCR *jcr) = NULL;
994 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
995 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
996 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
998 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1003 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1008 int main(int argc, char *argv[])
1010 char plugin_dir[1000];
1015 strcpy(my_name, "test-fd");
1017 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1018 load_fd_plugins(plugin_dir);
1026 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1027 generate_plugin_event(jcr1, bEventJobEnd);
1028 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1030 generate_plugin_event(jcr2, bEventJobEnd);
1035 Dmsg0(dbglvl, "bacula: OK ...\n");
1036 close_memory_pool();
1041 #endif /* TEST_PROGRAM */