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;
169 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
170 return; /* Return if no plugins loaded */
174 * Some events are sent to only a particular plugin or must be
175 * called even if the job is canceled
178 case bEventPluginCommand:
179 case bEventOptionPlugin:
180 name = (char *)value;
181 if (!get_plugin_name(jcr, name, &len)) {
185 case bEventRestoreObject:
186 /* After all RestoreObject, we have it one more time with value=NULL */
188 /* Some RestoreObjects may not have a plugin name */
189 rop = (restore_object_pkt *)value;
190 if (*rop->plugin_name) {
191 name = rop->plugin_name;
192 get_plugin_name(jcr, name, &len);
197 case bEventEndBackupJob:
198 case bEventEndVerifyJob:
199 call_if_canceled = true;
201 case bEventStartRestoreJob:
202 foreach_alist(plugin, bplugin_list) {
203 plugin->restoreFileStarted = false;
204 plugin->createFileCalled = false;
207 case bEventEndRestoreJob:
208 call_if_canceled = true;
209 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
210 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
213 jcr->plugin->restoreFileStarted = false;
214 jcr->plugin->createFileCalled = false;
221 if (!call_if_canceled && jcr->is_job_canceled()) {
225 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
226 event.eventType = eventType;
228 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
231 * Pass event to every plugin (except if name is set). If name
232 * is set, we pass it only to the plugin with that name.
234 foreach_alist(plugin, bplugin_list) {
235 if (name && !for_this_plug(plugin, name, len)) {
239 plugin_ctx = &plugin_ctx_list[i++];
240 if (is_plugin_disabled(plugin_ctx)) {
243 rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
252 * Check if file was seen for accurate
254 bool plugin_check_file(JCR *jcr, char *fname)
260 if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
261 return false; /* Return if no plugins loaded */
264 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
266 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
268 /* Pass event to every plugin */
269 foreach_alist(plugin, bplugin_list) {
270 jcr->plugin_ctx = &plugin_ctx_list[i++];
271 jcr->plugin = plugin;
272 if (is_plugin_disabled(jcr)) {
275 if (plug_func(plugin)->checkFile == NULL) {
278 rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
279 if (rc == bRC_Seen) {
285 jcr->plugin_ctx = NULL;
286 return rc == bRC_Seen;
289 /* Get the first part of the the plugin command
290 * systemstate:/@SYSTEMSTATE/
292 * => can use for_this_plug(plug, cmd, ret);
294 * The plugin command can contain only the plugin name
298 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
302 if (!cmd || (*cmd == '\0')) {
305 /* Handle plugin command here backup */
306 Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
307 if ((p = strchr(cmd, ':')) == NULL) {
308 if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
311 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
314 } else { /* plugin:argument */
325 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
327 ff_pkt->delta_seq = sp->delta_seq;
328 if (sp->flags & FO_DELTA) {
329 ff_pkt->flags |= FO_DELTA;
330 ff_pkt->delta_seq++; /* make new delta sequence number */
332 ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */
333 ff_pkt->delta_seq = 0;
336 if (sp->flags & FO_OFFSETS) {
337 ff_pkt->flags |= FO_OFFSETS;
339 if (sp->flags & FO_PORTABLE_DATA) {
340 ff_pkt->flags |= FO_PORTABLE_DATA;
342 ff_pkt->flags |= FO_PLUGIN; /* data from plugin */
345 /* Ask to a Option Plugin what to do with the current file */
346 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
350 char *cmd = ff_pkt->plugin;
354 event.eventType = bEventHandleBackupFile;
356 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
357 memset(sp, 0, sizeof(struct save_pkt));
358 sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
361 sp->link = ff_pkt->link;
362 sp->cmd = ff_pkt->plugin;
363 sp->statp = ff_pkt->statp;
364 sp->fname = ff_pkt->fname;
365 sp->delta_seq = ff_pkt->delta_seq;
367 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
368 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
369 goto bail_out; /* Return if no plugins loaded */
372 if (!get_plugin_name(jcr, cmd, &len)) {
376 /* Note, we stop the loop on the first plugin that matches the name */
377 foreach_alist(plugin, bplugin_list) {
378 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
379 if (!for_this_plug(plugin, cmd, len)) {
384 if (is_plugin_disabled(&plugin_ctx_list[i])) {
388 ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
391 /* TODO: would be better to set this in save_file() */
393 jcr->opt_plugin = true;
394 jcr->plugin = plugin;
395 jcr->plugin_sp = sp; /* Unset sp in save_file */
396 jcr->plugin_ctx = &plugin_ctx_list[i++];
398 update_ff_pkt(ff_pkt, sp);
402 } /* end foreach loop */
408 * Sequence of calls for a backup:
409 * 1. plugin_save() here is called with ff_pkt
410 * 2. we find the plugin requested on the command string
411 * 3. we generate a bEventBackupCommand event to the specified plugin
412 * and pass it the command string.
413 * 4. we make a startPluginBackup call to the plugin, which gives
414 * us the data we need in save_pkt
415 * 5. we call Bacula's save_file() subroutine to save the specified
416 * file. The plugin will be called at pluginIO() to supply the
419 * Sequence of calls for restore:
420 * See subroutine plugin_name_stream() below.
422 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
427 char *cmd = ff_pkt->top_fname;
430 POOL_MEM fname(PM_FNAME);
431 POOL_MEM link(PM_FNAME);
433 if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
434 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
435 return 1; /* Return if no plugins loaded */
438 jcr->cmd_plugin = true;
439 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
440 event.eventType = bEventBackupCommand;
442 if (!get_plugin_name(jcr, cmd, &len)) {
446 /* Note, we stop the loop on the first plugin that matches the name */
447 foreach_alist(plugin, bplugin_list) {
448 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
449 if (!for_this_plug(plugin, cmd, len)) {
454 * We put the current plugin pointer, and the plugin context
455 * into the jcr, because during save_file(), the plugin
456 * will be called many times and these values are needed.
458 jcr->plugin_ctx = &plugin_ctx_list[i];
459 jcr->plugin = plugin;
460 if (is_plugin_disabled(jcr)) {
464 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
465 /* Send the backup command to the right plugin*/
466 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
469 /* Loop getting filenames to backup then saving them */
470 while (!jcr->is_job_canceled()) {
471 memset(&sp, 0, sizeof(sp));
472 sp.pkt_size = sizeof(sp);
473 sp.pkt_end = sizeof(sp);
477 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
479 /* Get the file save parameters. I.e. the stat pkt ... */
480 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
484 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
488 jcr->plugin_sp = &sp;
491 * Copy fname and link because save_file() zaps them. This
492 * avoids zaping the plugin's strings.
494 ff_pkt->type = sp.type;
495 if (IS_FT_OBJECT(sp.type)) {
496 if (!sp.object_name) {
497 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
501 ff_pkt->fname = cmd; /* full plugin string */
502 ff_pkt->object_name = sp.object_name;
503 ff_pkt->object_index = sp.index; /* restore object index */
504 ff_pkt->object_compression = 0; /* no compression for now */
505 ff_pkt->object = sp.object;
506 ff_pkt->object_len = sp.object_len;
509 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
513 pm_strcpy(fname, sp.fname);
514 pm_strcpy(link, sp.link);
517 ff_pkt->fname = fname.c_str();
518 ff_pkt->link = link.c_str();
519 update_ff_pkt(ff_pkt, &sp);
522 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
523 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
525 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
527 /* Call Bacula core code to backup the plugin's file */
528 save_file(jcr, ff_pkt, true);
529 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
530 if (rc == bRC_More || rc == bRC_OK) {
531 accurate_mark_file_as_seen(jcr, fname.c_str());
533 if (rc == bRC_More) {
537 } /* end while loop */
539 } /* end loop over all plugins */
540 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
543 jcr->cmd_plugin = false;
545 jcr->plugin_ctx = NULL;
551 * Sequence of calls for a estimate:
552 * 1. plugin_estimate() here is called with ff_pkt
553 * 2. we find the plugin requested on the command string
554 * 3. we generate a bEventEstimateCommand event to the specified plugin
555 * and pass it the command string.
556 * 4. we make a startPluginBackup call to the plugin, which gives
557 * us the data we need in save_pkt
560 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
565 char *cmd = ff_pkt->top_fname;
568 POOL_MEM fname(PM_FNAME);
569 POOL_MEM link(PM_FNAME);
572 if (!bplugin_list || !jcr->plugin_ctx_list) {
573 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
574 return 1; /* Return if no plugins loaded */
577 jcr->cmd_plugin = true;
578 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
579 event.eventType = bEventEstimateCommand;
581 if (!get_plugin_name(jcr, cmd, &len)) {
585 /* Note, we stop the loop on the first plugin that matches the name */
586 foreach_alist(plugin, bplugin_list) {
587 Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
588 if (!for_this_plug(plugin, cmd, len)) {
593 * We put the current plugin pointer, and the plugin context
594 * into the jcr, because during save_file(), the plugin
595 * will be called many times and these values are needed.
597 jcr->plugin_ctx = &plugin_ctx_list[i];
598 jcr->plugin = plugin;
599 if (is_plugin_disabled(jcr)) {
603 Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
604 /* Send the backup command to the right plugin*/
605 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
608 /* Loop getting filenames to backup then saving them */
609 while (!jcr->is_job_canceled()) {
610 memset(&sp, 0, sizeof(sp));
611 sp.pkt_size = sizeof(sp);
612 sp.pkt_end = sizeof(sp);
616 Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
618 /* Get the file save parameters. I.e. the stat pkt ... */
619 if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
623 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
628 if (!IS_FT_OBJECT(sp.type)) {
630 Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
635 jcr->num_files_examined++;
636 jcr->JobFiles++; /* increment number of files seen */
638 if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
639 if (sp.statp.st_size > 0) {
640 jcr->JobBytes += sp.statp.st_size;
645 memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
647 attr.ofname = (POOLMEM *)sp.fname;
648 attr.olname = (POOLMEM *)sp.link;
649 print_ls_output(jcr, &attr);
653 Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
655 Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
657 bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
658 if (rc == bRC_More || rc == bRC_OK) {
659 accurate_mark_file_as_seen(jcr, sp.fname);
661 if (rc == bRC_More) {
665 } /* end while loop */
667 } /* end loop over all plugins */
668 Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
671 jcr->cmd_plugin = false;
673 jcr->plugin_ctx = NULL;
678 * Send plugin name start/end record to SD
680 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
683 int index = jcr->JobFiles;
684 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
687 Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
690 if (jcr->is_job_canceled()) {
695 index++; /* JobFiles not incremented yet */
697 Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
698 /* Send stream header */
699 if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
700 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
704 Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
707 /* Send data -- not much */
708 stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
710 /* Send end of data */
711 stat = sd->fsend("%ld 0", jcr->JobFiles);
714 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
718 Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
719 sd->signal(BNET_EOD); /* indicate end of plugin name data */
724 * Plugin name stream found during restore. The record passed in
725 * argument name was generated in send_plugin_name() above.
727 * Returns: true if start of stream
728 * false if end of steam
730 bool plugin_name_stream(JCR *jcr, char *name)
738 bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
740 Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
741 skip_nonspaces(&p); /* skip over jcr->JobFiles */
745 /* Start of plugin data */
746 skip_nonspaces(&p); /* skip start/end flag */
748 // portable = *p == '1';
749 skip_nonspaces(&p); /* skip portable flag */
754 * End of plugin data, notify plugin, then clear flags
756 Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
757 if (jcr->plugin && jcr->plugin->restoreFileStarted) {
758 plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
761 jcr->plugin->restoreFileStarted = false;
762 jcr->plugin->createFileCalled = false;
764 jcr->plugin_ctx = NULL;
768 if (!plugin_ctx_list) {
773 * After this point, we are dealing with a restore start
775 if (!get_plugin_name(jcr, cmd, &len)) {
780 * Search for correct plugin as specified on the command
782 foreach_alist(plugin, bplugin_list) {
784 Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
785 if (!for_this_plug(plugin, cmd, len)) {
789 jcr->plugin_ctx = &plugin_ctx_list[i];
790 jcr->plugin = plugin;
791 if (is_plugin_disabled(jcr)) {
794 Dmsg1(000, "Restore Command plugin = %s\n", cmd);
795 event.eventType = bEventRestoreCommand;
796 if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
797 &event, cmd) != bRC_OK) {
800 if (plugin->restoreFileStarted) {
801 Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
802 plugin->restoreFileStarted = false;
805 if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
806 plugin->restoreFileStarted = true;
810 Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
817 * Tell the plugin to create the file. Return values are
818 * This is called only during Restore
821 * CF_SKIP -- skip processing this file
822 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
823 * CF_CREATED -- created, but no content to extract (typically directories)
826 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
828 bpContext *plugin_ctx = jcr->plugin_ctx;
829 Plugin *plugin = jcr->plugin;
830 struct restore_pkt rp;
834 if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
838 rp.pkt_size = sizeof(rp);
839 rp.pkt_end = sizeof(rp);
840 rp.delta_seq = attr->delta_seq;
841 rp.stream = attr->stream;
842 rp.data_stream = attr->data_stream;
843 rp.type = attr->type;
844 rp.file_index = attr->file_index;
845 rp.LinkFI = attr->LinkFI;
847 rp.statp = attr->statp; /* structure assignment */
848 rp.attrEx = attr->attrEx;
849 rp.ofname = attr->ofname;
850 rp.olname = attr->olname;
851 rp.where = jcr->where;
852 rp.RegexWhere = jcr->RegexWhere;
853 rp.replace = jcr->replace;
854 rp.create_status = CF_ERROR;
855 Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
856 rp.stream, rp.type, rp.LinkFI, rp.ofname);
858 Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
860 if (!plugin->restoreFileStarted || plugin->createFileCalled) {
861 Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
862 plugin->createFileCalled = false;
865 rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
867 Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
871 if (rp.create_status == CF_CORE) {
872 return CF_CORE; /* Let Bacula core handle the file creation */
874 if (rp.create_status == CF_SKIP) {
877 if (rp.create_status == CF_ERROR) {
878 Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
882 /* Created link or directory? */
883 if (rp.create_status == CF_CREATED) {
884 return rp.create_status; /* yes, no need to bopen */
887 flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
888 Dmsg0(dbglvl, "call bopen\n");
889 int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
890 Dmsg1(50, "bopen status=%d\n", stat);
893 be.set_errno(bfd->berrno);
894 Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
895 attr->ofname, be.bstrerror());
896 Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
900 if (!is_bopen(bfd)) {
901 Dmsg0(000, "===== BFD is not open!!!!\n");
907 * Reset the file attributes after all file I/O is done -- this allows
908 * the previous access time/dates to be set properly, and it also allows
909 * us to properly set directory permissions.
910 * Not currently Implemented.
912 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
914 Dmsg0(dbglvl, "plugin_set_attributes\n");
918 pm_strcpy(attr->ofname, "*none*");
923 * Print to file the plugin info.
925 void dump_fd_plugin(Plugin *plugin, FILE *fp)
930 pInfo *info = (pInfo *)plugin->pinfo;
931 fprintf(fp, "\tversion=%d\n", info->version);
932 fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
933 fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
934 fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
935 fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
936 fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
937 fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
941 * This entry point is called internally by Bacula to ensure
942 * that the plugin IO calls come into this code.
944 void load_fd_plugins(const char *plugin_dir)
949 Dmsg0(dbglvl, "plugin dir is NULL\n");
953 bplugin_list = New(alist(10, not_owned_by_alist));
954 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
955 is_plugin_compatible)) {
956 /* Either none found, or some error */
957 if (bplugin_list->size() == 0) {
960 Dmsg0(dbglvl, "No plugins loaded\n");
965 /* Plug entry points called from findlib */
966 plugin_bopen = my_plugin_bopen;
967 plugin_bclose = my_plugin_bclose;
968 plugin_bread = my_plugin_bread;
969 plugin_bwrite = my_plugin_bwrite;
970 plugin_blseek = my_plugin_blseek;
973 * Verify that the plugin is acceptable, and print information
976 foreach_alist(plugin, bplugin_list) {
977 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
978 Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
981 dbg_plugin_add_hook(dump_fd_plugin);
985 * Check if a plugin is compatible. Called by the load_plugin function
986 * to allow us to verify the plugin.
988 static bool is_plugin_compatible(Plugin *plugin)
990 pInfo *info = (pInfo *)plugin->pinfo;
991 Dmsg0(50, "is_plugin_compatible called\n");
992 if (debug_level >= 50) {
993 dump_fd_plugin(plugin, stdin);
995 if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
996 Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
997 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
998 Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
999 plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1003 if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1004 Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1005 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1006 Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1007 plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1010 if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1011 strcmp(info->plugin_license, "AGPLv3") != 0) {
1012 Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1013 plugin->file, info->plugin_license);
1014 Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1015 plugin->file, info->plugin_license);
1018 if (info->size != sizeof(pInfo)) {
1019 Jmsg(NULL, M_ERROR, 0,
1020 _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1021 plugin->file, sizeof(pInfo), info->size);
1030 * Create a new instance of each plugin for this Job
1031 * Note, bplugin_list can exist but jcr->plugin_ctx_list can
1032 * be NULL if no plugins were loaded.
1034 void new_plugins(JCR *jcr)
1039 if (!bplugin_list) {
1040 Dmsg0(dbglvl, "plugin list is NULL\n");
1043 if (jcr->is_job_canceled()) {
1047 int num = bplugin_list->size();
1050 Dmsg0(dbglvl, "No plugins loaded\n");
1054 jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1056 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1057 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1058 foreach_alist(plugin, bplugin_list) {
1059 /* Start a new instance of each plugin */
1060 bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1061 memset(b_ctx, 0, sizeof(bacula_ctx));
1063 plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */
1064 plugin_ctx_list[i].pContext = NULL;
1065 if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
1066 b_ctx->disabled = true;
1072 * Free the plugin instances for this Job
1074 void free_plugins(JCR *jcr)
1079 if (!bplugin_list || !jcr->plugin_ctx_list) {
1080 return; /* no plugins, nothing to do */
1083 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1084 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1085 foreach_alist(plugin, bplugin_list) {
1086 /* Free the plugin instance */
1087 plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1088 free(plugin_ctx_list[i++].bContext); /* free Bacula private context */
1090 free(plugin_ctx_list);
1091 jcr->plugin_ctx_list = NULL;
1094 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1096 JCR *jcr = bfd->jcr;
1097 Plugin *plugin = (Plugin *)jcr->plugin;
1100 Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1101 if (!plugin || !jcr->plugin_ctx) {
1104 io.pkt_size = sizeof(io);
1105 io.pkt_end = sizeof(io);
1114 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1115 bfd->berrno = io.io_errno;
1117 errno = b_errno_win32;
1119 errno = io.io_errno;
1120 bfd->lerror = io.lerror;
1122 Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1126 static int my_plugin_bclose(BFILE *bfd)
1128 JCR *jcr = bfd->jcr;
1129 Plugin *plugin = (Plugin *)jcr->plugin;
1132 Dmsg0(dbglvl, "===== plugin_bclose\n");
1133 if (!plugin || !jcr->plugin_ctx) {
1136 io.pkt_size = sizeof(io);
1137 io.pkt_end = sizeof(io);
1143 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1144 bfd->berrno = io.io_errno;
1146 errno = b_errno_win32;
1148 errno = io.io_errno;
1149 bfd->lerror = io.lerror;
1151 Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1155 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1157 JCR *jcr = bfd->jcr;
1158 Plugin *plugin = (Plugin *)jcr->plugin;
1161 Dmsg0(dbglvl, "plugin_bread\n");
1162 if (!plugin || !jcr->plugin_ctx) {
1165 io.pkt_size = sizeof(io);
1166 io.pkt_end = sizeof(io);
1169 io.buf = (char *)buf;
1173 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1174 bfd->offset = io.offset;
1175 bfd->berrno = io.io_errno;
1177 errno = b_errno_win32;
1179 errno = io.io_errno;
1180 bfd->lerror = io.lerror;
1182 return (ssize_t)io.status;
1185 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1187 JCR *jcr = bfd->jcr;
1188 Plugin *plugin = (Plugin *)jcr->plugin;
1191 Dmsg0(dbglvl, "plugin_bwrite\n");
1192 if (!plugin || !jcr->plugin_ctx) {
1195 io.pkt_size = sizeof(io);
1196 io.pkt_end = sizeof(io);
1199 io.buf = (char *)buf;
1202 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1203 bfd->berrno = io.io_errno;
1205 errno = b_errno_win32;
1207 errno = io.io_errno;
1208 bfd->lerror = io.lerror;
1210 return (ssize_t)io.status;
1213 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1215 JCR *jcr = bfd->jcr;
1216 Plugin *plugin = (Plugin *)jcr->plugin;
1219 Dmsg0(dbglvl, "plugin_bseek\n");
1220 if (!plugin || !jcr->plugin_ctx) {
1223 io.pkt_size = sizeof(io);
1224 io.pkt_end = sizeof(io);
1230 plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1231 bfd->berrno = io.io_errno;
1233 errno = b_errno_win32;
1235 errno = io.io_errno;
1236 bfd->lerror = io.lerror;
1238 return (boffset_t)io.offset;
1241 /* ==============================================================
1243 * Callbacks from the plugin
1245 * ==============================================================
1247 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1254 switch (var) { /* General variables, no need of ctx */
1256 *((char **)value) = my_name;
1258 case bVarWorkingDir:
1259 *(void **)value = me->working_directory;
1262 *(char **)value = exepath;
1265 *(char **)value = version;
1268 *(char **)value = dist_name;
1271 *((int *)value) = beef;
1277 if (!ctx) { /* Other variables need context */
1281 jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1288 *((int *)value) = jcr->JobId;
1289 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1292 *((int *)value) = jcr->getJobLevel();
1293 Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1296 *((int *)value) = jcr->getJobType();
1297 Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1300 *((char **)value) = jcr->client_name;
1301 Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1304 *((char **)value) = jcr->Job;
1305 Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
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 */