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 bRC baculaAddExclude(bpContext *ctx, const char *file);
66 static bool is_plugin_compatible(Plugin *plugin);
69 * These will be plugged into the global pointer structure for
72 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
73 static int my_plugin_bclose(BFILE *bfd);
74 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
75 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
76 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
80 static bInfo binfo = {
82 FD_PLUGIN_INTERFACE_VERSION
85 /* Bacula entry points */
86 static bFuncs bfuncs = {
88 FD_PLUGIN_INTERFACE_VERSION,
100 * Bacula private context
103 JCR *jcr; /* jcr for plugin */
104 bRC rc; /* last return code */
105 bool disabled; /* set if plugin disabled */
106 findFILESET *fileset; /* pointer to exclude files */
109 static bool is_plugin_disabled(JCR *jcr)
112 if (!jcr->plugin_ctx) {
115 b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
116 return b_ctx->disabled;
121 * Create a plugin event
123 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
129 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
130 return; /* Return if no plugins loaded */
133 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
134 event.eventType = eventType;
136 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
138 /* Pass event to every plugin */
139 foreach_alist(plugin, plugin_list) {
141 jcr->plugin_ctx = &plugin_ctx_list[i++];
142 jcr->plugin = plugin;
143 if (is_plugin_disabled(jcr)) {
146 rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
153 jcr->plugin_ctx = NULL;
158 * Check if file was seen for accurate
160 bool plugin_check_file(JCR *jcr, char *fname)
166 if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
167 return false; /* Return if no plugins loaded */
170 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
172 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
174 /* Pass event to every plugin */
175 foreach_alist(plugin, plugin_list) {
176 jcr->plugin_ctx = &plugin_ctx_list[i++];
177 jcr->plugin = plugin;
178 if (is_plugin_disabled(jcr)) {
181 if (plug_func(plugin)->checkFile == NULL) {
184 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
185 if (rc == bRC_Seen) {
191 jcr->plugin_ctx = NULL;
192 return rc == bRC_Seen;
197 * Sequence of calls for a backup:
198 * 1. plugin_save() here is called with ff_pkt
199 * 2. we find the plugin requested on the command string
200 * 3. we generate a bEventBackupCommand event to the specified plugin
201 * and pass it the command string.
202 * 4. we make a startPluginBackup call to the plugin, which gives
203 * us the data we need in save_pkt
204 * 5. we call Bacula's save_file() subroutine to save the specified
205 * file. The plugin will be called at pluginIO() to supply the
208 * Sequence of calls for restore:
209 * See subroutine plugin_name_stream() below.
211 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
217 char *cmd = ff_pkt->top_fname;
220 POOL_MEM fname(PM_FNAME);
221 POOL_MEM link(PM_FNAME);
223 if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
224 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
225 return 1; /* Return if no plugins loaded */
228 jcr->cmd_plugin = true;
229 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
230 event.eventType = bEventBackupCommand;
232 /* Handle plugin command here backup */
233 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
234 if (!(p = strchr(cmd, ':'))) {
235 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
243 /* Note, we stop the loop on the first plugin that matches the name */
244 foreach_alist(plugin, plugin_list) {
245 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
246 if (strncmp(plugin->file, cmd, len) != 0) {
251 * We put the current plugin pointer, and the plugin context
252 * into the jcr, because during save_file(), the plugin
253 * will be called many times and these values are needed.
255 jcr->plugin_ctx = &plugin_ctx_list[i];
256 jcr->plugin = plugin;
257 if (is_plugin_disabled(jcr)) {
261 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
262 /* Send the backup command to the right plugin*/
263 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
266 /* Loop getting filenames to backup then saving them */
267 while (!jcr->is_job_canceled()) {
268 memset(&sp, 0, sizeof(sp));
269 sp.pkt_size = sizeof(sp);
270 sp.pkt_end = sizeof(sp);
273 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
275 /* Get the file save parameters. I.e. the stat pkt ... */
276 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
280 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
284 jcr->plugin_sp = &sp;
287 * Copy fname and link because save_file() zaps them. This
288 * avoids zaping the plugin's strings.
290 ff_pkt->type = sp.type;
291 if (sp.type == FT_RESTORE_FIRST) {
292 if (!sp.object_name) {
293 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
297 ff_pkt->fname = cmd; /* full plugin string */
298 ff_pkt->object_name = sp.object_name;
299 ff_pkt->object_index = sp.index; /* restore object index */
300 ff_pkt->object_compression = 0; /* no compression for now */
301 ff_pkt->object = sp.object;
302 ff_pkt->object_len = sp.object_len;
305 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
309 pm_strcpy(fname, sp.fname);
310 pm_strcpy(link, sp.link);
311 ff_pkt->fname = fname.c_str();
312 ff_pkt->link = link.c_str();
315 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
316 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
318 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
320 /* Call Bacula core code to backup the plugin's file */
321 save_file(jcr, ff_pkt, true);
322 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
323 if (rc == bRC_More || rc == bRC_OK) {
324 accurate_mark_file_as_seen(jcr, fname.c_str());
326 if (rc == bRC_More) {
333 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
336 jcr->cmd_plugin = false;
338 jcr->plugin_ctx = NULL;
343 * Send plugin name start/end record to SD
345 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
348 int index = jcr->JobFiles;
349 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
352 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
355 if (jcr->is_job_canceled()) {
360 index++; /* JobFiles not incremented yet */
362 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
363 /* Send stream header */
364 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
365 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
369 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
372 /* Send data -- not much */
373 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
375 /* Send end of data */
376 stat = sd->fsend("0 0");
379 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
383 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
384 sd->signal(BNET_EOD); /* indicate end of plugin name data */
389 * Plugin name stream found during restore. The record passed in
390 * argument name was generated in send_plugin_name() above.
392 * Returns: true if start of stream
393 * false if end of steam
395 bool plugin_name_stream(JCR *jcr, char *name)
399 bool start, portable;
403 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
405 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
406 skip_nonspaces(&p); /* skip over jcr->JobFiles */
410 /* Start of plugin data */
411 skip_nonspaces(&p); /* skip start/end flag */
413 portable = *p == '1';
414 skip_nonspaces(&p); /* skip portable flag */
419 * End of plugin data, notify plugin, then clear flags
421 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
423 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
425 jcr->plugin_ctx = NULL;
429 if (!plugin_ctx_list) {
434 * After this point, we are dealing with a restore start
437 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
438 if (!(p = strchr(cmd, ':'))) {
439 Jmsg1(jcr, M_ERROR, 0,
440 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
449 * Search for correct plugin as specified on the command
451 foreach_alist(plugin, plugin_list) {
453 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
454 if (strncmp(plugin->file, cmd, len) != 0) {
458 jcr->plugin_ctx = &plugin_ctx_list[i];
459 jcr->plugin = plugin;
460 if (is_plugin_disabled(jcr)) {
463 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
464 event.eventType = bEventRestoreCommand;
465 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
466 &event, cmd) != bRC_OK) {
469 /* ***FIXME**** check error code */
470 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
473 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
480 * Tell the plugin to create the file. Return values are
481 * This is called only during Restore
484 * CF_SKIP -- skip processing this file
485 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
486 * CF_CREATED -- created, but no content to extract (typically directories)
489 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
491 bpContext *plugin_ctx = jcr->plugin_ctx;
492 Plugin *plugin = jcr->plugin;
493 struct restore_pkt rp;
497 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
501 rp.pkt_size = sizeof(rp);
502 rp.pkt_end = sizeof(rp);
503 rp.stream = attr->stream;
504 rp.data_stream = attr->data_stream;
505 rp.type = attr->type;
506 rp.file_index = attr->file_index;
507 rp.LinkFI = attr->LinkFI;
509 rp.statp = attr->statp; /* structure assignment */
510 rp.attrEx = attr->attrEx;
511 rp.ofname = attr->ofname;
512 rp.olname = attr->olname;
513 rp.where = jcr->where;
514 rp.RegexWhere = jcr->RegexWhere;
515 rp.replace = jcr->replace;
516 rp.create_status = CF_ERROR;
517 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
518 rp.stream, rp.type, rp.LinkFI, rp.ofname);
520 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
522 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
524 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
528 if (rp.create_status == CF_ERROR) {
529 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
533 /* Created link or directory? */
534 if (rp.create_status == CF_CREATED) {
535 return rp.create_status; /* yes, no need to bopen */
538 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
539 Dmsg0(dbglvl, "call bopen\n");
540 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
541 Dmsg1(50, "bopen status=%d\n", stat);
544 be.set_errno(bfd->berrno);
545 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
546 attr->ofname, be.bstrerror());
547 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
551 if (!is_bopen(bfd)) {
552 Dmsg0(000, "===== BFD is not open!!!!\n");
558 * Reset the file attributes after all file I/O is done -- this allows
559 * the previous access time/dates to be set properly, and it also allows
560 * us to properly set directory permissions.
561 * Not currently Implemented.
563 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
565 Dmsg0(dbglvl, "plugin_set_attributes\n");
569 pm_strcpy(attr->ofname, "*none*");
574 * Print to file the plugin info.
576 void dump_fd_plugin(Plugin *plugin, FILE *fp)
581 pInfo *info = (pInfo *)plugin->pinfo;
582 fprintf(fp, "\tversion=%d\n", info->version);
583 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
584 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
585 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
586 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
587 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
588 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
592 * This entry point is called internally by Bacula to ensure
593 * that the plugin IO calls come into this code.
595 void load_fd_plugins(const char *plugin_dir)
600 Dmsg0(dbglvl, "plugin dir is NULL\n");
604 plugin_list = New(alist(10, not_owned_by_alist));
605 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
606 is_plugin_compatible)) {
607 /* Either none found, or some error */
608 if (plugin_list->size() == 0) {
611 Dmsg0(dbglvl, "No plugins loaded\n");
616 /* Plug entry points called from findlib */
617 plugin_bopen = my_plugin_bopen;
618 plugin_bclose = my_plugin_bclose;
619 plugin_bread = my_plugin_bread;
620 plugin_bwrite = my_plugin_bwrite;
621 plugin_blseek = my_plugin_blseek;
624 * Verify that the plugin is acceptable, and print information
627 foreach_alist(plugin, plugin_list) {
628 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
629 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
632 dbg_plugin_add_hook(dump_fd_plugin);
636 * Check if a plugin is compatible. Called by the load_plugin function
637 * to allow us to verify the plugin.
639 static bool is_plugin_compatible(Plugin *plugin)
641 pInfo *info = (pInfo *)plugin->pinfo;
642 Dmsg0(50, "is_plugin_compatible called\n");
643 if (debug_level >= 50) {
644 dump_fd_plugin(plugin, stdin);
646 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
647 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
648 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
649 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
650 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
654 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
655 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
656 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
657 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
658 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
661 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
662 strcmp(info->plugin_license, "GPLv2") != 0) {
663 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
664 plugin->file, info->plugin_license);
665 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
666 plugin->file, info->plugin_license);
675 * Create a new instance of each plugin for this Job
676 * Note, plugin_list can exist but jcr->plugin_ctx_list can
677 * be NULL if no plugins were loaded.
679 void new_plugins(JCR *jcr)
685 Dmsg0(dbglvl, "plugin list is NULL\n");
688 if (jcr->is_job_canceled() || jcr->JobId == 0) {
692 int num = plugin_list->size();
695 Dmsg0(dbglvl, "No plugins loaded\n");
699 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
701 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
702 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
703 foreach_alist(plugin, plugin_list) {
704 /* Start a new instance of each plugin */
705 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
706 memset(b_ctx, 0, sizeof(bacula_ctx));
708 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
709 plugin_ctx_list[i].pContext = NULL;
710 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
711 b_ctx->disabled = true;
717 * Free the plugin instances for this Job
719 void free_plugins(JCR *jcr)
724 if (!plugin_list || !jcr->plugin_ctx_list) {
725 return; /* no plugins, nothing to do */
728 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
729 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
730 foreach_alist(plugin, plugin_list) {
731 /* Free the plugin instance */
732 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
733 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
735 free(plugin_ctx_list);
736 jcr->plugin_ctx_list = NULL;
739 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
742 Plugin *plugin = (Plugin *)jcr->plugin;
745 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
746 if (!plugin || !jcr->plugin_ctx) {
749 io.pkt_size = sizeof(io);
750 io.pkt_end = sizeof(io);
759 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
760 bfd->berrno = io.io_errno;
762 errno = b_errno_win32;
765 bfd->lerror = io.lerror;
767 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
771 static int my_plugin_bclose(BFILE *bfd)
774 Plugin *plugin = (Plugin *)jcr->plugin;
777 Dmsg0(dbglvl, "===== plugin_bclose\n");
778 if (!plugin || !jcr->plugin_ctx) {
781 io.pkt_size = sizeof(io);
782 io.pkt_end = sizeof(io);
788 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
789 bfd->berrno = io.io_errno;
791 errno = b_errno_win32;
794 bfd->lerror = io.lerror;
796 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
800 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
803 Plugin *plugin = (Plugin *)jcr->plugin;
806 Dmsg0(dbglvl, "plugin_bread\n");
807 if (!plugin || !jcr->plugin_ctx) {
810 io.pkt_size = sizeof(io);
811 io.pkt_end = sizeof(io);
814 io.buf = (char *)buf;
817 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
818 bfd->berrno = io.io_errno;
820 errno = b_errno_win32;
823 bfd->lerror = io.lerror;
825 return (ssize_t)io.status;
828 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
831 Plugin *plugin = (Plugin *)jcr->plugin;
834 Dmsg0(dbglvl, "plugin_bwrite\n");
835 if (!plugin || !jcr->plugin_ctx) {
838 io.pkt_size = sizeof(io);
839 io.pkt_end = sizeof(io);
842 io.buf = (char *)buf;
845 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
846 bfd->berrno = io.io_errno;
848 errno = b_errno_win32;
851 bfd->lerror = io.lerror;
853 return (ssize_t)io.status;
856 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
859 Plugin *plugin = (Plugin *)jcr->plugin;
862 Dmsg0(dbglvl, "plugin_bseek\n");
863 if (!plugin || !jcr->plugin_ctx) {
866 io.pkt_size = sizeof(io);
867 io.pkt_end = sizeof(io);
873 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
874 bfd->berrno = io.io_errno;
876 errno = b_errno_win32;
879 bfd->lerror = io.lerror;
881 return (boffset_t)io.offset;
884 /* ==============================================================
886 * Callbacks from the plugin
888 * ==============================================================
890 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
893 if (!value || !ctx) {
896 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
897 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
901 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
904 *((int *)value) = jcr->JobId;
905 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
908 *((char **)value) = my_name;
909 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
912 *((int *)value) = jcr->getJobLevel();
913 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
916 *((int *)value) = jcr->getJobType();
917 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
920 *((char **)value) = jcr->client_name;
921 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
924 *((char **)value) = jcr->Job;
925 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
928 *((int *)value) = jcr->JobStatus;
929 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
932 *((int *)value) = (int)jcr->mtime;
933 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
936 *((int *)value) = (int)jcr->accurate;
937 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
940 break; /* a write only variable, ignore read request */
944 *(void **)value = g_pVSSClient->GetVssObject();
949 case bVarVssDllHandle:
952 *(void **)value = g_pVSSClient->GetVssDllHandle();
961 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
964 if (!value || !ctx) {
967 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
968 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
972 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
975 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
985 static bRC baculaRegisterEvents(bpContext *ctx, ...)
995 while ((event = va_arg(args, uint32_t))) {
996 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1002 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1003 int type, utime_t mtime, const char *fmt, ...)
1010 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1015 va_start(arg_ptr, fmt);
1016 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1018 Jmsg(jcr, type, mtime, "%s", buf);
1022 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1023 int level, const char *fmt, ...)
1028 va_start(arg_ptr, fmt);
1029 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1031 d_msg(file, line, level, "%s", buf);
1035 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1039 return sm_malloc(file, line, size);
1041 return malloc(size);
1045 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1048 sm_free(file, line, mem);
1055 * Let the plugin define files/directories to be excluded
1056 * from the main backup.
1058 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1068 bctx = (bacula_ctx *)ctx->bContext;
1076 if (!bctx->fileset) {
1077 bctx->fileset = new_exclude(jcr);
1079 add_file_to_fileset(jcr, file, bctx->fileset, true);
1086 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1087 int (*plugin_bclose)(JCR *jcr) = NULL;
1088 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1089 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1090 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1092 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1097 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1102 int main(int argc, char *argv[])
1104 char plugin_dir[1000];
1109 strcpy(my_name, "test-fd");
1111 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1112 load_fd_plugins(plugin_dir);
1120 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1121 generate_plugin_event(jcr1, bEventJobEnd);
1122 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1124 generate_plugin_event(jcr2, bEventJobEnd);
1129 Dmsg0(dbglvl, "bacula: OK ...\n");
1130 close_memory_pool();
1135 #endif /* TEST_PROGRAM */