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_index(i, 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_index(i, plugin, bplugin_list) {
234 if (name && !for_this_plug(plugin, name, len)) {
237 plugin_ctx = &plugin_ctx_list[i];
238 if (is_plugin_disabled(plugin_ctx)) {
241 plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
247 * Check if file was seen for accurate
249 bool plugin_check_file(JCR *jcr, char *fname)
255 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
256 return false; /* Return if no plugins loaded */
259 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
261 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
263 /* Pass event to every plugin */
264 foreach_alist_index(i, plugin, bplugin_list) {
265 jcr->plugin_ctx = &plugin_ctx_list[i];
266 jcr->plugin = plugin;
267 if (is_plugin_disabled(jcr)) {
270 if (plug_func(plugin)->checkFile == NULL) {
273 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
274 if (rc == bRC_Seen) {
280 jcr->plugin_ctx = NULL;
281 return rc == bRC_Seen;
284 /* Get the first part of the the plugin command
285 * systemstate:/@SYSTEMSTATE/
287 * => can use for_this_plug(plug, cmd, ret);
289 * The plugin command can contain only the plugin name
293 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
297 if (!cmd || (*cmd == '\0')) {
300 /* Handle plugin command here backup */
301 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
302 if ((p = strchr(cmd, ':')) == NULL) {
303 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
306 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
309 } else { /* plugin:argument */
320 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
322 ff_pkt->delta_seq = sp->delta_seq;
323 if (sp->flags & FO_DELTA) {
324 ff_pkt->flags |= FO_DELTA;
325 ff_pkt->delta_seq++; /* make new delta sequence number */
327 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
328 ff_pkt->delta_seq = 0;
331 if (sp->flags & FO_OFFSETS) {
332 ff_pkt->flags |= FO_OFFSETS;
334 if (sp->flags & FO_PORTABLE_DATA) {
335 ff_pkt->flags |= FO_PORTABLE_DATA;
337 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
340 /* Ask to a Option Plugin what to do with the current file */
341 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
345 char *cmd = ff_pkt->plugin;
349 event.eventType = bEventHandleBackupFile;
351 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
352 memset(sp, 0, sizeof(struct save_pkt));
353 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
356 sp->link = ff_pkt->link;
357 sp->cmd = ff_pkt->plugin;
358 sp->statp = ff_pkt->statp;
359 sp->fname = ff_pkt->fname;
360 sp->delta_seq = ff_pkt->delta_seq;
361 sp->accurate_found = ff_pkt->accurate_found;
363 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
364 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
365 goto bail_out; /* Return if no plugins loaded */
368 if (!get_plugin_name(jcr, cmd, &len)) {
372 /* Note, we stop the loop on the first plugin that matches the name */
373 foreach_alist_index(i, plugin, bplugin_list) {
374 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
375 if (!for_this_plug(plugin, cmd, len)) {
379 if (is_plugin_disabled(&plugin_ctx_list[i])) {
383 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
386 /* TODO: would be better to set this in save_file() */
388 jcr->opt_plugin = true;
389 jcr->plugin = plugin;
390 jcr->plugin_sp = sp; /* Unset sp in save_file */
391 jcr->plugin_ctx = &plugin_ctx_list[i];
393 update_ff_pkt(ff_pkt, sp);
397 } /* end foreach loop */
403 * Sequence of calls for a backup:
404 * 1. plugin_save() here is called with ff_pkt
405 * 2. we find the plugin requested on the command string
406 * 3. we generate a bEventBackupCommand event to the specified plugin
407 * and pass it the command string.
408 * 4. we make a startPluginBackup call to the plugin, which gives
409 * us the data we need in save_pkt
410 * 5. we call Bacula's save_file() subroutine to save the specified
411 * file. The plugin will be called at pluginIO() to supply the
414 * Sequence of calls for restore:
415 * See subroutine plugin_name_stream() below.
417 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
422 char *cmd = ff_pkt->top_fname;
425 POOL_MEM fname(PM_FNAME);
426 POOL_MEM link(PM_FNAME);
428 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
429 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
430 return 1; /* Return if no plugins loaded */
433 jcr->cmd_plugin = true;
434 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
435 event.eventType = bEventBackupCommand;
437 if (!get_plugin_name(jcr, cmd, &len)) {
441 /* Note, we stop the loop on the first plugin that matches the name */
442 foreach_alist_index(i, plugin, bplugin_list) {
443 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
444 if (!for_this_plug(plugin, cmd, len)) {
448 * We put the current plugin pointer, and the plugin context
449 * into the jcr, because during save_file(), the plugin
450 * will be called many times and these values are needed.
452 jcr->plugin_ctx = &plugin_ctx_list[i];
453 jcr->plugin = plugin;
454 if (is_plugin_disabled(jcr)) {
458 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
459 /* Send the backup command to the right plugin*/
460 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
463 /* Loop getting filenames to backup then saving them */
464 while (!jcr->is_job_canceled()) {
465 memset(&sp, 0, sizeof(sp));
466 sp.pkt_size = sizeof(sp);
467 sp.pkt_end = sizeof(sp);
471 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
473 /* Get the file save parameters. I.e. the stat pkt ... */
474 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
478 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
482 jcr->plugin_sp = &sp;
485 * Copy fname and link because save_file() zaps them. This
486 * avoids zaping the plugin's strings.
488 ff_pkt->type = sp.type;
489 if (IS_FT_OBJECT(sp.type)) {
490 if (!sp.object_name) {
491 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
495 ff_pkt->fname = cmd; /* full plugin string */
496 ff_pkt->object_name = sp.object_name;
497 ff_pkt->object_index = sp.index; /* restore object index */
498 ff_pkt->object_compression = 0; /* no compression for now */
499 ff_pkt->object = sp.object;
500 ff_pkt->object_len = sp.object_len;
503 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
507 pm_strcpy(fname, sp.fname);
508 pm_strcpy(link, sp.link);
511 ff_pkt->fname = fname.c_str();
512 ff_pkt->link = link.c_str();
513 update_ff_pkt(ff_pkt, &sp);
516 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
517 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
519 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
521 /* Call Bacula core code to backup the plugin's file */
522 save_file(jcr, ff_pkt, true);
523 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
524 if (rc == bRC_More || rc == bRC_OK) {
525 accurate_mark_file_as_seen(jcr, fname.c_str());
527 if (rc == bRC_More) {
531 } /* end while loop */
533 } /* end loop over all plugins */
534 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
537 jcr->cmd_plugin = false;
539 jcr->plugin_ctx = NULL;
545 * Sequence of calls for a estimate:
546 * 1. plugin_estimate() here is called with ff_pkt
547 * 2. we find the plugin requested on the command string
548 * 3. we generate a bEventEstimateCommand event to the specified plugin
549 * and pass it the command string.
550 * 4. we make a startPluginBackup call to the plugin, which gives
551 * us the data we need in save_pkt
554 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
559 char *cmd = ff_pkt->top_fname;
562 POOL_MEM fname(PM_FNAME);
563 POOL_MEM link(PM_FNAME);
566 if (!bplugin_list || !jcr->plugin_ctx_list) {
567 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
568 return 1; /* Return if no plugins loaded */
571 jcr->cmd_plugin = true;
572 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
573 event.eventType = bEventEstimateCommand;
575 if (!get_plugin_name(jcr, cmd, &len)) {
579 /* Note, we stop the loop on the first plugin that matches the name */
580 foreach_alist_index(i, plugin, bplugin_list) {
581 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
582 if (!for_this_plug(plugin, cmd, len)) {
586 * We put the current plugin pointer, and the plugin context
587 * into the jcr, because during save_file(), the plugin
588 * will be called many times and these values are needed.
590 jcr->plugin_ctx = &plugin_ctx_list[i];
591 jcr->plugin = plugin;
592 if (is_plugin_disabled(jcr)) {
596 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
597 /* Send the backup command to the right plugin*/
598 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
601 /* Loop getting filenames to backup then saving them */
602 while (!jcr->is_job_canceled()) {
603 memset(&sp, 0, sizeof(sp));
604 sp.pkt_size = sizeof(sp);
605 sp.pkt_end = sizeof(sp);
609 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
611 /* Get the file save parameters. I.e. the stat pkt ... */
612 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
616 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
621 if (!IS_FT_OBJECT(sp.type)) {
623 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
628 jcr->num_files_examined++;
629 jcr->JobFiles++; /* increment number of files seen */
631 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
632 if (sp.statp.st_size > 0) {
633 jcr->JobBytes += sp.statp.st_size;
638 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
640 attr.ofname = (POOLMEM *)sp.fname;
641 attr.olname = (POOLMEM *)sp.link;
642 print_ls_output(jcr, &attr);
646 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
648 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
650 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
651 if (rc == bRC_More || rc == bRC_OK) {
652 accurate_mark_file_as_seen(jcr, sp.fname);
654 if (rc == bRC_More) {
658 } /* end while loop */
660 } /* end loop over all plugins */
661 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
664 jcr->cmd_plugin = false;
666 jcr->plugin_ctx = NULL;
671 * Send plugin name start/end record to SD
673 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
676 int index = jcr->JobFiles;
677 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
680 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
683 if (jcr->is_job_canceled()) {
688 index++; /* JobFiles not incremented yet */
690 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
691 /* Send stream header */
692 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
693 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
697 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
700 /* Send data -- not much */
701 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
703 /* Send end of data */
704 stat = sd->fsend("%ld 0", jcr->JobFiles);
707 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
711 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
712 sd->signal(BNET_EOD); /* indicate end of plugin name data */
717 * Plugin name stream found during restore. The record passed in
718 * argument name was generated in send_plugin_name() above.
720 * Returns: true if start of stream
721 * false if end of steam
723 bool plugin_name_stream(JCR *jcr, char *name)
731 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
733 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
734 skip_nonspaces(&p); /* skip over jcr->JobFiles */
738 /* Start of plugin data */
739 skip_nonspaces(&p); /* skip start/end flag */
741 // portable = *p == '1';
742 skip_nonspaces(&p); /* skip portable flag */
747 * End of plugin data, notify plugin, then clear flags
749 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
750 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
751 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
754 jcr->plugin->restoreFileStarted = false;
755 jcr->plugin->createFileCalled = false;
757 jcr->plugin_ctx = NULL;
761 if (!plugin_ctx_list) {
766 * After this point, we are dealing with a restore start
768 if (!get_plugin_name(jcr, cmd, &len)) {
773 * Search for correct plugin as specified on the command
775 foreach_alist_index(i, plugin, bplugin_list) {
777 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
778 if (!for_this_plug(plugin, cmd, len)) {
781 jcr->plugin_ctx = &plugin_ctx_list[i];
782 jcr->plugin = plugin;
783 if (is_plugin_disabled(jcr)) {
786 Dmsg1(000, "Restore Command plugin = %s\n", cmd);
787 event.eventType = bEventRestoreCommand;
788 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
789 &event, cmd) != bRC_OK) {
792 if (plugin->restoreFileStarted) {
793 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
794 plugin->restoreFileStarted = false;
797 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
798 plugin->restoreFileStarted = true;
802 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
809 * Tell the plugin to create the file. Return values are
810 * This is called only during Restore
813 * CF_SKIP -- skip processing this file
814 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
815 * CF_CREATED -- created, but no content to extract (typically directories)
818 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
820 bpContext *plugin_ctx = jcr->plugin_ctx;
821 Plugin *plugin = jcr->plugin;
822 struct restore_pkt rp;
826 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
830 rp.pkt_size = sizeof(rp);
831 rp.pkt_end = sizeof(rp);
832 rp.delta_seq = attr->delta_seq;
833 rp.stream = attr->stream;
834 rp.data_stream = attr->data_stream;
835 rp.type = attr->type;
836 rp.file_index = attr->file_index;
837 rp.LinkFI = attr->LinkFI;
839 rp.statp = attr->statp; /* structure assignment */
840 rp.attrEx = attr->attrEx;
841 rp.ofname = attr->ofname;
842 rp.olname = attr->olname;
843 rp.where = jcr->where;
844 rp.RegexWhere = jcr->RegexWhere;
845 rp.replace = jcr->replace;
846 rp.create_status = CF_ERROR;
847 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
848 rp.stream, rp.type, rp.LinkFI, rp.ofname);
850 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
852 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
853 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
854 plugin->createFileCalled = false;
857 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
859 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
863 if (rp.create_status == CF_CORE) {
864 return CF_CORE; /* Let Bacula core handle the file creation */
866 if (rp.create_status == CF_SKIP) {
869 if (rp.create_status == CF_ERROR) {
870 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
874 /* Created link or directory? */
875 if (rp.create_status == CF_CREATED) {
876 return rp.create_status; /* yes, no need to bopen */
879 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
880 Dmsg0(dbglvl, "call bopen\n");
881 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
882 Dmsg1(50, "bopen status=%d\n", stat);
885 be.set_errno(bfd->berrno);
886 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
887 attr->ofname, be.bstrerror());
888 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
892 if (!is_bopen(bfd)) {
893 Dmsg0(000, "===== BFD is not open!!!!\n");
899 * Reset the file attributes after all file I/O is done -- this allows
900 * the previous access time/dates to be set properly, and it also allows
901 * us to properly set directory permissions.
902 * Not currently Implemented.
904 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
906 Dmsg0(dbglvl, "plugin_set_attributes\n");
910 pm_strcpy(attr->ofname, "*none*");
915 * Print to file the plugin info.
917 void dump_fd_plugin(Plugin *plugin, FILE *fp)
922 pInfo *info = (pInfo *)plugin->pinfo;
923 fprintf(fp, "\tversion=%d\n", info->version);
924 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
925 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
926 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
927 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
928 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
929 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
933 * This entry point is called internally by Bacula to ensure
934 * that the plugin IO calls come into this code.
936 void load_fd_plugins(const char *plugin_dir)
942 Dmsg0(dbglvl, "plugin dir is NULL\n");
946 bplugin_list = New(alist(10, not_owned_by_alist));
947 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
948 is_plugin_compatible)) {
949 /* Either none found, or some error */
950 if (bplugin_list->size() == 0) {
953 Dmsg0(dbglvl, "No plugins loaded\n");
958 /* Plug entry points called from findlib */
959 plugin_bopen = my_plugin_bopen;
960 plugin_bclose = my_plugin_bclose;
961 plugin_bread = my_plugin_bread;
962 plugin_bwrite = my_plugin_bwrite;
963 plugin_blseek = my_plugin_blseek;
966 * Verify that the plugin is acceptable, and print information
969 foreach_alist_index(i, plugin, bplugin_list) {
970 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
971 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
974 dbg_plugin_add_hook(dump_fd_plugin);
978 * Check if a plugin is compatible. Called by the load_plugin function
979 * to allow us to verify the plugin.
981 static bool is_plugin_compatible(Plugin *plugin)
983 pInfo *info = (pInfo *)plugin->pinfo;
984 Dmsg0(50, "is_plugin_compatible called\n");
985 if (debug_level >= 50) {
986 dump_fd_plugin(plugin, stdin);
988 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
989 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
990 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
991 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
992 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
996 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
997 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
998 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
999 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1000 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1003 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1004 strcmp(info->plugin_license, "AGPLv3") != 0) {
1005 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1006 plugin->file, info->plugin_license);
1007 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1008 plugin->file, info->plugin_license);
1011 if (info->size != sizeof(pInfo)) {
1012 Jmsg(NULL, M_ERROR, 0,
1013 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1014 plugin->file, sizeof(pInfo), info->size);
1023 * Create a new instance of each plugin for this Job
1024 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1025 * be NULL if no plugins were loaded.
1027 void new_plugins(JCR *jcr)
1032 if (!bplugin_list) {
1033 Dmsg0(dbglvl, "plugin list is NULL\n");
1036 if (jcr->is_job_canceled()) {
1040 int num = bplugin_list->size();
1043 Dmsg0(dbglvl, "No plugins loaded\n");
1047 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1049 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1050 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1051 foreach_alist_index(i, plugin, bplugin_list) {
1052 /* Start a new instance of each plugin */
1053 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1054 memset(b_ctx, 0, sizeof(bacula_ctx));
1056 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1057 plugin_ctx_list[i].pContext = NULL;
1058 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1059 b_ctx->disabled = true;
1065 * Free the plugin instances for this Job
1067 void free_plugins(JCR *jcr)
1072 if (!bplugin_list || !jcr->plugin_ctx_list) {
1073 return; /* no plugins, nothing to do */
1076 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1077 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1078 foreach_alist_index(i, plugin, bplugin_list) {
1079 /* Free the plugin instance */
1080 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1081 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1083 free(plugin_ctx_list);
1084 jcr->plugin_ctx_list = NULL;
1087 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1089 JCR *jcr = bfd->jcr;
1090 Plugin *plugin = (Plugin *)jcr->plugin;
1093 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1094 if (!plugin || !jcr->plugin_ctx) {
1097 io.pkt_size = sizeof(io);
1098 io.pkt_end = sizeof(io);
1107 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1108 bfd->berrno = io.io_errno;
1110 errno = b_errno_win32;
1112 errno = io.io_errno;
1113 bfd->lerror = io.lerror;
1115 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1119 static int my_plugin_bclose(BFILE *bfd)
1121 JCR *jcr = bfd->jcr;
1122 Plugin *plugin = (Plugin *)jcr->plugin;
1125 Dmsg0(dbglvl, "===== plugin_bclose\n");
1126 if (!plugin || !jcr->plugin_ctx) {
1129 io.pkt_size = sizeof(io);
1130 io.pkt_end = sizeof(io);
1136 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1137 bfd->berrno = io.io_errno;
1139 errno = b_errno_win32;
1141 errno = io.io_errno;
1142 bfd->lerror = io.lerror;
1144 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1148 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1150 JCR *jcr = bfd->jcr;
1151 Plugin *plugin = (Plugin *)jcr->plugin;
1154 Dmsg0(dbglvl, "plugin_bread\n");
1155 if (!plugin || !jcr->plugin_ctx) {
1158 io.pkt_size = sizeof(io);
1159 io.pkt_end = sizeof(io);
1162 io.buf = (char *)buf;
1166 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1167 bfd->offset = io.offset;
1168 bfd->berrno = io.io_errno;
1170 errno = b_errno_win32;
1172 errno = io.io_errno;
1173 bfd->lerror = io.lerror;
1175 return (ssize_t)io.status;
1178 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1180 JCR *jcr = bfd->jcr;
1181 Plugin *plugin = (Plugin *)jcr->plugin;
1184 Dmsg0(dbglvl, "plugin_bwrite\n");
1185 if (!plugin || !jcr->plugin_ctx) {
1188 io.pkt_size = sizeof(io);
1189 io.pkt_end = sizeof(io);
1192 io.buf = (char *)buf;
1195 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1196 bfd->berrno = io.io_errno;
1198 errno = b_errno_win32;
1200 errno = io.io_errno;
1201 bfd->lerror = io.lerror;
1203 return (ssize_t)io.status;
1206 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1208 JCR *jcr = bfd->jcr;
1209 Plugin *plugin = (Plugin *)jcr->plugin;
1212 Dmsg0(dbglvl, "plugin_bseek\n");
1213 if (!plugin || !jcr->plugin_ctx) {
1216 io.pkt_size = sizeof(io);
1217 io.pkt_end = sizeof(io);
1223 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1224 bfd->berrno = io.io_errno;
1226 errno = b_errno_win32;
1228 errno = io.io_errno;
1229 bfd->lerror = io.lerror;
1231 return (boffset_t)io.offset;
1234 /* ==============================================================
1236 * Callbacks from the plugin
1238 * ==============================================================
1240 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1247 switch (var) { /* General variables, no need of ctx */
1249 *((char **)value) = my_name;
1251 case bVarWorkingDir:
1252 *(void **)value = me->working_directory;
1255 *(char **)value = exepath;
1258 *(char **)value = version;
1261 *(char **)value = dist_name;
1264 *((int *)value) = beef;
1270 if (!ctx) { /* Other variables need context */
1274 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1281 *((int *)value) = jcr->JobId;
1282 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1285 *((int *)value) = jcr->getJobLevel();
1286 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1289 *((int *)value) = jcr->getJobType();
1290 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1293 *((char **)value) = jcr->client_name;
1294 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1297 *((char **)value) = jcr->Job;
1298 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1300 case bVarPrevJobName:
1301 *((char **)value) = jcr->PrevJob;
1302 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1305 *((int *)value) = jcr->JobStatus;
1306 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1309 *((int *)value) = (int)jcr->mtime;
1310 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1313 *((int *)value) = (int)jcr->accurate;
1314 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1317 break; /* a write only variable, ignore read request */
1321 *(void **)value = g_pVSSClient->GetVssObject();
1326 case bVarVssDllHandle:
1329 *(void **)value = g_pVSSClient->GetVssDllHandle();
1335 *(char **)value = jcr->where;
1337 case bVarRegexWhere:
1338 *(char **)value = jcr->RegexWhere;
1341 case bVarFDName: /* get warning with g++ if we missed one */
1342 case bVarWorkingDir:
1352 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1355 if (!value || !ctx) {
1358 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1359 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1363 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1366 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1376 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1385 va_start(args, ctx);
1386 while ((event = va_arg(args, uint32_t))) {
1387 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1393 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1394 int type, utime_t mtime, const char *fmt, ...)
1401 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1406 va_start(arg_ptr, fmt);
1407 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1409 Jmsg(jcr, type, mtime, "%s", buf);
1413 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1414 int level, const char *fmt, ...)
1419 va_start(arg_ptr, fmt);
1420 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1422 d_msg(file, line, level, "%s", buf);
1426 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1430 return sm_malloc(file, line, size);
1432 return malloc(size);
1436 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1439 sm_free(file, line, mem);
1445 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1450 bctx = (bacula_ctx *)ctx->bContext;
1462 * Let the plugin define files/directories to be excluded
1463 * from the main backup.
1465 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1469 if (!is_ctx_good(ctx, jcr, bctx)) {
1475 if (!bctx->exclude) {
1476 bctx->exclude = new_exclude(jcr);
1477 new_options(jcr, bctx->exclude);
1479 set_incexe(jcr, bctx->exclude);
1480 add_file_to_fileset(jcr, file, true);
1481 Dmsg1(100, "Add exclude file=%s\n", file);
1486 * Let the plugin define files/directories to be excluded
1487 * from the main backup.
1489 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1493 if (!is_ctx_good(ctx, jcr, bctx)) {
1499 /* Not right time to add include */
1500 if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1503 if (!bctx->include) {
1504 bctx->include = jcr->ff->fileset->incexe;
1506 set_incexe(jcr, bctx->include);
1507 add_file_to_fileset(jcr, file, true);
1508 Dmsg1(100, "Add include file=%s\n", file);
1512 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1516 if (!is_ctx_good(ctx, jcr, bctx)) {
1522 add_options_to_fileset(jcr, opts);
1523 Dmsg1(1000, "Add options=%s\n", opts);
1527 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1531 if (!is_ctx_good(ctx, jcr, bctx)) {
1537 add_regex_to_fileset(jcr, item, type);
1538 Dmsg1(100, "Add regex=%s\n", item);
1542 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1546 if (!is_ctx_good(ctx, jcr, bctx)) {
1552 add_wild_to_fileset(jcr, item, type);
1553 Dmsg1(100, "Add wild=%s\n", item);
1557 static bRC baculaNewOptions(bpContext *ctx)
1561 if (!is_ctx_good(ctx, jcr, bctx)) {
1564 (void)new_options(jcr, NULL);
1568 static bRC baculaNewInclude(bpContext *ctx)
1572 if (!is_ctx_good(ctx, jcr, bctx)) {
1575 (void)new_include(jcr);
1579 static bRC baculaNewPreInclude(bpContext *ctx)
1583 if (!is_ctx_good(ctx, jcr, bctx)) {
1587 bctx->include = new_preinclude(jcr);
1588 new_options(jcr, bctx->include);
1589 set_incexe(jcr, bctx->include);
1595 * Check if a file have to be backuped using Accurate code
1597 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1602 bRC ret = bRC_Error;
1604 if (!is_ctx_good(ctx, jcr, bctx)) {
1613 * Copy fname and link because save_file() zaps them. This
1614 * avoids zaping the plugin's strings.
1616 ff_pkt->type = sp->type;
1618 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1622 ff_pkt->fname = sp->fname;
1623 ff_pkt->link = sp->link;
1624 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1626 if (check_changes(jcr, ff_pkt)) {
1632 /* check_changes() can update delta sequence number, return it to the
1635 sp->delta_seq = ff_pkt->delta_seq;
1636 sp->accurate_found = ff_pkt->accurate_found;
1639 Dmsg1(100, "checkChanges=%i\n", ret);
1646 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1647 int (*plugin_bclose)(JCR *jcr) = NULL;
1648 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1649 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1650 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1652 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1657 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1662 int main(int argc, char *argv[])
1664 char plugin_dir[1000];
1669 strcpy(my_name, "test-fd");
1671 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1672 load_fd_plugins(plugin_dir);
1680 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1681 generate_plugin_event(jcr1, bEventJobEnd);
1682 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1684 generate_plugin_event(jcr2, bEventJobEnd);
1689 Dmsg0(dbglvl, "bacula: OK ...\n");
1690 close_memory_pool();
1691 sm_dump(false); /* unit test */
1695 #endif /* TEST_PROGRAM */