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;
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(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)) {
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(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)) {
450 * We put the current plugin pointer, and the plugin context
451 * into the jcr, because during save_file(), the plugin
452 * will be called many times and these values are needed.
454 jcr->plugin_ctx = &plugin_ctx_list[i];
455 jcr->plugin = plugin;
456 if (is_plugin_disabled(jcr)) {
460 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
461 /* Send the backup command to the right plugin*/
462 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
465 /* Loop getting filenames to backup then saving them */
466 while (!jcr->is_job_canceled()) {
467 memset(&sp, 0, sizeof(sp));
468 sp.pkt_size = sizeof(sp);
469 sp.pkt_end = sizeof(sp);
473 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
475 /* Get the file save parameters. I.e. the stat pkt ... */
476 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
480 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
484 jcr->plugin_sp = &sp;
487 * Copy fname and link because save_file() zaps them. This
488 * avoids zaping the plugin's strings.
490 ff_pkt->type = sp.type;
491 if (IS_FT_OBJECT(sp.type)) {
492 if (!sp.object_name) {
493 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
497 ff_pkt->fname = cmd; /* full plugin string */
498 ff_pkt->object_name = sp.object_name;
499 ff_pkt->object_index = sp.index; /* restore object index */
500 ff_pkt->object_compression = 0; /* no compression for now */
501 ff_pkt->object = sp.object;
502 ff_pkt->object_len = sp.object_len;
505 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
509 pm_strcpy(fname, sp.fname);
510 pm_strcpy(link, sp.link);
513 ff_pkt->fname = fname.c_str();
514 ff_pkt->link = link.c_str();
515 update_ff_pkt(ff_pkt, &sp);
518 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
519 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
521 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
523 /* Call Bacula core code to backup the plugin's file */
524 save_file(jcr, ff_pkt, true);
525 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
526 if (rc == bRC_More || rc == bRC_OK) {
527 accurate_mark_file_as_seen(jcr, fname.c_str());
529 if (rc == bRC_More) {
533 } /* end while loop */
535 } /* end loop over all plugins */
536 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
539 jcr->cmd_plugin = false;
541 jcr->plugin_ctx = NULL;
547 * Sequence of calls for a estimate:
548 * 1. plugin_estimate() here is called with ff_pkt
549 * 2. we find the plugin requested on the command string
550 * 3. we generate a bEventEstimateCommand event to the specified plugin
551 * and pass it the command string.
552 * 4. we make a startPluginBackup call to the plugin, which gives
553 * us the data we need in save_pkt
556 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
561 char *cmd = ff_pkt->top_fname;
564 POOL_MEM fname(PM_FNAME);
565 POOL_MEM link(PM_FNAME);
568 if (!bplugin_list || !jcr->plugin_ctx_list) {
569 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
570 return 1; /* Return if no plugins loaded */
573 jcr->cmd_plugin = true;
574 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
575 event.eventType = bEventEstimateCommand;
577 if (!get_plugin_name(jcr, cmd, &len)) {
581 /* Note, we stop the loop on the first plugin that matches the name */
582 foreach_alist(plugin, bplugin_list) {
583 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
584 if (!for_this_plug(plugin, cmd, len)) {
589 * We put the current plugin pointer, and the plugin context
590 * into the jcr, because during save_file(), the plugin
591 * will be called many times and these values are needed.
593 jcr->plugin_ctx = &plugin_ctx_list[i];
594 jcr->plugin = plugin;
595 if (is_plugin_disabled(jcr)) {
599 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
600 /* Send the backup command to the right plugin*/
601 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
604 /* Loop getting filenames to backup then saving them */
605 while (!jcr->is_job_canceled()) {
606 memset(&sp, 0, sizeof(sp));
607 sp.pkt_size = sizeof(sp);
608 sp.pkt_end = sizeof(sp);
612 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
614 /* Get the file save parameters. I.e. the stat pkt ... */
615 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
619 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
624 if (!IS_FT_OBJECT(sp.type)) {
626 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
631 jcr->num_files_examined++;
632 jcr->JobFiles++; /* increment number of files seen */
634 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
635 if (sp.statp.st_size > 0) {
636 jcr->JobBytes += sp.statp.st_size;
641 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
643 attr.ofname = (POOLMEM *)sp.fname;
644 attr.olname = (POOLMEM *)sp.link;
645 print_ls_output(jcr, &attr);
649 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
651 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
653 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
654 if (rc == bRC_More || rc == bRC_OK) {
655 accurate_mark_file_as_seen(jcr, sp.fname);
657 if (rc == bRC_More) {
661 } /* end while loop */
663 } /* end loop over all plugins */
664 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
667 jcr->cmd_plugin = false;
669 jcr->plugin_ctx = NULL;
674 * Send plugin name start/end record to SD
676 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
679 int index = jcr->JobFiles;
680 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
683 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
686 if (jcr->is_job_canceled()) {
691 index++; /* JobFiles not incremented yet */
693 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
694 /* Send stream header */
695 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
696 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
700 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
703 /* Send data -- not much */
704 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
706 /* Send end of data */
707 stat = sd->fsend("%ld 0", jcr->JobFiles);
710 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
714 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
715 sd->signal(BNET_EOD); /* indicate end of plugin name data */
720 * Plugin name stream found during restore. The record passed in
721 * argument name was generated in send_plugin_name() above.
723 * Returns: true if start of stream
724 * false if end of steam
726 bool plugin_name_stream(JCR *jcr, char *name)
734 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
736 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
737 skip_nonspaces(&p); /* skip over jcr->JobFiles */
741 /* Start of plugin data */
742 skip_nonspaces(&p); /* skip start/end flag */
744 // portable = *p == '1';
745 skip_nonspaces(&p); /* skip portable flag */
750 * End of plugin data, notify plugin, then clear flags
752 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
753 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
754 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
757 jcr->plugin->restoreFileStarted = false;
758 jcr->plugin->createFileCalled = false;
760 jcr->plugin_ctx = NULL;
764 if (!plugin_ctx_list) {
769 * After this point, we are dealing with a restore start
771 if (!get_plugin_name(jcr, cmd, &len)) {
776 * Search for correct plugin as specified on the command
778 foreach_alist(plugin, bplugin_list) {
780 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
781 if (!for_this_plug(plugin, cmd, len)) {
785 jcr->plugin_ctx = &plugin_ctx_list[i];
786 jcr->plugin = plugin;
787 if (is_plugin_disabled(jcr)) {
790 Dmsg1(000, "Restore Command plugin = %s\n", cmd);
791 event.eventType = bEventRestoreCommand;
792 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
793 &event, cmd) != bRC_OK) {
796 if (plugin->restoreFileStarted) {
797 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
798 plugin->restoreFileStarted = false;
801 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
802 plugin->restoreFileStarted = true;
806 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
813 * Tell the plugin to create the file. Return values are
814 * This is called only during Restore
817 * CF_SKIP -- skip processing this file
818 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
819 * CF_CREATED -- created, but no content to extract (typically directories)
822 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
824 bpContext *plugin_ctx = jcr->plugin_ctx;
825 Plugin *plugin = jcr->plugin;
826 struct restore_pkt rp;
830 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
834 rp.pkt_size = sizeof(rp);
835 rp.pkt_end = sizeof(rp);
836 rp.delta_seq = attr->delta_seq;
837 rp.stream = attr->stream;
838 rp.data_stream = attr->data_stream;
839 rp.type = attr->type;
840 rp.file_index = attr->file_index;
841 rp.LinkFI = attr->LinkFI;
843 rp.statp = attr->statp; /* structure assignment */
844 rp.attrEx = attr->attrEx;
845 rp.ofname = attr->ofname;
846 rp.olname = attr->olname;
847 rp.where = jcr->where;
848 rp.RegexWhere = jcr->RegexWhere;
849 rp.replace = jcr->replace;
850 rp.create_status = CF_ERROR;
851 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
852 rp.stream, rp.type, rp.LinkFI, rp.ofname);
854 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
856 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
857 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
858 plugin->createFileCalled = false;
861 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
863 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
867 if (rp.create_status == CF_CORE) {
868 return CF_CORE; /* Let Bacula core handle the file creation */
870 if (rp.create_status == CF_SKIP) {
873 if (rp.create_status == CF_ERROR) {
874 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
878 /* Created link or directory? */
879 if (rp.create_status == CF_CREATED) {
880 return rp.create_status; /* yes, no need to bopen */
883 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
884 Dmsg0(dbglvl, "call bopen\n");
885 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
886 Dmsg1(50, "bopen status=%d\n", stat);
889 be.set_errno(bfd->berrno);
890 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
891 attr->ofname, be.bstrerror());
892 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
896 if (!is_bopen(bfd)) {
897 Dmsg0(000, "===== BFD is not open!!!!\n");
903 * Reset the file attributes after all file I/O is done -- this allows
904 * the previous access time/dates to be set properly, and it also allows
905 * us to properly set directory permissions.
906 * Not currently Implemented.
908 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
910 Dmsg0(dbglvl, "plugin_set_attributes\n");
914 pm_strcpy(attr->ofname, "*none*");
919 * Print to file the plugin info.
921 void dump_fd_plugin(Plugin *plugin, FILE *fp)
926 pInfo *info = (pInfo *)plugin->pinfo;
927 fprintf(fp, "\tversion=%d\n", info->version);
928 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
929 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
930 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
931 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
932 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
933 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
937 * This entry point is called internally by Bacula to ensure
938 * that the plugin IO calls come into this code.
940 void load_fd_plugins(const char *plugin_dir)
945 Dmsg0(dbglvl, "plugin dir is NULL\n");
949 bplugin_list = New(alist(10, not_owned_by_alist));
950 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
951 is_plugin_compatible)) {
952 /* Either none found, or some error */
953 if (bplugin_list->size() == 0) {
956 Dmsg0(dbglvl, "No plugins loaded\n");
961 /* Plug entry points called from findlib */
962 plugin_bopen = my_plugin_bopen;
963 plugin_bclose = my_plugin_bclose;
964 plugin_bread = my_plugin_bread;
965 plugin_bwrite = my_plugin_bwrite;
966 plugin_blseek = my_plugin_blseek;
969 * Verify that the plugin is acceptable, and print information
972 foreach_alist(plugin, bplugin_list) {
973 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
974 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
977 dbg_plugin_add_hook(dump_fd_plugin);
981 * Check if a plugin is compatible. Called by the load_plugin function
982 * to allow us to verify the plugin.
984 static bool is_plugin_compatible(Plugin *plugin)
986 pInfo *info = (pInfo *)plugin->pinfo;
987 Dmsg0(50, "is_plugin_compatible called\n");
988 if (debug_level >= 50) {
989 dump_fd_plugin(plugin, stdin);
991 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
992 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
993 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
994 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
995 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
999 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1000 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1001 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1002 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1003 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1006 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1007 strcmp(info->plugin_license, "AGPLv3") != 0) {
1008 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1009 plugin->file, info->plugin_license);
1010 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1011 plugin->file, info->plugin_license);
1014 if (info->size != sizeof(pInfo)) {
1015 Jmsg(NULL, M_ERROR, 0,
1016 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1017 plugin->file, sizeof(pInfo), info->size);
1026 * Create a new instance of each plugin for this Job
1027 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1028 * be NULL if no plugins were loaded.
1030 void new_plugins(JCR *jcr)
1035 if (!bplugin_list) {
1036 Dmsg0(dbglvl, "plugin list is NULL\n");
1039 if (jcr->is_job_canceled()) {
1043 int num = bplugin_list->size();
1046 Dmsg0(dbglvl, "No plugins loaded\n");
1050 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1052 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1053 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1054 foreach_alist(plugin, bplugin_list) {
1055 /* Start a new instance of each plugin */
1056 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1057 memset(b_ctx, 0, sizeof(bacula_ctx));
1059 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1060 plugin_ctx_list[i].pContext = NULL;
1061 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
1062 b_ctx->disabled = true;
1068 * Free the plugin instances for this Job
1070 void free_plugins(JCR *jcr)
1075 if (!bplugin_list || !jcr->plugin_ctx_list) {
1076 return; /* no plugins, nothing to do */
1079 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1080 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1081 foreach_alist(plugin, bplugin_list) {
1082 /* Free the plugin instance */
1083 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1084 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
1086 free(plugin_ctx_list);
1087 jcr->plugin_ctx_list = NULL;
1090 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1092 JCR *jcr = bfd->jcr;
1093 Plugin *plugin = (Plugin *)jcr->plugin;
1096 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1097 if (!plugin || !jcr->plugin_ctx) {
1100 io.pkt_size = sizeof(io);
1101 io.pkt_end = sizeof(io);
1110 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1111 bfd->berrno = io.io_errno;
1113 errno = b_errno_win32;
1115 errno = io.io_errno;
1116 bfd->lerror = io.lerror;
1118 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1122 static int my_plugin_bclose(BFILE *bfd)
1124 JCR *jcr = bfd->jcr;
1125 Plugin *plugin = (Plugin *)jcr->plugin;
1128 Dmsg0(dbglvl, "===== plugin_bclose\n");
1129 if (!plugin || !jcr->plugin_ctx) {
1132 io.pkt_size = sizeof(io);
1133 io.pkt_end = sizeof(io);
1139 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1140 bfd->berrno = io.io_errno;
1142 errno = b_errno_win32;
1144 errno = io.io_errno;
1145 bfd->lerror = io.lerror;
1147 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1151 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1153 JCR *jcr = bfd->jcr;
1154 Plugin *plugin = (Plugin *)jcr->plugin;
1157 Dmsg0(dbglvl, "plugin_bread\n");
1158 if (!plugin || !jcr->plugin_ctx) {
1161 io.pkt_size = sizeof(io);
1162 io.pkt_end = sizeof(io);
1165 io.buf = (char *)buf;
1169 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1170 bfd->offset = io.offset;
1171 bfd->berrno = io.io_errno;
1173 errno = b_errno_win32;
1175 errno = io.io_errno;
1176 bfd->lerror = io.lerror;
1178 return (ssize_t)io.status;
1181 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1183 JCR *jcr = bfd->jcr;
1184 Plugin *plugin = (Plugin *)jcr->plugin;
1187 Dmsg0(dbglvl, "plugin_bwrite\n");
1188 if (!plugin || !jcr->plugin_ctx) {
1191 io.pkt_size = sizeof(io);
1192 io.pkt_end = sizeof(io);
1195 io.buf = (char *)buf;
1198 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1199 bfd->berrno = io.io_errno;
1201 errno = b_errno_win32;
1203 errno = io.io_errno;
1204 bfd->lerror = io.lerror;
1206 return (ssize_t)io.status;
1209 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1211 JCR *jcr = bfd->jcr;
1212 Plugin *plugin = (Plugin *)jcr->plugin;
1215 Dmsg0(dbglvl, "plugin_bseek\n");
1216 if (!plugin || !jcr->plugin_ctx) {
1219 io.pkt_size = sizeof(io);
1220 io.pkt_end = sizeof(io);
1226 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1227 bfd->berrno = io.io_errno;
1229 errno = b_errno_win32;
1231 errno = io.io_errno;
1232 bfd->lerror = io.lerror;
1234 return (boffset_t)io.offset;
1237 /* ==============================================================
1239 * Callbacks from the plugin
1241 * ==============================================================
1243 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1250 switch (var) { /* General variables, no need of ctx */
1252 *((char **)value) = my_name;
1254 case bVarWorkingDir:
1255 *(void **)value = me->working_directory;
1258 *(char **)value = exepath;
1261 *(char **)value = version;
1264 *(char **)value = dist_name;
1267 *((int *)value) = beef;
1273 if (!ctx) { /* Other variables need context */
1277 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1284 *((int *)value) = jcr->JobId;
1285 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1288 *((int *)value) = jcr->getJobLevel();
1289 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1292 *((int *)value) = jcr->getJobType();
1293 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1296 *((char **)value) = jcr->client_name;
1297 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1300 *((char **)value) = jcr->Job;
1301 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1303 case bVarPrevJobName:
1304 *((char **)value) = jcr->PrevJob;
1305 Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1308 *((int *)value) = jcr->JobStatus;
1309 Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1312 *((int *)value) = (int)jcr->mtime;
1313 Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1316 *((int *)value) = (int)jcr->accurate;
1317 Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1320 break; /* a write only variable, ignore read request */
1324 *(void **)value = g_pVSSClient->GetVssObject();
1329 case bVarVssDllHandle:
1332 *(void **)value = g_pVSSClient->GetVssDllHandle();
1338 *(char **)value = jcr->where;
1340 case bVarRegexWhere:
1341 *(char **)value = jcr->RegexWhere;
1344 case bVarFDName: /* get warning with g++ if we missed one */
1345 case bVarWorkingDir:
1355 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1358 if (!value || !ctx) {
1361 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1362 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1366 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1369 if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1379 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1388 va_start(args, ctx);
1389 while ((event = va_arg(args, uint32_t))) {
1390 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1396 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1397 int type, utime_t mtime, const char *fmt, ...)
1404 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1409 va_start(arg_ptr, fmt);
1410 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1412 Jmsg(jcr, type, mtime, "%s", buf);
1416 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1417 int level, const char *fmt, ...)
1422 va_start(arg_ptr, fmt);
1423 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1425 d_msg(file, line, level, "%s", buf);
1429 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1433 return sm_malloc(file, line, size);
1435 return malloc(size);
1439 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1442 sm_free(file, line, mem);
1448 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1453 bctx = (bacula_ctx *)ctx->bContext;
1465 * Let the plugin define files/directories to be excluded
1466 * from the main backup.
1468 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1472 if (!is_ctx_good(ctx, jcr, bctx)) {
1478 if (!bctx->exclude) {
1479 bctx->exclude = new_exclude(jcr);
1480 new_options(jcr, bctx->exclude);
1482 set_incexe(jcr, bctx->exclude);
1483 add_file_to_fileset(jcr, file, true);
1484 Dmsg1(100, "Add exclude file=%s\n", file);
1489 * Let the plugin define files/directories to be excluded
1490 * from the main backup.
1492 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1496 if (!is_ctx_good(ctx, jcr, bctx)) {
1502 /* Not right time to add include */
1503 if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1506 if (!bctx->include) {
1507 bctx->include = jcr->ff->fileset->incexe;
1509 set_incexe(jcr, bctx->include);
1510 add_file_to_fileset(jcr, file, true);
1511 Dmsg1(100, "Add include file=%s\n", file);
1515 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1519 if (!is_ctx_good(ctx, jcr, bctx)) {
1525 add_options_to_fileset(jcr, opts);
1526 Dmsg1(1000, "Add options=%s\n", opts);
1530 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1534 if (!is_ctx_good(ctx, jcr, bctx)) {
1540 add_regex_to_fileset(jcr, item, type);
1541 Dmsg1(100, "Add regex=%s\n", item);
1545 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1549 if (!is_ctx_good(ctx, jcr, bctx)) {
1555 add_wild_to_fileset(jcr, item, type);
1556 Dmsg1(100, "Add wild=%s\n", item);
1560 static bRC baculaNewOptions(bpContext *ctx)
1564 if (!is_ctx_good(ctx, jcr, bctx)) {
1567 (void)new_options(jcr, NULL);
1571 static bRC baculaNewInclude(bpContext *ctx)
1575 if (!is_ctx_good(ctx, jcr, bctx)) {
1578 (void)new_include(jcr);
1582 static bRC baculaNewPreInclude(bpContext *ctx)
1586 if (!is_ctx_good(ctx, jcr, bctx)) {
1590 bctx->include = new_preinclude(jcr);
1591 new_options(jcr, bctx->include);
1592 set_incexe(jcr, bctx->include);
1598 * Check if a file have to be backuped using Accurate code
1600 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1605 bRC ret = bRC_Error;
1607 if (!is_ctx_good(ctx, jcr, bctx)) {
1616 * Copy fname and link because save_file() zaps them. This
1617 * avoids zaping the plugin's strings.
1619 ff_pkt->type = sp->type;
1621 Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1625 ff_pkt->fname = sp->fname;
1626 ff_pkt->link = sp->link;
1627 memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1629 if (check_changes(jcr, ff_pkt)) {
1635 /* check_changes() can update delta sequence number, return it to the
1638 sp->delta_seq = ff_pkt->delta_seq;
1641 Dmsg1(100, "checkChanges=%i\n", ret);
1648 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1649 int (*plugin_bclose)(JCR *jcr) = NULL;
1650 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1651 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1652 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1654 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1659 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1664 int main(int argc, char *argv[])
1666 char plugin_dir[1000];
1671 strcpy(my_name, "test-fd");
1673 getcwd(plugin_dir, sizeof(plugin_dir)-1);
1674 load_fd_plugins(plugin_dir);
1682 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1683 generate_plugin_event(jcr1, bEventJobEnd);
1684 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1686 generate_plugin_event(jcr2, bEventJobEnd);
1691 Dmsg0(dbglvl, "bacula: OK ...\n");
1692 close_memory_pool();
1693 sm_dump(false); /* unit test */
1697 #endif /* TEST_PROGRAM */