2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2012 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->no_read = sp->no_read;
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_index(i, 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)) {
380 if (is_plugin_disabled(&plugin_ctx_list[i])) {
384 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
387 /* TODO: would be better to set this in save_file() */
389 jcr->opt_plugin = true;
390 jcr->plugin = plugin;
391 jcr->plugin_sp = sp; /* Unset sp in save_file */
392 jcr->plugin_ctx = &plugin_ctx_list[i];
394 update_ff_pkt(ff_pkt, sp);
398 } /* end foreach loop */
404 * Sequence of calls for a backup:
405 * 1. plugin_save() here is called with ff_pkt
406 * 2. we find the plugin requested on the command string
407 * 3. we generate a bEventBackupCommand event to the specified plugin
408 * and pass it the command string.
409 * 4. we make a startPluginBackup call to the plugin, which gives
410 * us the data we need in save_pkt
411 * 5. we call Bacula's save_file() subroutine to save the specified
412 * file. The plugin will be called at pluginIO() to supply the
415 * Sequence of calls for restore:
416 * See subroutine plugin_name_stream() below.
418 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
423 char *cmd = ff_pkt->top_fname;
426 POOL_MEM fname(PM_FNAME);
427 POOL_MEM link(PM_FNAME);
429 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
430 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
431 return 1; /* Return if no plugins loaded */
434 jcr->cmd_plugin = true;
435 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
436 event.eventType = bEventBackupCommand;
438 if (!get_plugin_name(jcr, cmd, &len)) {
442 /* Note, we stop the loop on the first plugin that matches the name */
443 foreach_alist_index(i, plugin, bplugin_list) {
444 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
445 if (!for_this_plug(plugin, cmd, len)) {
449 * We put the current plugin pointer, and the plugin context
450 * into the jcr, because during save_file(), the plugin
451 * will be called many times and these values are needed.
453 jcr->plugin_ctx = &plugin_ctx_list[i];
454 jcr->plugin = plugin;
455 if (is_plugin_disabled(jcr)) {
459 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
460 /* Send the backup command to the right plugin*/
461 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
464 /* Loop getting filenames to backup then saving them */
465 while (!jcr->is_job_canceled()) {
466 memset(&sp, 0, sizeof(sp));
467 sp.pkt_size = sizeof(sp);
468 sp.pkt_end = sizeof(sp);
472 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
474 /* Get the file save parameters. I.e. the stat pkt ... */
475 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
479 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
483 jcr->plugin_sp = &sp;
486 * Copy fname and link because save_file() zaps them. This
487 * avoids zaping the plugin's strings.
489 ff_pkt->type = sp.type;
490 if (IS_FT_OBJECT(sp.type)) {
491 if (!sp.object_name) {
492 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
496 ff_pkt->fname = cmd; /* full plugin string */
497 ff_pkt->object_name = sp.object_name;
498 ff_pkt->object_index = sp.index; /* restore object index */
499 ff_pkt->object_compression = 0; /* no compression for now */
500 ff_pkt->object = sp.object;
501 ff_pkt->object_len = sp.object_len;
504 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
508 pm_strcpy(fname, sp.fname);
509 pm_strcpy(link, sp.link);
512 ff_pkt->fname = fname.c_str();
513 ff_pkt->link = link.c_str();
514 update_ff_pkt(ff_pkt, &sp);
517 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
518 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
520 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
522 /* Call Bacula core code to backup the plugin's file */
523 save_file(jcr, ff_pkt, true);
524 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
525 if (rc == bRC_More || rc == bRC_OK) {
526 accurate_mark_file_as_seen(jcr, fname.c_str());
528 if (rc == bRC_More) {
532 } /* end while loop */
534 } /* end loop over all plugins */
535 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
538 jcr->cmd_plugin = false;
540 jcr->plugin_ctx = NULL;
546 * Sequence of calls for a estimate:
547 * 1. plugin_estimate() here is called with ff_pkt
548 * 2. we find the plugin requested on the command string
549 * 3. we generate a bEventEstimateCommand event to the specified plugin
550 * and pass it the command string.
551 * 4. we make a startPluginBackup call to the plugin, which gives
552 * us the data we need in save_pkt
555 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
560 char *cmd = ff_pkt->top_fname;
563 POOL_MEM fname(PM_FNAME);
564 POOL_MEM link(PM_FNAME);
567 if (!bplugin_list || !jcr->plugin_ctx_list) {
568 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
569 return 1; /* Return if no plugins loaded */
572 jcr->cmd_plugin = true;
573 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
574 event.eventType = bEventEstimateCommand;
576 if (!get_plugin_name(jcr, cmd, &len)) {
580 /* Note, we stop the loop on the first plugin that matches the name */
581 foreach_alist_index(i, plugin, bplugin_list) {
582 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
583 if (!for_this_plug(plugin, cmd, len)) {
587 * We put the current plugin pointer, and the plugin context
588 * into the jcr, because during save_file(), the plugin
589 * will be called many times and these values are needed.
591 jcr->plugin_ctx = &plugin_ctx_list[i];
592 jcr->plugin = plugin;
593 if (is_plugin_disabled(jcr)) {
597 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
598 /* Send the backup command to the right plugin*/
599 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
602 /* Loop getting filenames to backup then saving them */
603 while (!jcr->is_job_canceled()) {
604 memset(&sp, 0, sizeof(sp));
605 sp.pkt_size = sizeof(sp);
606 sp.pkt_end = sizeof(sp);
610 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
612 /* Get the file save parameters. I.e. the stat pkt ... */
613 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
617 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
622 if (!IS_FT_OBJECT(sp.type)) {
624 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
629 jcr->num_files_examined++;
630 jcr->JobFiles++; /* increment number of files seen */
632 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
633 if (sp.statp.st_size > 0) {
634 jcr->JobBytes += sp.statp.st_size;
639 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
641 attr.ofname = (POOLMEM *)sp.fname;
642 attr.olname = (POOLMEM *)sp.link;
643 print_ls_output(jcr, &attr);
647 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
649 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
651 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
652 if (rc == bRC_More || rc == bRC_OK) {
653 accurate_mark_file_as_seen(jcr, sp.fname);
655 if (rc == bRC_More) {
659 } /* end while loop */
661 } /* end loop over all plugins */
662 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
665 jcr->cmd_plugin = false;
667 jcr->plugin_ctx = NULL;
672 * Send plugin name start/end record to SD
674 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
677 int index = jcr->JobFiles;
678 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
681 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
684 if (jcr->is_job_canceled()) {
689 index++; /* JobFiles not incremented yet */
691 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
692 /* Send stream header */
693 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
694 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
698 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
701 /* Send data -- not much */
702 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
704 /* Send end of data */
705 stat = sd->fsend("%ld 0", jcr->JobFiles);
708 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
712 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
713 sd->signal(BNET_EOD); /* indicate end of plugin name data */
718 * Plugin name stream found during restore. The record passed in
719 * argument name was generated in send_plugin_name() above.
721 * Returns: true if start of stream
722 * false if end of steam
724 bool plugin_name_stream(JCR *jcr, char *name)
732 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
734 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
735 skip_nonspaces(&p); /* skip over jcr->JobFiles */
739 /* Start of plugin data */
740 skip_nonspaces(&p); /* skip start/end flag */
742 // portable = *p == '1';
743 skip_nonspaces(&p); /* skip portable flag */
748 * End of plugin data, notify plugin, then clear flags
750 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
751 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
752 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
755 jcr->plugin->restoreFileStarted = false;
756 jcr->plugin->createFileCalled = false;
758 jcr->plugin_ctx = NULL;
762 if (!plugin_ctx_list) {
767 * After this point, we are dealing with a restore start
769 if (!get_plugin_name(jcr, cmd, &len)) {
774 * Search for correct plugin as specified on the command
776 foreach_alist_index(i, plugin, bplugin_list) {
778 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
779 if (!for_this_plug(plugin, cmd, len)) {
782 jcr->plugin_ctx = &plugin_ctx_list[i];
783 jcr->plugin = plugin;
784 if (is_plugin_disabled(jcr)) {
787 Dmsg1(000, "Restore Command plugin = %s\n", cmd);
788 event.eventType = bEventRestoreCommand;
789 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
790 &event, cmd) != bRC_OK) {
793 if (plugin->restoreFileStarted) {
794 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
795 plugin->restoreFileStarted = false;
798 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
799 plugin->restoreFileStarted = true;
803 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
810 * Tell the plugin to create the file. Return values are
811 * This is called only during Restore
814 * CF_SKIP -- skip processing this file
815 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
816 * CF_CREATED -- created, but no content to extract (typically directories)
819 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
821 bpContext *plugin_ctx = jcr->plugin_ctx;
822 Plugin *plugin = jcr->plugin;
823 struct restore_pkt rp;
827 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
831 rp.pkt_size = sizeof(rp);
832 rp.pkt_end = sizeof(rp);
833 rp.delta_seq = attr->delta_seq;
834 rp.stream = attr->stream;
835 rp.data_stream = attr->data_stream;
836 rp.type = attr->type;
837 rp.file_index = attr->file_index;
838 rp.LinkFI = attr->LinkFI;
840 rp.statp = attr->statp; /* structure assignment */
841 rp.attrEx = attr->attrEx;
842 rp.ofname = attr->ofname;
843 rp.olname = attr->olname;
844 rp.where = jcr->where;
845 rp.RegexWhere = jcr->RegexWhere;
846 rp.replace = jcr->replace;
847 rp.create_status = CF_ERROR;
848 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
849 rp.stream, rp.type, rp.LinkFI, rp.ofname);
851 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
853 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
854 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
855 plugin->createFileCalled = false;
858 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
860 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
864 if (rp.create_status == CF_CORE) {
865 return CF_CORE; /* Let Bacula core handle the file creation */
867 if (rp.create_status == CF_SKIP) {
870 if (rp.create_status == CF_ERROR) {
871 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
875 /* Created link or directory? */
876 if (rp.create_status == CF_CREATED) {
877 return rp.create_status; /* yes, no need to bopen */
880 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
881 Dmsg0(dbglvl, "call bopen\n");
882 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
883 Dmsg1(50, "bopen status=%d\n", stat);
886 be.set_errno(bfd->berrno);
887 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
888 attr->ofname, be.bstrerror());
889 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
893 if (!is_bopen(bfd)) {
894 Dmsg0(000, "===== BFD is not open!!!!\n");
900 * Reset the file attributes after all file I/O is done -- this allows
901 * the previous access time/dates to be set properly, and it also allows
902 * us to properly set directory permissions.
903 * Not currently Implemented.
905 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
907 Dmsg0(dbglvl, "plugin_set_attributes\n");
911 pm_strcpy(attr->ofname, "*none*");
916 * Print to file the plugin info.
918 void dump_fd_plugin(Plugin *plugin, FILE *fp)
923 pInfo *info = (pInfo *)plugin->pinfo;
924 fprintf(fp, "\tversion=%d\n", info->version);
925 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
926 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
927 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
928 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
929 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
930 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
934 * This entry point is called internally by Bacula to ensure
935 * that the plugin IO calls come into this code.
937 void load_fd_plugins(const char *plugin_dir)
943 Dmsg0(dbglvl, "plugin dir is NULL\n");
947 bplugin_list = New(alist(10, not_owned_by_alist));
948 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
949 is_plugin_compatible)) {
950 /* Either none found, or some error */
951 if (bplugin_list->size() == 0) {
954 Dmsg0(dbglvl, "No plugins loaded\n");
959 /* Plug entry points called from findlib */
960 plugin_bopen = my_plugin_bopen;
961 plugin_bclose = my_plugin_bclose;
962 plugin_bread = my_plugin_bread;
963 plugin_bwrite = my_plugin_bwrite;
964 plugin_blseek = my_plugin_blseek;
967 * Verify that the plugin is acceptable, and print information
970 foreach_alist_index(i, plugin, bplugin_list) {
971 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
972 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
975 dbg_plugin_add_hook(dump_fd_plugin);
979 * Check if a plugin is compatible. Called by the load_plugin function
980 * to allow us to verify the plugin.
982 static bool is_plugin_compatible(Plugin *plugin)
984 pInfo *info = (pInfo *)plugin->pinfo;
985 Dmsg0(50, "is_plugin_compatible called\n");
986 if (debug_level >= 50) {
987 dump_fd_plugin(plugin, stdin);
989 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
990 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
991 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
992 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
993 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
997 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
998 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
999 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1000 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1001 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1004 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1005 strcmp(info->plugin_license, "AGPLv3") != 0) {
1006 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1007 plugin->file, info->plugin_license);
1008 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1009 plugin->file, info->plugin_license);
1012 if (info->size != sizeof(pInfo)) {
1013 Jmsg(NULL, M_ERROR, 0,
1014 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1015 plugin->file, sizeof(pInfo), info->size);
1024 * Create a new instance of each plugin for this Job
1025 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1026 * be NULL if no plugins were loaded.
1028 void new_plugins(JCR *jcr)
1033 if (!bplugin_list) {
1034 Dmsg0(dbglvl, "plugin list is NULL\n");
1037 if (jcr->is_job_canceled()) {
1041 int num = bplugin_list->size();
1044 Dmsg0(dbglvl, "No plugins loaded\n");
1048 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1050 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1051 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1052 foreach_alist_index(i, plugin, bplugin_list) {
1053 /* Start a new instance of each plugin */
1054 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1055 memset(b_ctx, 0, sizeof(bacula_ctx));
1057 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1058 plugin_ctx_list[i].pContext = NULL;
1059 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1060 b_ctx->disabled = true;
1066 * Free the plugin instances for this Job
1068 void free_plugins(JCR *jcr)
1073 if (!bplugin_list || !jcr->plugin_ctx_list) {
1074 return; /* no plugins, nothing to do */
1077 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1078 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1079 foreach_alist_index(i, plugin, bplugin_list) {
1080 /* Free the plugin instance */
1081 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1082 free(plugin_ctx_list[i].bContext); /* free Bacula private context */
1084 free(plugin_ctx_list);
1085 jcr->plugin_ctx_list = NULL;
1088 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1090 JCR *jcr = bfd->jcr;
1091 Plugin *plugin = (Plugin *)jcr->plugin;
1094 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1095 if (!plugin || !jcr->plugin_ctx) {
1098 io.pkt_size = sizeof(io);
1099 io.pkt_end = sizeof(io);
1108 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1109 bfd->berrno = io.io_errno;
1111 errno = b_errno_win32;
1113 errno = io.io_errno;
1114 bfd->lerror = io.lerror;
1116 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1120 static int my_plugin_bclose(BFILE *bfd)
1122 JCR *jcr = bfd->jcr;
1123 Plugin *plugin = (Plugin *)jcr->plugin;
1126 Dmsg0(dbglvl, "===== plugin_bclose\n");
1127 if (!plugin || !jcr->plugin_ctx) {
1130 io.pkt_size = sizeof(io);
1131 io.pkt_end = sizeof(io);
1137 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1138 bfd->berrno = io.io_errno;
1140 errno = b_errno_win32;
1142 errno = io.io_errno;
1143 bfd->lerror = io.lerror;
1145 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1149 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1151 JCR *jcr = bfd->jcr;
1152 Plugin *plugin = (Plugin *)jcr->plugin;
1155 Dmsg0(dbglvl, "plugin_bread\n");
1156 if (!plugin || !jcr->plugin_ctx) {
1159 io.pkt_size = sizeof(io);
1160 io.pkt_end = sizeof(io);
1163 io.buf = (char *)buf;
1167 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1168 bfd->offset = io.offset;
1169 bfd->berrno = io.io_errno;
1171 errno = b_errno_win32;
1173 errno = io.io_errno;
1174 bfd->lerror = io.lerror;
1176 return (ssize_t)io.status;
1179 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1181 JCR *jcr = bfd->jcr;
1182 Plugin *plugin = (Plugin *)jcr->plugin;
1185 Dmsg0(dbglvl, "plugin_bwrite\n");
1186 if (!plugin || !jcr->plugin_ctx) {
1189 io.pkt_size = sizeof(io);
1190 io.pkt_end = sizeof(io);
1193 io.buf = (char *)buf;
1196 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1197 bfd->berrno = io.io_errno;
1199 errno = b_errno_win32;
1201 errno = io.io_errno;
1202 bfd->lerror = io.lerror;
1204 return (ssize_t)io.status;
1207 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1209 JCR *jcr = bfd->jcr;
1210 Plugin *plugin = (Plugin *)jcr->plugin;
1213 Dmsg0(dbglvl, "plugin_bseek\n");
1214 if (!plugin || !jcr->plugin_ctx) {
1217 io.pkt_size = sizeof(io);
1218 io.pkt_end = sizeof(io);
1224 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1225 bfd->berrno = io.io_errno;
1227 errno = b_errno_win32;
1229 errno = io.io_errno;
1230 bfd->lerror = io.lerror;
1232 return (boffset_t)io.offset;
1235 /* ==============================================================
1237 * Callbacks from the plugin
1239 * ==============================================================
1241 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1248 switch (var) { /* General variables, no need of ctx */
1250 *((char **)value) = my_name;
1252 case bVarWorkingDir:
1253 *(void **)value = me->working_directory;
1256 *(char **)value = exepath;
1259 *(char **)value = version;
1262 *(char **)value = dist_name;
1265 *((int *)value) = beef;
1271 if (!ctx) { /* Other variables need context */
1275 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1282 *((int *)value) = jcr->JobId;
1283 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1286 *((int *)value) = jcr->getJobLevel();
1287 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1290 *((int *)value) = jcr->getJobType();
1291 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1294 *((char **)value) = jcr->client_name;
1295 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1298 *((char **)value) = jcr->Job;
1299 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1301 case bVarPrevJobName:
1302 *((char **)value) = jcr->PrevJob;
1303 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1306 *((int *)value) = jcr->JobStatus;
1307 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1310 *((int *)value) = (int)jcr->mtime;
1311 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1314 *((int *)value) = (int)jcr->accurate;
1315 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1318 break; /* a write only variable, ignore read request */
1322 *(void **)value = g_pVSSClient->GetVssObject();
1327 case bVarVssDllHandle:
1330 *(void **)value = g_pVSSClient->GetVssDllHandle();
1336 *(char **)value = jcr->where;
1338 case bVarRegexWhere:
1339 *(char **)value = jcr->RegexWhere;
1341 case bVarFDName: /* get warning with g++ if we missed one */
1342 case bVarWorkingDir:
1347 case bVarPrefixLinks:
1353 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1356 if (!value || !ctx) {
1359 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1360 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1364 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1367 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1377 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1386 va_start(args, ctx);
1387 while ((event = va_arg(args, uint32_t))) {
1388 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1394 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1395 int type, utime_t mtime, const char *fmt, ...)
1402 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1407 va_start(arg_ptr, fmt);
1408 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1410 Jmsg(jcr, type, mtime, "%s", buf);
1414 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1415 int level, const char *fmt, ...)
1420 va_start(arg_ptr, fmt);
1421 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1423 d_msg(file, line, level, "%s", buf);
1427 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1431 return sm_malloc(file, line, size);
1433 return malloc(size);
1437 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1440 sm_free(file, line, mem);
1446 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1451 bctx = (bacula_ctx *)ctx->bContext;
1463 * Let the plugin define files/directories to be excluded
1464 * from the main backup.
1466 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1470 if (!is_ctx_good(ctx, jcr, bctx)) {
1476 if (!bctx->exclude) {
1477 bctx->exclude = new_exclude(jcr);
1478 new_options(jcr, bctx->exclude);
1480 set_incexe(jcr, bctx->exclude);
1481 add_file_to_fileset(jcr, file, true);
1482 Dmsg1(100, "Add exclude file=%s\n", file);
1487 * Let the plugin define files/directories to be excluded
1488 * from the main backup.
1490 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1494 if (!is_ctx_good(ctx, jcr, bctx)) {
1500 /* Not right time to add include */
1501 if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1504 if (!bctx->include) {
1505 bctx->include = jcr->ff->fileset->incexe;
1507 set_incexe(jcr, bctx->include);
1508 add_file_to_fileset(jcr, file, true);
1509 Dmsg1(100, "Add include file=%s\n", file);
1513 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1517 if (!is_ctx_good(ctx, jcr, bctx)) {
1523 add_options_to_fileset(jcr, opts);
1524 Dmsg1(1000, "Add options=%s\n", opts);
1528 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1532 if (!is_ctx_good(ctx, jcr, bctx)) {
1538 add_regex_to_fileset(jcr, item, type);
1539 Dmsg1(100, "Add regex=%s\n", item);
1543 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1547 if (!is_ctx_good(ctx, jcr, bctx)) {
1553 add_wild_to_fileset(jcr, item, type);
1554 Dmsg1(100, "Add wild=%s\n", item);
1558 static bRC baculaNewOptions(bpContext *ctx)
1562 if (!is_ctx_good(ctx, jcr, bctx)) {
1565 (void)new_options(jcr, NULL);
1569 static bRC baculaNewInclude(bpContext *ctx)
1573 if (!is_ctx_good(ctx, jcr, bctx)) {
1576 (void)new_include(jcr);
1580 static bRC baculaNewPreInclude(bpContext *ctx)
1584 if (!is_ctx_good(ctx, jcr, bctx)) {
1588 bctx->include = new_preinclude(jcr);
1589 new_options(jcr, bctx->include);
1590 set_incexe(jcr, bctx->include);
1596 * Check if a file have to be backuped using Accurate code
1598 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1603 bRC ret = bRC_Error;
1605 if (!is_ctx_good(ctx, jcr, bctx)) {
1614 * Copy fname and link because save_file() zaps them. This
1615 * avoids zaping the plugin's strings.
1617 ff_pkt->type = sp->type;
1619 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1623 ff_pkt->fname = sp->fname;
1624 ff_pkt->link = sp->link;
1625 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1627 if (check_changes(jcr, ff_pkt)) {
1633 /* check_changes() can update delta sequence number, return it to the
1636 sp->delta_seq = ff_pkt->delta_seq;
1637 sp->accurate_found = ff_pkt->accurate_found;
1640 Dmsg1(100, "checkChanges=%i\n", ret);
1647 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1648 int (*plugin_bclose)(JCR *jcr) = NULL;
1649 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1650 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1651 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1653 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1658 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1663 int main(int argc, char *argv[])
1665 char plugin_dir[1000];
1670 strcpy(my_name, "test-fd");
1672 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1673 load_fd_plugins(plugin_dir);
1681 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1682 generate_plugin_event(jcr1, bEventJobEnd);
1683 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1685 generate_plugin_event(jcr2, bEventJobEnd);
1690 Dmsg0(dbglvl, "bacula: OK ...\n");
1691 close_memory_pool();
1692 sm_dump(false); /* unit test */
1696 #endif /* TEST_PROGRAM */