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);
67 * These will be plugged into the global pointer structure for
70 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
71 static int my_plugin_bclose(BFILE *bfd);
72 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
73 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
74 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
78 static bInfo binfo = {
80 FD_PLUGIN_INTERFACE_VERSION
83 /* Bacula entry points */
84 static bFuncs bfuncs = {
86 FD_PLUGIN_INTERFACE_VERSION,
97 * Bacula private context
100 JCR *jcr; /* jcr for plugin */
101 bRC rc; /* last return code */
102 bool disabled; /* set if plugin disabled */
105 static bool is_plugin_disabled(JCR *jcr)
108 if (!jcr->plugin_ctx) {
111 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
112 return b_ctx->disabled;
117 * Create a plugin event
119 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
125 if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
126 return; /* Return if no plugins loaded */
129 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
130 event.eventType = eventType;
132 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
134 /* Pass event to every plugin */
135 foreach_alist(plugin, plugin_list) {
137 jcr->plugin_ctx = &plugin_ctx_list[i++];
138 jcr->plugin = plugin;
139 if (is_plugin_disabled(jcr)) {
142 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
149 jcr->plugin_ctx = NULL;
154 * Check if file was seen for accurate
156 bool plugin_check_file(JCR *jcr, char *fname)
162 if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
163 return false; /* Return if no plugins loaded */
166 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
168 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
170 /* Pass event to every plugin */
171 foreach_alist(plugin, plugin_list) {
172 jcr->plugin_ctx = &plugin_ctx_list[i++];
173 jcr->plugin = plugin;
174 if (is_plugin_disabled(jcr)) {
177 if (plug_func(plugin)->checkFile == NULL) {
180 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
181 if (rc == bRC_Seen) {
187 jcr->plugin_ctx = NULL;
188 return rc == bRC_Seen;
193 * Sequence of calls for a backup:
194 * 1. plugin_save() here is called with ff_pkt
195 * 2. we find the plugin requested on the command string
196 * 3. we generate a bEventBackupCommand event to the specified plugin
197 * and pass it the command string.
198 * 4. we make a startPluginBackup call to the plugin, which gives
199 * us the data we need in save_pkt
200 * 5. we call Bacula's save_file() subroutine to save the specified
201 * file. The plugin will be called at pluginIO() to supply the
204 * Sequence of calls for restore:
205 * See subroutine plugin_name_stream() below.
207 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
213 char *cmd = ff_pkt->top_fname;
217 if (!plugin_list || !jcr->plugin_ctx_list) {
218 return 1; /* Return if no plugins loaded */
221 jcr->cmd_plugin = true;
222 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
223 event.eventType = bEventBackupCommand;
225 /* Handle plugin command here backup */
226 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
227 if (!(p = strchr(cmd, ':'))) {
228 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
236 /* Note, we stop the loop on the first plugin that matches the name */
237 foreach_alist(plugin, plugin_list) {
238 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
239 if (strncmp(plugin->file, cmd, len) != 0) {
244 * We put the current plugin pointer, and the plugin context
245 * into the jcr, because during save_file(), the plugin
246 * will be called many times and these values are needed.
248 jcr->plugin_ctx = &plugin_ctx_list[i];
249 jcr->plugin = plugin;
250 if (is_plugin_disabled(jcr)) {
254 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
255 /* Send the backup command to the right plugin*/
256 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
259 /* Loop getting filenames to backup then saving them */
260 while (!job_canceled(jcr)) {
261 memset(&sp, 0, sizeof(sp));
262 sp.pkt_size = sizeof(sp);
263 sp.pkt_end = sizeof(sp);
266 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
268 /* Get the file save parameters */
269 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
272 if (sp.type == 0 || sp.fname == NULL) {
273 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
277 jcr->plugin_sp = &sp;
279 ff_pkt->fname = sp.fname;
280 ff_pkt->link = sp.link;
281 ff_pkt->type = sp.type;
282 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
283 Dmsg1(dbglvl, "Save_file: file=%s\n", ff_pkt->fname);
284 save_file(jcr, ff_pkt, true);
285 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
286 if (rc == bRC_More) {
293 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
296 jcr->cmd_plugin = false;
298 jcr->plugin_ctx = NULL;
303 * Send plugin name start/end record to SD
305 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
308 int index = jcr->JobFiles;
309 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
312 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
317 index++; /* JobFiles not incremented yet */
319 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
320 /* Send stream header */
321 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
322 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
326 Dmsg1(50, "send: %s\n", sd->msg);
329 /* Send data -- not much */
330 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
332 /* Send end of data */
333 stat = sd->fsend("0 0");
336 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
340 Dmsg1(dbglvl, "send: %s\n", sd->msg);
341 sd->signal(BNET_EOD); /* indicate end of plugin name data */
346 * Plugin name stream found during restore. The record passed in
347 * argument name was generated in send_plugin_name() above.
349 * Returns: true if start of stream
350 * false if end of steam
352 bool plugin_name_stream(JCR *jcr, char *name)
356 bool start, portable;
360 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
362 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
363 skip_nonspaces(&p); /* skip over jcr->JobFiles */
367 /* Start of plugin data */
368 skip_nonspaces(&p); /* skip start/end flag */
370 portable = *p == '1';
371 skip_nonspaces(&p); /* skip portable flag */
376 * End of plugin data, notify plugin, then clear flags
378 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
380 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
382 jcr->plugin_ctx = NULL;
386 if (!plugin_ctx_list) {
391 * After this point, we are dealing with a restore start
394 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
395 if (!(p = strchr(cmd, ':'))) {
396 Jmsg1(jcr, M_ERROR, 0,
397 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
406 * Search for correct plugin as specified on the command
408 foreach_alist(plugin, plugin_list) {
410 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
411 if (strncmp(plugin->file, cmd, len) != 0) {
415 jcr->plugin_ctx = &plugin_ctx_list[i];
416 jcr->plugin = plugin;
417 if (is_plugin_disabled(jcr)) {
420 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
421 event.eventType = bEventRestoreCommand;
422 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
423 &event, cmd) != bRC_OK) {
426 /* ***FIXME**** check error code */
427 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
430 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
437 * Tell the plugin to create the file. Return values are
440 * CF_SKIP -- skip processing this file
441 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
442 * CF_CREATED -- created, but no content to extract (typically directories)
445 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
447 bpContext *plugin_ctx = jcr->plugin_ctx;
448 Plugin *plugin = jcr->plugin;
449 struct restore_pkt rp;
453 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr)) {
456 rp.pkt_size = sizeof(rp);
457 rp.pkt_end = sizeof(rp);
458 rp.stream = attr->stream;
459 rp.data_stream = attr->data_stream;
460 rp.type = attr->type;
461 rp.file_index = attr->file_index;
462 rp.LinkFI = attr->LinkFI;
464 rp.statp = attr->statp; /* structure assignment */
465 rp.attrEx = attr->attrEx;
466 rp.ofname = attr->ofname;
467 rp.olname = attr->olname;
468 rp.where = jcr->where;
469 rp.RegexWhere = jcr->RegexWhere;
470 rp.replace = jcr->replace;
471 rp.create_status = CF_ERROR;
472 Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
473 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
475 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
479 if (rp.create_status == CF_ERROR) {
480 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
484 /* Created link or directory? */
485 if (rp.create_status == CF_CREATED) {
486 return rp.create_status; /* yes, no need to bopen */
489 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
490 Dmsg0(dbglvl, "call bopen\n");
491 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
492 Dmsg1(50, "bopen status=%d\n", stat);
495 be.set_errno(bfd->berrno);
496 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
497 attr->ofname, be.bstrerror());
498 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
502 if (!is_bopen(bfd)) {
503 Dmsg0(000, "===== BFD is not open!!!!\n");
509 * Reset the file attributes after all file I/O is done -- this allows
510 * the previous access time/dates to be set properly, and it also allows
511 * us to properly set directory permissions.
513 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
515 Dmsg0(dbglvl, "plugin_set_attributes\n");
519 pm_strcpy(attr->ofname, "*none*");
523 void dump_fd_plugin(Plugin *plugin, FILE *fp)
528 pInfo *info = (pInfo *) plugin->pinfo;
529 fprintf(fp, "\tversion=%d\n", info->version);
530 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
531 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
532 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
533 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
534 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
535 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
539 * This entry point is called internally by Bacula to ensure
540 * that the plugin IO calls come into this code.
542 void load_fd_plugins(const char *plugin_dir)
547 Dmsg0(dbglvl, "plugin dir is NULL\n");
551 plugin_list = New(alist(10, not_owned_by_alist));
552 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
553 /* Either none found, or some error */
554 if (plugin_list->size() == 0) {
557 Dmsg0(dbglvl, "No plugins loaded\n");
562 /* Plug entry points called from findlib */
563 plugin_bopen = my_plugin_bopen;
564 plugin_bclose = my_plugin_bclose;
565 plugin_bread = my_plugin_bread;
566 plugin_bwrite = my_plugin_bwrite;
567 plugin_blseek = my_plugin_blseek;
568 foreach_alist(plugin, plugin_list) {
569 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
570 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
574 dbg_plugin_add_hook(dump_fd_plugin);
578 * Create a new instance of each plugin for this Job
579 * Note, plugin_list can exist but jcr->plugin_ctx_list can
580 * be NULL if no plugins were loaded.
582 void new_plugins(JCR *jcr)
588 Dmsg0(dbglvl, "plugin list is NULL\n");
592 int num = plugin_list->size();
595 Dmsg0(dbglvl, "No plugins loaded\n");
599 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
601 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
602 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
603 foreach_alist(plugin, plugin_list) {
604 /* Start a new instance of each plugin */
605 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
606 memset(b_ctx, 0, sizeof(bacula_ctx));
608 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
609 plugin_ctx_list[i].pContext = NULL;
610 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
611 b_ctx->disabled = true;
617 * Free the plugin instances for this Job
619 void free_plugins(JCR *jcr)
624 if (!plugin_list || !jcr->plugin_ctx_list) {
625 return; /* no plugins, nothing to do */
628 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
629 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
630 foreach_alist(plugin, plugin_list) {
631 /* Free the plugin instance */
632 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
633 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
635 free(plugin_ctx_list);
636 jcr->plugin_ctx_list = NULL;
639 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
642 Plugin *plugin = (Plugin *)jcr->plugin;
645 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
646 io.pkt_size = sizeof(io);
647 io.pkt_end = sizeof(io);
656 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
657 bfd->berrno = io.io_errno;
659 errno = b_errno_win32;
662 bfd->lerror = io.lerror;
664 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
668 static int my_plugin_bclose(BFILE *bfd)
671 Plugin *plugin = (Plugin *)jcr->plugin;
673 Dmsg0(dbglvl, "===== plugin_bclose\n");
674 io.pkt_size = sizeof(io);
675 io.pkt_end = sizeof(io);
681 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
682 bfd->berrno = io.io_errno;
684 errno = b_errno_win32;
687 bfd->lerror = io.lerror;
689 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
693 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
696 Plugin *plugin = (Plugin *)jcr->plugin;
698 Dmsg0(dbglvl, "plugin_bread\n");
699 io.pkt_size = sizeof(io);
700 io.pkt_end = sizeof(io);
703 io.buf = (char *)buf;
706 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
707 bfd->berrno = io.io_errno;
709 errno = b_errno_win32;
712 bfd->lerror = io.lerror;
714 return (ssize_t)io.status;
717 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
720 Plugin *plugin = (Plugin *)jcr->plugin;
722 Dmsg0(dbglvl, "plugin_bwrite\n");
723 io.pkt_size = sizeof(io);
724 io.pkt_end = sizeof(io);
727 io.buf = (char *)buf;
730 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
731 bfd->berrno = io.io_errno;
733 errno = b_errno_win32;
736 bfd->lerror = io.lerror;
738 return (ssize_t)io.status;
741 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
744 Plugin *plugin = (Plugin *)jcr->plugin;
746 Dmsg0(dbglvl, "plugin_bseek\n");
747 io.pkt_size = sizeof(io);
748 io.pkt_end = sizeof(io);
754 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
755 bfd->berrno = io.io_errno;
757 errno = b_errno_win32;
760 bfd->lerror = io.lerror;
762 return (boffset_t)io.offset;
765 /* ==============================================================
767 * Callbacks from the plugin
769 * ==============================================================
771 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
774 if (!value || !ctx) {
777 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
778 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
779 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
783 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
786 *((int *)value) = jcr->JobId;
787 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
790 *((char **)value) = my_name;
791 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
794 *((int *)value) = jcr->get_JobLevel();
795 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->get_JobLevel());
798 *((int *)value) = jcr->get_JobType();
799 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->get_JobType());
802 *((char **)value) = jcr->client_name;
803 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
806 *((char **)value) = jcr->Job;
807 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
810 *((int *)value) = jcr->JobStatus;
811 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
814 *((int *)value) = (int)jcr->mtime;
815 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
818 *((int *)value) = (int)jcr->accurate;
819 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
822 break; /* a write only variable, ignore read request */
827 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
830 if (!value || !ctx) {
833 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
834 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
835 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
839 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
842 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
852 static bRC baculaRegisterEvents(bpContext *ctx, ...)
862 while ((event = va_arg(args, uint32_t))) {
863 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
869 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
870 int type, utime_t mtime, const char *fmt, ...)
877 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
882 va_start(arg_ptr, fmt);
883 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
885 Jmsg(jcr, type, mtime, "%s", buf);
889 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
890 int level, const char *fmt, ...)
895 va_start(arg_ptr, fmt);
896 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
898 d_msg(file, line, level, "%s", buf);
902 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
906 return sm_malloc(file, line, size);
912 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
915 sm_free(file, line, mem);
925 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
926 int (*plugin_bclose)(JCR *jcr) = NULL;
927 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
928 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
929 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
931 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
936 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
941 int main(int argc, char *argv[])
943 char plugin_dir[1000];
948 strcpy(my_name, "test-fd");
950 getcwd(plugin_dir, sizeof(plugin_dir)-1);
951 load_fd_plugins(plugin_dir);
959 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
960 generate_plugin_event(jcr1, bEventJobEnd);
961 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
963 generate_plugin_event(jcr2, bEventJobEnd);
968 Dmsg0(dbglvl, "bacula: OK ...\n");
974 #endif /* TEST_PROGRAM */