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) {
279 if (sp.type == 0 || sp.fname == NULL) {
280 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad 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 pm_strcpy(fname, sp.fname);
291 pm_strcpy(link, sp.link);
292 ff_pkt->fname = fname.c_str();
293 ff_pkt->link = link.c_str();
294 ff_pkt->type = sp.type;
295 ff_pkt->object = sp.object;
296 if (sp.type == FT_RESTORE_FIRST) {
297 ff_pkt->LinkFI = sp.index; /* restore object index */
299 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
300 Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
301 save_file(jcr, ff_pkt, true);
302 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
303 if (rc == bRC_More || rc == bRC_OK) {
304 accurate_mark_file_as_seen(jcr, fname.c_str());
306 if (rc == bRC_More) {
313 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
316 jcr->cmd_plugin = false;
318 jcr->plugin_ctx = NULL;
323 * Send plugin name start/end record to SD
325 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
328 int index = jcr->JobFiles;
329 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
332 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
335 if (jcr->is_job_canceled()) {
340 index++; /* JobFiles not incremented yet */
342 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
343 /* Send stream header */
344 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
345 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
349 Dmsg1(50, "send: %s\n", sd->msg);
352 /* Send data -- not much */
353 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
355 /* Send end of data */
356 stat = sd->fsend("0 0");
359 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
363 Dmsg1(dbglvl, "send: %s\n", sd->msg);
364 sd->signal(BNET_EOD); /* indicate end of plugin name data */
369 * Plugin name stream found during restore. The record passed in
370 * argument name was generated in send_plugin_name() above.
372 * Returns: true if start of stream
373 * false if end of steam
375 bool plugin_name_stream(JCR *jcr, char *name)
379 bool start, portable;
383 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
385 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
386 skip_nonspaces(&p); /* skip over jcr->JobFiles */
390 /* Start of plugin data */
391 skip_nonspaces(&p); /* skip start/end flag */
393 portable = *p == '1';
394 skip_nonspaces(&p); /* skip portable flag */
399 * End of plugin data, notify plugin, then clear flags
401 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
403 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
405 jcr->plugin_ctx = NULL;
409 if (!plugin_ctx_list) {
414 * After this point, we are dealing with a restore start
417 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
418 if (!(p = strchr(cmd, ':'))) {
419 Jmsg1(jcr, M_ERROR, 0,
420 _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
429 * Search for correct plugin as specified on the command
431 foreach_alist(plugin, plugin_list) {
433 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
434 if (strncmp(plugin->file, cmd, len) != 0) {
438 jcr->plugin_ctx = &plugin_ctx_list[i];
439 jcr->plugin = plugin;
440 if (is_plugin_disabled(jcr)) {
443 Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
444 event.eventType = bEventRestoreCommand;
445 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
446 &event, cmd) != bRC_OK) {
449 /* ***FIXME**** check error code */
450 plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
453 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
460 * Tell the plugin to create the file. Return values are
461 * This is called only during Restore
464 * CF_SKIP -- skip processing this file
465 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
466 * CF_CREATED -- created, but no content to extract (typically directories)
469 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
471 bpContext *plugin_ctx = jcr->plugin_ctx;
472 Plugin *plugin = jcr->plugin;
473 struct restore_pkt rp;
477 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
480 rp.pkt_size = sizeof(rp);
481 rp.pkt_end = sizeof(rp);
482 rp.stream = attr->stream;
483 rp.data_stream = attr->data_stream;
484 rp.type = attr->type;
485 rp.file_index = attr->file_index;
486 rp.LinkFI = attr->LinkFI;
488 rp.statp = attr->statp; /* structure assignment */
489 rp.attrEx = attr->attrEx;
490 rp.ofname = attr->ofname;
491 rp.olname = attr->olname;
492 rp.where = jcr->where;
493 rp.RegexWhere = jcr->RegexWhere;
494 rp.replace = jcr->replace;
495 rp.create_status = CF_ERROR;
496 Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
497 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
499 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
503 if (rp.create_status == CF_ERROR) {
504 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
508 /* Created link or directory? */
509 if (rp.create_status == CF_CREATED) {
510 return rp.create_status; /* yes, no need to bopen */
513 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
514 Dmsg0(dbglvl, "call bopen\n");
515 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
516 Dmsg1(50, "bopen status=%d\n", stat);
519 be.set_errno(bfd->berrno);
520 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
521 attr->ofname, be.bstrerror());
522 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
526 if (!is_bopen(bfd)) {
527 Dmsg0(000, "===== BFD is not open!!!!\n");
533 * Reset the file attributes after all file I/O is done -- this allows
534 * the previous access time/dates to be set properly, and it also allows
535 * us to properly set directory permissions.
536 * Not currently Implemented.
538 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
540 Dmsg0(dbglvl, "plugin_set_attributes\n");
544 pm_strcpy(attr->ofname, "*none*");
549 * Print to file the plugin info.
551 void dump_fd_plugin(Plugin *plugin, FILE *fp)
556 pInfo *info = (pInfo *)plugin->pinfo;
557 fprintf(fp, "\tversion=%d\n", info->version);
558 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
559 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
560 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
561 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
562 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
563 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
567 * This entry point is called internally by Bacula to ensure
568 * that the plugin IO calls come into this code.
570 void load_fd_plugins(const char *plugin_dir)
575 Dmsg0(dbglvl, "plugin dir is NULL\n");
579 plugin_list = New(alist(10, not_owned_by_alist));
580 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
581 is_plugin_compatible)) {
582 /* Either none found, or some error */
583 if (plugin_list->size() == 0) {
586 Dmsg0(dbglvl, "No plugins loaded\n");
591 /* Plug entry points called from findlib */
592 plugin_bopen = my_plugin_bopen;
593 plugin_bclose = my_plugin_bclose;
594 plugin_bread = my_plugin_bread;
595 plugin_bwrite = my_plugin_bwrite;
596 plugin_blseek = my_plugin_blseek;
599 * Verify that the plugin is acceptable, and print information
602 foreach_alist(plugin, plugin_list) {
603 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
604 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
607 dbg_plugin_add_hook(dump_fd_plugin);
611 * Check if a plugin is compatible. Called by the load_plugin function
612 * to allow us to verify the plugin.
614 static bool is_plugin_compatible(Plugin *plugin)
616 pInfo *info = (pInfo *)plugin->pinfo;
617 Dmsg0(50, "is_plugin_compatible called\n");
618 if (debug_level >= 50) {
619 dump_fd_plugin(plugin, stdin);
621 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
622 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
623 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
624 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
625 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
629 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
630 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
631 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
632 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
633 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
636 if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
637 strcmp(info->plugin_license, "GPLv2") != 0) {
638 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
639 plugin->file, info->plugin_license);
640 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
641 plugin->file, info->plugin_license);
650 * Create a new instance of each plugin for this Job
651 * Note, plugin_list can exist but jcr->plugin_ctx_list can
652 * be NULL if no plugins were loaded.
654 void new_plugins(JCR *jcr)
660 Dmsg0(dbglvl, "plugin list is NULL\n");
663 if (jcr->is_job_canceled()) {
667 int num = plugin_list->size();
670 Dmsg0(dbglvl, "No plugins loaded\n");
674 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
676 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
677 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
678 foreach_alist(plugin, plugin_list) {
679 /* Start a new instance of each plugin */
680 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
681 memset(b_ctx, 0, sizeof(bacula_ctx));
683 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
684 plugin_ctx_list[i].pContext = NULL;
685 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
686 b_ctx->disabled = true;
692 * Free the plugin instances for this Job
694 void free_plugins(JCR *jcr)
699 if (!plugin_list || !jcr->plugin_ctx_list) {
700 return; /* no plugins, nothing to do */
703 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
704 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
705 foreach_alist(plugin, plugin_list) {
706 /* Free the plugin instance */
707 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
708 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
710 free(plugin_ctx_list);
711 jcr->plugin_ctx_list = NULL;
714 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
717 Plugin *plugin = (Plugin *)jcr->plugin;
720 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
721 if (!plugin || !jcr->plugin_ctx) {
724 io.pkt_size = sizeof(io);
725 io.pkt_end = sizeof(io);
734 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
735 bfd->berrno = io.io_errno;
737 errno = b_errno_win32;
740 bfd->lerror = io.lerror;
742 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
746 static int my_plugin_bclose(BFILE *bfd)
749 Plugin *plugin = (Plugin *)jcr->plugin;
752 Dmsg0(dbglvl, "===== plugin_bclose\n");
753 if (!plugin || !jcr->plugin_ctx) {
756 io.pkt_size = sizeof(io);
757 io.pkt_end = sizeof(io);
763 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
764 bfd->berrno = io.io_errno;
766 errno = b_errno_win32;
769 bfd->lerror = io.lerror;
771 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
775 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
778 Plugin *plugin = (Plugin *)jcr->plugin;
781 Dmsg0(dbglvl, "plugin_bread\n");
782 if (!plugin || !jcr->plugin_ctx) {
785 io.pkt_size = sizeof(io);
786 io.pkt_end = sizeof(io);
789 io.buf = (char *)buf;
792 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
793 bfd->berrno = io.io_errno;
795 errno = b_errno_win32;
798 bfd->lerror = io.lerror;
800 return (ssize_t)io.status;
803 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
806 Plugin *plugin = (Plugin *)jcr->plugin;
809 Dmsg0(dbglvl, "plugin_bwrite\n");
810 if (!plugin || !jcr->plugin_ctx) {
813 io.pkt_size = sizeof(io);
814 io.pkt_end = sizeof(io);
817 io.buf = (char *)buf;
820 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
821 bfd->berrno = io.io_errno;
823 errno = b_errno_win32;
826 bfd->lerror = io.lerror;
828 return (ssize_t)io.status;
831 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
834 Plugin *plugin = (Plugin *)jcr->plugin;
837 Dmsg0(dbglvl, "plugin_bseek\n");
838 if (!plugin || !jcr->plugin_ctx) {
841 io.pkt_size = sizeof(io);
842 io.pkt_end = sizeof(io);
848 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
849 bfd->berrno = io.io_errno;
851 errno = b_errno_win32;
854 bfd->lerror = io.lerror;
856 return (boffset_t)io.offset;
859 /* ==============================================================
861 * Callbacks from the plugin
863 * ==============================================================
865 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
868 if (!value || !ctx) {
871 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
872 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
876 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
879 *((int *)value) = jcr->JobId;
880 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
883 *((char **)value) = my_name;
884 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
887 *((int *)value) = jcr->getJobLevel();
888 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
891 *((int *)value) = jcr->getJobType();
892 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
895 *((char **)value) = jcr->client_name;
896 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
899 *((char **)value) = jcr->Job;
900 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
903 *((int *)value) = jcr->JobStatus;
904 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
907 *((int *)value) = (int)jcr->mtime;
908 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
911 *((int *)value) = (int)jcr->accurate;
912 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
915 break; /* a write only variable, ignore read request */
919 *(void **)value = g_pVSSClient->GetVssObject();
924 case bVarVssDllHandle:
927 *(void **)value = g_pVSSClient->GetVssDllHandle();
936 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
939 if (!value || !ctx) {
942 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
943 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
947 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
950 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
960 static bRC baculaRegisterEvents(bpContext *ctx, ...)
970 while ((event = va_arg(args, uint32_t))) {
971 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
977 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
978 int type, utime_t mtime, const char *fmt, ...)
985 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
990 va_start(arg_ptr, fmt);
991 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
993 Jmsg(jcr, type, mtime, "%s", buf);
997 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
998 int level, const char *fmt, ...)
1003 va_start(arg_ptr, fmt);
1004 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1006 d_msg(file, line, level, "%s", buf);
1010 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1014 return sm_malloc(file, line, size);
1016 return malloc(size);
1020 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1023 sm_free(file, line, mem);
1030 * Let the plugin define files/directories to be excluded
1031 * from the main backup.
1033 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1043 bctx = (bacula_ctx *)ctx->bContext;
1051 if (!bctx->fileset) {
1052 bctx->fileset = new_exclude(jcr);
1054 add_file_to_fileset(jcr, file, bctx->fileset, true);
1061 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1062 int (*plugin_bclose)(JCR *jcr) = NULL;
1063 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1064 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1065 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1067 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1072 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1077 int main(int argc, char *argv[])
1079 char plugin_dir[1000];
1084 strcpy(my_name, "test-fd");
1086 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1087 load_fd_plugins(plugin_dir);
1095 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1096 generate_plugin_event(jcr1, bEventJobEnd);
1097 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1099 generate_plugin_event(jcr2, bEventJobEnd);
1104 Dmsg0(dbglvl, "bacula: OK ...\n");
1105 close_memory_pool();
1110 #endif /* TEST_PROGRAM */