2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2011 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 three of the GNU Affero 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 Affero 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
38 extern DLL_IMP_EXP char *exepath;
39 extern DLL_IMP_EXP char *version;
40 extern DLL_IMP_EXP char *dist_name;
41 extern DLL_IMP_EXP int beef;
43 const int dbglvl = 150;
45 const char *plugin_type = "-fd.dll";
47 const char *plugin_type = "-fd.so";
50 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
51 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
53 /* Function pointers to be set here */
54 extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
55 extern DLL_IMP_EXP int (*plugin_bclose)(BFILE *bfd);
56 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
57 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
58 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
61 /* Forward referenced functions */
62 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
63 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
64 static bRC baculaRegisterEvents(bpContext *ctx, ...);
65 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
66 int type, utime_t mtime, const char *fmt, ...);
67 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
68 int level, const char *fmt, ...);
69 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
71 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
72 static bRC baculaAddExclude(bpContext *ctx, const char *file);
73 static bRC baculaAddInclude(bpContext *ctx, const char *file);
74 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
75 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
76 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
77 static bRC baculaNewOptions(bpContext *ctx);
78 static bRC baculaNewInclude(bpContext *ctx);
79 static bRC baculaNewPreInclude(bpContext *ctx);
80 static bool is_plugin_compatible(Plugin *plugin);
81 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
82 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
85 * These will be plugged into the global pointer structure for
88 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
89 static int my_plugin_bclose(BFILE *bfd);
90 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
91 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
92 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
94 #define for_this_plug(plugin, str, len) (((len) == (plugin)->file_len) && strncmp((plugin)->file, str, len) == 0)
97 static bInfo binfo = {
99 FD_PLUGIN_INTERFACE_VERSION
102 /* Bacula entry points */
103 static bFuncs bfuncs = {
105 FD_PLUGIN_INTERFACE_VERSION,
106 baculaRegisterEvents,
125 * Bacula private context
128 JCR *jcr; /* jcr for plugin */
129 bRC rc; /* last return code */
130 bool disabled; /* set if plugin disabled */
131 findINCEXE *exclude; /* pointer to exclude files */
132 findINCEXE *include; /* pointer to include/exclude files */
135 static bool is_plugin_disabled(bpContext *plugin_ctx)
141 b_ctx = (bacula_ctx *)plugin_ctx->bContext;
145 return b_ctx->disabled;
148 static bool is_plugin_disabled(JCR *jcr)
150 return is_plugin_disabled(jcr->plugin_ctx);
154 * Create a plugin event When receiving bEventCancelCommand, this function is
155 * called by an other thread.
157 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
159 bpContext *plugin_ctx;
165 bool call_if_canceled = false;
166 restore_object_pkt *rop;
168 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
169 return; /* Return if no plugins loaded */
173 * Some events are sent to only a particular plugin or must be
174 * called even if the job is canceled
177 case bEventPluginCommand:
178 case bEventOptionPlugin:
179 name = (char *)value;
180 if (!get_plugin_name(jcr, name, &len)) {
184 case bEventRestoreObject:
185 /* After all RestoreObject, we have it one more time with value=NULL */
187 /* Some RestoreObjects may not have a plugin name */
188 rop = (restore_object_pkt *)value;
189 if (*rop->plugin_name) {
190 name = rop->plugin_name;
191 get_plugin_name(jcr, name, &len);
196 case bEventEndBackupJob:
197 case bEventEndVerifyJob:
198 call_if_canceled = true;
200 case bEventStartRestoreJob:
201 foreach_alist(plugin, bplugin_list) {
202 plugin->restoreFileStarted = false;
203 plugin->createFileCalled = false;
206 case bEventEndRestoreJob:
207 call_if_canceled = true;
208 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
209 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
212 jcr->plugin->restoreFileStarted = false;
213 jcr->plugin->createFileCalled = false;
220 if (!call_if_canceled && jcr->is_job_canceled()) {
224 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
225 event.eventType = eventType;
227 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
230 * Pass event to every plugin (except if name is set). If name
231 * is set, we pass it only to the plugin with that name.
233 foreach_alist(plugin, bplugin_list) {
234 if (name && !for_this_plug(plugin, name, len)) {
238 plugin_ctx = &plugin_ctx_list[i++];
239 if (is_plugin_disabled(plugin_ctx)) {
242 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
248 * Check if file was seen for accurate
250 bool plugin_check_file(JCR *jcr, char *fname)
256 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
257 return false; /* Return if no plugins loaded */
260 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
262 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
264 /* Pass event to every plugin */
265 foreach_alist(plugin, bplugin_list) {
266 jcr->plugin_ctx = &plugin_ctx_list[i++];
267 jcr->plugin = plugin;
268 if (is_plugin_disabled(jcr)) {
271 if (plug_func(plugin)->checkFile == NULL) {
274 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
275 if (rc == bRC_Seen) {
281 jcr->plugin_ctx = NULL;
282 return rc == bRC_Seen;
285 /* Get the first part of the the plugin command
286 * systemstate:/@SYSTEMSTATE/
288 * => can use for_this_plug(plug, cmd, ret);
290 * The plugin command can contain only the plugin name
294 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
298 if (!cmd || (*cmd == '\0')) {
301 /* Handle plugin command here backup */
302 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
303 if ((p = strchr(cmd, ':')) == NULL) {
304 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
307 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
310 } else { /* plugin:argument */
321 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
323 ff_pkt->delta_seq = sp->delta_seq;
324 if (sp->flags & FO_DELTA) {
325 ff_pkt->flags |= FO_DELTA;
326 ff_pkt->delta_seq++; /* make new delta sequence number */
328 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
329 ff_pkt->delta_seq = 0;
332 if (sp->flags & FO_OFFSETS) {
333 ff_pkt->flags |= FO_OFFSETS;
335 if (sp->flags & FO_PORTABLE_DATA) {
336 ff_pkt->flags |= FO_PORTABLE_DATA;
338 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
341 /* Ask to a Option Plugin what to do with the current file */
342 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
346 char *cmd = ff_pkt->plugin;
350 event.eventType = bEventHandleBackupFile;
352 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
353 memset(sp, 0, sizeof(struct save_pkt));
354 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
357 sp->link = ff_pkt->link;
358 sp->cmd = ff_pkt->plugin;
359 sp->statp = ff_pkt->statp;
360 sp->fname = ff_pkt->fname;
361 sp->delta_seq = ff_pkt->delta_seq;
362 sp->accurate_found = ff_pkt->accurate_found;
364 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
365 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
366 goto bail_out; /* Return if no plugins loaded */
369 if (!get_plugin_name(jcr, cmd, &len)) {
373 /* Note, we stop the loop on the first plugin that matches the name */
374 foreach_alist(plugin, bplugin_list) {
375 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
376 if (!for_this_plug(plugin, cmd, len)) {
381 if (is_plugin_disabled(&plugin_ctx_list[i])) {
385 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
388 /* TODO: would be better to set this in save_file() */
390 jcr->opt_plugin = true;
391 jcr->plugin = plugin;
392 jcr->plugin_sp = sp; /* Unset sp in save_file */
393 jcr->plugin_ctx = &plugin_ctx_list[i++];
395 update_ff_pkt(ff_pkt, sp);
399 } /* end foreach loop */
405 * Sequence of calls for a backup:
406 * 1. plugin_save() here is called with ff_pkt
407 * 2. we find the plugin requested on the command string
408 * 3. we generate a bEventBackupCommand event to the specified plugin
409 * and pass it the command string.
410 * 4. we make a startPluginBackup call to the plugin, which gives
411 * us the data we need in save_pkt
412 * 5. we call Bacula's save_file() subroutine to save the specified
413 * file. The plugin will be called at pluginIO() to supply the
416 * Sequence of calls for restore:
417 * See subroutine plugin_name_stream() below.
419 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
424 char *cmd = ff_pkt->top_fname;
427 POOL_MEM fname(PM_FNAME);
428 POOL_MEM link(PM_FNAME);
430 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
431 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
432 return 1; /* Return if no plugins loaded */
435 jcr->cmd_plugin = true;
436 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
437 event.eventType = bEventBackupCommand;
439 if (!get_plugin_name(jcr, cmd, &len)) {
443 /* Note, we stop the loop on the first plugin that matches the name */
444 foreach_alist(plugin, bplugin_list) {
445 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
446 if (!for_this_plug(plugin, cmd, len)) {
451 * We put the current plugin pointer, and the plugin context
452 * into the jcr, because during save_file(), the plugin
453 * will be called many times and these values are needed.
455 jcr->plugin_ctx = &plugin_ctx_list[i];
456 jcr->plugin = plugin;
457 if (is_plugin_disabled(jcr)) {
461 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
462 /* Send the backup command to the right plugin*/
463 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
466 /* Loop getting filenames to backup then saving them */
467 while (!jcr->is_job_canceled()) {
468 memset(&sp, 0, sizeof(sp));
469 sp.pkt_size = sizeof(sp);
470 sp.pkt_end = sizeof(sp);
474 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
476 /* Get the file save parameters. I.e. the stat pkt ... */
477 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
481 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
485 jcr->plugin_sp = &sp;
488 * Copy fname and link because save_file() zaps them. This
489 * avoids zaping the plugin's strings.
491 ff_pkt->type = sp.type;
492 if (IS_FT_OBJECT(sp.type)) {
493 if (!sp.object_name) {
494 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
498 ff_pkt->fname = cmd; /* full plugin string */
499 ff_pkt->object_name = sp.object_name;
500 ff_pkt->object_index = sp.index; /* restore object index */
501 ff_pkt->object_compression = 0; /* no compression for now */
502 ff_pkt->object = sp.object;
503 ff_pkt->object_len = sp.object_len;
506 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
510 pm_strcpy(fname, sp.fname);
511 pm_strcpy(link, sp.link);
514 ff_pkt->fname = fname.c_str();
515 ff_pkt->link = link.c_str();
516 update_ff_pkt(ff_pkt, &sp);
519 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
520 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
522 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
524 /* Call Bacula core code to backup the plugin's file */
525 save_file(jcr, ff_pkt, true);
526 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
527 if (rc == bRC_More || rc == bRC_OK) {
528 accurate_mark_file_as_seen(jcr, fname.c_str());
530 if (rc == bRC_More) {
534 } /* end while loop */
536 } /* end loop over all plugins */
537 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
540 jcr->cmd_plugin = false;
542 jcr->plugin_ctx = NULL;
548 * Sequence of calls for a estimate:
549 * 1. plugin_estimate() here is called with ff_pkt
550 * 2. we find the plugin requested on the command string
551 * 3. we generate a bEventEstimateCommand event to the specified plugin
552 * and pass it the command string.
553 * 4. we make a startPluginBackup call to the plugin, which gives
554 * us the data we need in save_pkt
557 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
562 char *cmd = ff_pkt->top_fname;
565 POOL_MEM fname(PM_FNAME);
566 POOL_MEM link(PM_FNAME);
569 if (!bplugin_list || !jcr->plugin_ctx_list) {
570 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
571 return 1; /* Return if no plugins loaded */
574 jcr->cmd_plugin = true;
575 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
576 event.eventType = bEventEstimateCommand;
578 if (!get_plugin_name(jcr, cmd, &len)) {
582 /* Note, we stop the loop on the first plugin that matches the name */
583 foreach_alist(plugin, bplugin_list) {
584 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
585 if (!for_this_plug(plugin, cmd, len)) {
590 * We put the current plugin pointer, and the plugin context
591 * into the jcr, because during save_file(), the plugin
592 * will be called many times and these values are needed.
594 jcr->plugin_ctx = &plugin_ctx_list[i];
595 jcr->plugin = plugin;
596 if (is_plugin_disabled(jcr)) {
600 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
601 /* Send the backup command to the right plugin*/
602 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
605 /* Loop getting filenames to backup then saving them */
606 while (!jcr->is_job_canceled()) {
607 memset(&sp, 0, sizeof(sp));
608 sp.pkt_size = sizeof(sp);
609 sp.pkt_end = sizeof(sp);
613 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
615 /* Get the file save parameters. I.e. the stat pkt ... */
616 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
620 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
625 if (!IS_FT_OBJECT(sp.type)) {
627 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
632 jcr->num_files_examined++;
633 jcr->JobFiles++; /* increment number of files seen */
635 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
636 if (sp.statp.st_size > 0) {
637 jcr->JobBytes += sp.statp.st_size;
642 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
644 attr.ofname = (POOLMEM *)sp.fname;
645 attr.olname = (POOLMEM *)sp.link;
646 print_ls_output(jcr, &attr);
650 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
652 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
654 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
655 if (rc == bRC_More || rc == bRC_OK) {
656 accurate_mark_file_as_seen(jcr, sp.fname);
658 if (rc == bRC_More) {
662 } /* end while loop */
664 } /* end loop over all plugins */
665 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
668 jcr->cmd_plugin = false;
670 jcr->plugin_ctx = NULL;
675 * Send plugin name start/end record to SD
677 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
680 int index = jcr->JobFiles;
681 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
684 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
687 if (jcr->is_job_canceled()) {
692 index++; /* JobFiles not incremented yet */
694 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
695 /* Send stream header */
696 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
697 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
701 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
704 /* Send data -- not much */
705 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
707 /* Send end of data */
708 stat = sd->fsend("%ld 0", jcr->JobFiles);
711 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
715 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
716 sd->signal(BNET_EOD); /* indicate end of plugin name data */
721 * Plugin name stream found during restore. The record passed in
722 * argument name was generated in send_plugin_name() above.
724 * Returns: true if start of stream
725 * false if end of steam
727 bool plugin_name_stream(JCR *jcr, char *name)
735 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
737 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
738 skip_nonspaces(&p); /* skip over jcr->JobFiles */
742 /* Start of plugin data */
743 skip_nonspaces(&p); /* skip start/end flag */
745 // portable = *p == '1';
746 skip_nonspaces(&p); /* skip portable flag */
751 * End of plugin data, notify plugin, then clear flags
753 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
754 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
755 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
758 jcr->plugin->restoreFileStarted = false;
759 jcr->plugin->createFileCalled = false;
761 jcr->plugin_ctx = NULL;
765 if (!plugin_ctx_list) {
770 * After this point, we are dealing with a restore start
772 if (!get_plugin_name(jcr, cmd, &len)) {
777 * Search for correct plugin as specified on the command
779 foreach_alist(plugin, bplugin_list) {
781 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
782 if (!for_this_plug(plugin, cmd, len)) {
786 jcr->plugin_ctx = &plugin_ctx_list[i];
787 jcr->plugin = plugin;
788 if (is_plugin_disabled(jcr)) {
791 Dmsg1(000, "Restore Command plugin = %s\n", cmd);
792 event.eventType = bEventRestoreCommand;
793 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
794 &event, cmd) != bRC_OK) {
797 if (plugin->restoreFileStarted) {
798 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
799 plugin->restoreFileStarted = false;
802 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
803 plugin->restoreFileStarted = true;
807 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
814 * Tell the plugin to create the file. Return values are
815 * This is called only during Restore
818 * CF_SKIP -- skip processing this file
819 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
820 * CF_CREATED -- created, but no content to extract (typically directories)
823 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
825 bpContext *plugin_ctx = jcr->plugin_ctx;
826 Plugin *plugin = jcr->plugin;
827 struct restore_pkt rp;
831 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
835 rp.pkt_size = sizeof(rp);
836 rp.pkt_end = sizeof(rp);
837 rp.delta_seq = attr->delta_seq;
838 rp.stream = attr->stream;
839 rp.data_stream = attr->data_stream;
840 rp.type = attr->type;
841 rp.file_index = attr->file_index;
842 rp.LinkFI = attr->LinkFI;
844 rp.statp = attr->statp; /* structure assignment */
845 rp.attrEx = attr->attrEx;
846 rp.ofname = attr->ofname;
847 rp.olname = attr->olname;
848 rp.where = jcr->where;
849 rp.RegexWhere = jcr->RegexWhere;
850 rp.replace = jcr->replace;
851 rp.create_status = CF_ERROR;
852 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
853 rp.stream, rp.type, rp.LinkFI, rp.ofname);
855 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
857 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
858 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
859 plugin->createFileCalled = false;
862 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
864 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
868 if (rp.create_status == CF_CORE) {
869 return CF_CORE; /* Let Bacula core handle the file creation */
871 if (rp.create_status == CF_SKIP) {
874 if (rp.create_status == CF_ERROR) {
875 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
879 /* Created link or directory? */
880 if (rp.create_status == CF_CREATED) {
881 return rp.create_status; /* yes, no need to bopen */
884 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
885 Dmsg0(dbglvl, "call bopen\n");
886 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
887 Dmsg1(50, "bopen status=%d\n", stat);
890 be.set_errno(bfd->berrno);
891 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
892 attr->ofname, be.bstrerror());
893 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
897 if (!is_bopen(bfd)) {
898 Dmsg0(000, "===== BFD is not open!!!!\n");
904 * Reset the file attributes after all file I/O is done -- this allows
905 * the previous access time/dates to be set properly, and it also allows
906 * us to properly set directory permissions.
907 * Not currently Implemented.
909 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
911 Dmsg0(dbglvl, "plugin_set_attributes\n");
915 pm_strcpy(attr->ofname, "*none*");
920 * Print to file the plugin info.
922 void dump_fd_plugin(Plugin *plugin, FILE *fp)
927 pInfo *info = (pInfo *)plugin->pinfo;
928 fprintf(fp, "\tversion=%d\n", info->version);
929 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
930 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
931 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
932 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
933 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
934 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
938 * This entry point is called internally by Bacula to ensure
939 * that the plugin IO calls come into this code.
941 void load_fd_plugins(const char *plugin_dir)
946 Dmsg0(dbglvl, "plugin dir is NULL\n");
950 bplugin_list = New(alist(10, not_owned_by_alist));
951 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
952 is_plugin_compatible)) {
953 /* Either none found, or some error */
954 if (bplugin_list->size() == 0) {
957 Dmsg0(dbglvl, "No plugins loaded\n");
962 /* Plug entry points called from findlib */
963 plugin_bopen = my_plugin_bopen;
964 plugin_bclose = my_plugin_bclose;
965 plugin_bread = my_plugin_bread;
966 plugin_bwrite = my_plugin_bwrite;
967 plugin_blseek = my_plugin_blseek;
970 * Verify that the plugin is acceptable, and print information
973 foreach_alist(plugin, bplugin_list) {
974 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
975 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
978 dbg_plugin_add_hook(dump_fd_plugin);
982 * Check if a plugin is compatible. Called by the load_plugin function
983 * to allow us to verify the plugin.
985 static bool is_plugin_compatible(Plugin *plugin)
987 pInfo *info = (pInfo *)plugin->pinfo;
988 Dmsg0(50, "is_plugin_compatible called\n");
989 if (debug_level >= 50) {
990 dump_fd_plugin(plugin, stdin);
992 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
993 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
994 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
995 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
996 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1000 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1001 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1002 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1003 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1004 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1007 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1008 strcmp(info->plugin_license, "AGPLv3") != 0) {
1009 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1010 plugin->file, info->plugin_license);
1011 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1012 plugin->file, info->plugin_license);
1015 if (info->size != sizeof(pInfo)) {
1016 Jmsg(NULL, M_ERROR, 0,
1017 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1018 plugin->file, sizeof(pInfo), info->size);
1027 * Create a new instance of each plugin for this Job
1028 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1029 * be NULL if no plugins were loaded.
1031 void new_plugins(JCR *jcr)
1036 if (!bplugin_list) {
1037 Dmsg0(dbglvl, "plugin list is NULL\n");
1040 if (jcr->is_job_canceled()) {
1044 int num = bplugin_list->size();
1047 Dmsg0(dbglvl, "No plugins loaded\n");
1051 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1053 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1054 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1055 foreach_alist(plugin, bplugin_list) {
1056 /* Start a new instance of each plugin */
1057 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1058 memset(b_ctx, 0, sizeof(bacula_ctx));
1060 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1061 plugin_ctx_list[i].pContext = NULL;
1062 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
1063 b_ctx->disabled = true;
1069 * Free the plugin instances for this Job
1071 void free_plugins(JCR *jcr)
1076 if (!bplugin_list || !jcr->plugin_ctx_list) {
1077 return; /* no plugins, nothing to do */
1080 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1081 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1082 foreach_alist(plugin, bplugin_list) {
1083 /* Free the plugin instance */
1084 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1085 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
1087 free(plugin_ctx_list);
1088 jcr->plugin_ctx_list = NULL;
1091 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1093 JCR *jcr = bfd->jcr;
1094 Plugin *plugin = (Plugin *)jcr->plugin;
1097 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1098 if (!plugin || !jcr->plugin_ctx) {
1101 io.pkt_size = sizeof(io);
1102 io.pkt_end = sizeof(io);
1111 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1112 bfd->berrno = io.io_errno;
1114 errno = b_errno_win32;
1116 errno = io.io_errno;
1117 bfd->lerror = io.lerror;
1119 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1123 static int my_plugin_bclose(BFILE *bfd)
1125 JCR *jcr = bfd->jcr;
1126 Plugin *plugin = (Plugin *)jcr->plugin;
1129 Dmsg0(dbglvl, "===== plugin_bclose\n");
1130 if (!plugin || !jcr->plugin_ctx) {
1133 io.pkt_size = sizeof(io);
1134 io.pkt_end = sizeof(io);
1140 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1141 bfd->berrno = io.io_errno;
1143 errno = b_errno_win32;
1145 errno = io.io_errno;
1146 bfd->lerror = io.lerror;
1148 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1152 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1154 JCR *jcr = bfd->jcr;
1155 Plugin *plugin = (Plugin *)jcr->plugin;
1158 Dmsg0(dbglvl, "plugin_bread\n");
1159 if (!plugin || !jcr->plugin_ctx) {
1162 io.pkt_size = sizeof(io);
1163 io.pkt_end = sizeof(io);
1166 io.buf = (char *)buf;
1170 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1171 bfd->offset = io.offset;
1172 bfd->berrno = io.io_errno;
1174 errno = b_errno_win32;
1176 errno = io.io_errno;
1177 bfd->lerror = io.lerror;
1179 return (ssize_t)io.status;
1182 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1184 JCR *jcr = bfd->jcr;
1185 Plugin *plugin = (Plugin *)jcr->plugin;
1188 Dmsg0(dbglvl, "plugin_bwrite\n");
1189 if (!plugin || !jcr->plugin_ctx) {
1192 io.pkt_size = sizeof(io);
1193 io.pkt_end = sizeof(io);
1196 io.buf = (char *)buf;
1199 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1200 bfd->berrno = io.io_errno;
1202 errno = b_errno_win32;
1204 errno = io.io_errno;
1205 bfd->lerror = io.lerror;
1207 return (ssize_t)io.status;
1210 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1212 JCR *jcr = bfd->jcr;
1213 Plugin *plugin = (Plugin *)jcr->plugin;
1216 Dmsg0(dbglvl, "plugin_bseek\n");
1217 if (!plugin || !jcr->plugin_ctx) {
1220 io.pkt_size = sizeof(io);
1221 io.pkt_end = sizeof(io);
1227 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1228 bfd->berrno = io.io_errno;
1230 errno = b_errno_win32;
1232 errno = io.io_errno;
1233 bfd->lerror = io.lerror;
1235 return (boffset_t)io.offset;
1238 /* ==============================================================
1240 * Callbacks from the plugin
1242 * ==============================================================
1244 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1251 switch (var) { /* General variables, no need of ctx */
1253 *((char **)value) = my_name;
1255 case bVarWorkingDir:
1256 *(void **)value = me->working_directory;
1259 *(char **)value = exepath;
1262 *(char **)value = version;
1265 *(char **)value = dist_name;
1268 *((int *)value) = beef;
1274 if (!ctx) { /* Other variables need context */
1278 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1285 *((int *)value) = jcr->JobId;
1286 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1289 *((int *)value) = jcr->getJobLevel();
1290 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1293 *((int *)value) = jcr->getJobType();
1294 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1297 *((char **)value) = jcr->client_name;
1298 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1301 *((char **)value) = jcr->Job;
1302 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1304 case bVarPrevJobName:
1305 *((char **)value) = jcr->PrevJob;
1306 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1309 *((int *)value) = jcr->JobStatus;
1310 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1313 *((int *)value) = (int)jcr->mtime;
1314 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1317 *((int *)value) = (int)jcr->accurate;
1318 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1321 break; /* a write only variable, ignore read request */
1325 *(void **)value = g_pVSSClient->GetVssObject();
1330 case bVarVssDllHandle:
1333 *(void **)value = g_pVSSClient->GetVssDllHandle();
1339 *(char **)value = jcr->where;
1341 case bVarRegexWhere:
1342 *(char **)value = jcr->RegexWhere;
1345 case bVarFDName: /* get warning with g++ if we missed one */
1346 case bVarWorkingDir:
1356 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1359 if (!value || !ctx) {
1362 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1363 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1367 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1370 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1380 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1389 va_start(args, ctx);
1390 while ((event = va_arg(args, uint32_t))) {
1391 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1397 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1398 int type, utime_t mtime, const char *fmt, ...)
1405 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1410 va_start(arg_ptr, fmt);
1411 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1413 Jmsg(jcr, type, mtime, "%s", buf);
1417 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1418 int level, const char *fmt, ...)
1423 va_start(arg_ptr, fmt);
1424 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1426 d_msg(file, line, level, "%s", buf);
1430 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1434 return sm_malloc(file, line, size);
1436 return malloc(size);
1440 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1443 sm_free(file, line, mem);
1449 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1454 bctx = (bacula_ctx *)ctx->bContext;
1466 * Let the plugin define files/directories to be excluded
1467 * from the main backup.
1469 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1473 if (!is_ctx_good(ctx, jcr, bctx)) {
1479 if (!bctx->exclude) {
1480 bctx->exclude = new_exclude(jcr);
1481 new_options(jcr, bctx->exclude);
1483 set_incexe(jcr, bctx->exclude);
1484 add_file_to_fileset(jcr, file, true);
1485 Dmsg1(100, "Add exclude file=%s\n", file);
1490 * Let the plugin define files/directories to be excluded
1491 * from the main backup.
1493 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1497 if (!is_ctx_good(ctx, jcr, bctx)) {
1503 /* Not right time to add include */
1504 if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1507 if (!bctx->include) {
1508 bctx->include = jcr->ff->fileset->incexe;
1510 set_incexe(jcr, bctx->include);
1511 add_file_to_fileset(jcr, file, true);
1512 Dmsg1(100, "Add include file=%s\n", file);
1516 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1520 if (!is_ctx_good(ctx, jcr, bctx)) {
1526 add_options_to_fileset(jcr, opts);
1527 Dmsg1(1000, "Add options=%s\n", opts);
1531 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1535 if (!is_ctx_good(ctx, jcr, bctx)) {
1541 add_regex_to_fileset(jcr, item, type);
1542 Dmsg1(100, "Add regex=%s\n", item);
1546 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1550 if (!is_ctx_good(ctx, jcr, bctx)) {
1556 add_wild_to_fileset(jcr, item, type);
1557 Dmsg1(100, "Add wild=%s\n", item);
1561 static bRC baculaNewOptions(bpContext *ctx)
1565 if (!is_ctx_good(ctx, jcr, bctx)) {
1568 (void)new_options(jcr, NULL);
1572 static bRC baculaNewInclude(bpContext *ctx)
1576 if (!is_ctx_good(ctx, jcr, bctx)) {
1579 (void)new_include(jcr);
1583 static bRC baculaNewPreInclude(bpContext *ctx)
1587 if (!is_ctx_good(ctx, jcr, bctx)) {
1591 bctx->include = new_preinclude(jcr);
1592 new_options(jcr, bctx->include);
1593 set_incexe(jcr, bctx->include);
1599 * Check if a file have to be backuped using Accurate code
1601 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1606 bRC ret = bRC_Error;
1608 if (!is_ctx_good(ctx, jcr, bctx)) {
1617 * Copy fname and link because save_file() zaps them. This
1618 * avoids zaping the plugin's strings.
1620 ff_pkt->type = sp->type;
1622 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1626 ff_pkt->fname = sp->fname;
1627 ff_pkt->link = sp->link;
1628 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1630 if (check_changes(jcr, ff_pkt)) {
1636 /* check_changes() can update delta sequence number, return it to the
1639 sp->delta_seq = ff_pkt->delta_seq;
1640 sp->accurate_found = ff_pkt->accurate_found;
1643 Dmsg1(100, "checkChanges=%i\n", ret);
1650 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1651 int (*plugin_bclose)(JCR *jcr) = NULL;
1652 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1653 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1654 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1656 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1661 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1666 int main(int argc, char *argv[])
1668 char plugin_dir[1000];
1673 strcpy(my_name, "test-fd");
1675 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1676 load_fd_plugins(plugin_dir);
1684 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1685 generate_plugin_event(jcr1, bEventJobEnd);
1686 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1688 generate_plugin_event(jcr2, bEventJobEnd);
1693 Dmsg0(dbglvl, "bacula: OK ...\n");
1694 close_memory_pool();
1695 sm_dump(false); /* unit test */
1699 #endif /* TEST_PROGRAM */