X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Ffiled%2Ffd_plugins.c;h=b54adc9810f2462321688276513029b89764345d;hb=20787d8026640d85591e521b5e373ead1c3af0ce;hp=67943bbf722c04f4148df43c817e1690a4a0e77e;hpb=37c74f65b3da63f13cf7730ce5a9f03831215a2f;p=bacula%2Fbacula diff --git a/bacula/src/filed/fd_plugins.c b/bacula/src/filed/fd_plugins.c index 67943bbf72..b54adc9810 100644 --- a/bacula/src/filed/fd_plugins.c +++ b/bacula/src/filed/fd_plugins.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2007-2009 Free Software Foundation Europe e.V. + Copyright (C) 2007-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation, which is listed in the file LICENSE. @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -25,7 +25,7 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ -/* +/** * Main program to test loading and running Bacula plugins. * Destined to become Bacula pluginloader, ... * @@ -34,6 +34,9 @@ #include "bacula.h" #include "filed.h" +extern CLIENT *me; +extern DLL_IMP_EXP char *exepath; + const int dbglvl = 150; #ifdef HAVE_WIN32 const char *plugin_type = "-fd.dll"; @@ -42,6 +45,7 @@ const char *plugin_type = "-fd.so"; #endif extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level); +extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt); /* Function pointers to be set here */ extern DLL_IMP_EXP int (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode); @@ -62,7 +66,16 @@ static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line, static void *baculaMalloc(bpContext *ctx, const char *file, int line, size_t size); static void baculaFree(bpContext *ctx, const char *file, int line, void *mem); +static bRC baculaAddExclude(bpContext *ctx, const char *file); +static bRC baculaAddInclude(bpContext *ctx, const char *file); +static bRC baculaAddOptions(bpContext *ctx, const char *opts); +static bRC baculaAddRegex(bpContext *ctx, const char *item, int type); +static bRC baculaAddWild(bpContext *ctx, const char *item, int type); +static bRC baculaNewOptions(bpContext *ctx); +static bRC baculaNewInclude(bpContext *ctx); static bool is_plugin_compatible(Plugin *plugin); +static bool get_plugin_name(JCR *jcr, char *cmd, int *ret); +static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp); /* * These will be plugged into the global pointer structure for @@ -74,6 +87,7 @@ static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count); static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count); static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence); +#define for_this_plug(plugin, str, len) (((len) == (plugin)->file_len) && strncmp((plugin)->file, str, len) == 0) /* Bacula info */ static bInfo binfo = { @@ -91,7 +105,15 @@ static bFuncs bfuncs = { baculaJobMsg, baculaDebugMsg, baculaMalloc, - baculaFree + baculaFree, + baculaAddExclude, + baculaAddInclude, + baculaAddOptions, + baculaAddRegex, + baculaAddWild, + baculaNewOptions, + baculaNewInclude, + baculaCheckChanges }; /* @@ -101,57 +123,108 @@ struct bacula_ctx { JCR *jcr; /* jcr for plugin */ bRC rc; /* last return code */ bool disabled; /* set if plugin disabled */ + findINCEXE *exclude; /* pointer to exclude files */ + findINCEXE *include; /* pointer to include/exclude files */ }; -static bool is_plugin_disabled(JCR *jcr) +static bool is_plugin_disabled(bpContext *plugin_ctx) { bacula_ctx *b_ctx; - if (!jcr->plugin_ctx) { + if (!plugin_ctx) { return true; } - b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext; + b_ctx = (bacula_ctx *)plugin_ctx->bContext; return b_ctx->disabled; } +static bool is_plugin_disabled(JCR *jcr) +{ + return is_plugin_disabled(jcr->plugin_ctx); +} -/* +/** * Create a plugin event + * When receiving bEventCancelCommand, this function is called by an other thread. */ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { + bpContext *plugin_ctx; bEvent event; Plugin *plugin; int i = 0; + char *name = NULL; + int len = 0; + bool call_if_canceled = false; + bRC rc; if (!plugin_list || !jcr || !jcr->plugin_ctx_list) { return; /* Return if no plugins loaded */ } + + /* + * Some events are sent to only a particular plugin or must be + * called even if the job is canceled + */ + switch(eventType) { + case bEventPluginCommand: + name = (char *)value; + if (!get_plugin_name(jcr, name, &len)) { + return; + } + break; + case bEventEndBackupJob: + case bEventEndVerifyJob: + call_if_canceled = true; + break; + case bEventStartRestoreJob: + if (jcr->plugin) { + jcr->plugin->restoreFileStarted = false; + jcr->plugin->createFileCalled = false; + } + break; + case bEventEndRestoreJob: + call_if_canceled = true; + if (jcr->plugin && jcr->plugin->restoreFileStarted) { + plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx); + jcr->plugin->restoreFileStarted = false; + jcr->plugin->createFileCalled = false; + } + break; + default: + break; + } + + if (!call_if_canceled && jcr->is_job_canceled()) { + return; + } bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list; event.eventType = eventType; Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId); - /* Pass event to every plugin */ + /* + * Pass event to every plugin (except if name is set). If name + * is set, we pass it only to the plugin with that name. + */ foreach_alist(plugin, plugin_list) { - bRC rc; - jcr->plugin_ctx = &plugin_ctx_list[i++]; - jcr->plugin = plugin; - if (is_plugin_disabled(jcr)) { + if (name && !for_this_plug(plugin, name, len)) { + i++; + continue; + } + plugin_ctx = &plugin_ctx_list[i++]; + if (is_plugin_disabled(plugin_ctx)) { continue; } - rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value); + rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value); if (rc != bRC_OK) { break; } } - - jcr->plugin = NULL; - jcr->plugin_ctx = NULL; return; } -/* +/** * Check if file was seen for accurate */ bool plugin_check_file(JCR *jcr, char *fname) @@ -160,7 +233,7 @@ bool plugin_check_file(JCR *jcr, char *fname) int rc = bRC_OK; int i = 0; - if (!plugin_list || !jcr || !jcr->plugin_ctx_list) { + if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) { return false; /* Return if no plugins loaded */ } @@ -189,8 +262,42 @@ bool plugin_check_file(JCR *jcr, char *fname) return rc == bRC_Seen; } +/* Get the first part of the the plugin command + * systemstate:/@SYSTEMSTATE/ + * => ret = 11 + * => can use for_this_plug(plug, cmd, ret); + * + * The plugin command can contain only the plugin name + * Plugin = alldrives + * => ret = 9 + */ +static bool get_plugin_name(JCR *jcr, char *cmd, int *ret) +{ + char *p; + int len; + if (!cmd || (*cmd == '\0')) { + return false; + } + /* Handle plugin command here backup */ + Dmsg1(dbglvl, "plugin cmd=%s\n", cmd); + if ((p = strchr(cmd, ':')) == NULL) { + if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */ + len = strlen(cmd); + } else { + Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd); + return false; + } + } else { /* plugin:argument */ + len = p - cmd; + if (len <= 0) { + return false; + } + } + *ret = len; + return true; +} -/* +/** * Sequence of calls for a backup: * 1. plugin_save() here is called with ff_pkt * 2. we find the plugin requested on the command string @@ -210,12 +317,14 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) Plugin *plugin; int i = 0; int len; - char *p; char *cmd = ff_pkt->top_fname; struct save_pkt sp; bEvent event; + POOL_MEM fname(PM_FNAME); + POOL_MEM link(PM_FNAME); - if (!plugin_list || !jcr->plugin_ctx_list) { + if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) { + Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd); return 1; /* Return if no plugins loaded */ } @@ -223,21 +332,14 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list; event.eventType = bEventBackupCommand; - /* Handle plugin command here backup */ - Dmsg1(dbglvl, "plugin cmd=%s\n", cmd); - if (!(p = strchr(cmd, ':'))) { - Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd); - goto bail_out; - } - len = p - cmd; - if (len <= 0) { + if (!get_plugin_name(jcr, cmd, &len)) { goto bail_out; } /* Note, we stop the loop on the first plugin that matches the name */ foreach_alist(plugin, plugin_list) { - Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len); - if (strncmp(plugin->file, cmd, len) != 0) { + Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len); + if (!for_this_plug(plugin, cmd, len)) { i++; continue; } @@ -258,40 +360,89 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) goto bail_out; } /* Loop getting filenames to backup then saving them */ - while (!job_canceled(jcr)) { + while (!jcr->is_job_canceled()) { memset(&sp, 0, sizeof(sp)); sp.pkt_size = sizeof(sp); sp.pkt_end = sizeof(sp); sp.portable = true; + sp.flags = 0; sp.cmd = cmd; Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks, &sp); - /* Get the file save parameters */ + /* Get the file save parameters. I.e. the stat pkt ... */ if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) { goto bail_out; } - if (sp.type == 0 || sp.fname == NULL) { - Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"), + if (sp.type == 0) { + Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"), cmd); goto bail_out; } jcr->plugin_sp = &sp; ff_pkt = jcr->ff; - ff_pkt->fname = sp.fname; - ff_pkt->link = sp.link; + /* + * Copy fname and link because save_file() zaps them. This + * avoids zaping the plugin's strings. + */ ff_pkt->type = sp.type; + if (sp.type == FT_RESTORE_FIRST) { + if (!sp.object_name) { + Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"), + cmd); + goto bail_out; + } + ff_pkt->fname = cmd; /* full plugin string */ + ff_pkt->object_name = sp.object_name; + ff_pkt->object_index = sp.index; /* restore object index */ + ff_pkt->object_compression = 0; /* no compression for now */ + ff_pkt->object = sp.object; + ff_pkt->object_len = sp.object_len; + } else { + if (!sp.fname) { + Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"), + cmd); + goto bail_out; + } + pm_strcpy(fname, sp.fname); + pm_strcpy(link, sp.link); + ff_pkt->fname = fname.c_str(); + ff_pkt->link = link.c_str(); + ff_pkt->delta_seq = sp.delta_seq; + if (sp.flags & FO_DELTA) { + ff_pkt->flags |= FO_DELTA; + ff_pkt->delta_seq++; /* make new delta sequence number */ + } else { + ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */ + ff_pkt->delta_seq = 0; + } + if (sp.flags & FO_OFFSETS) { + ff_pkt->flags |= FO_OFFSETS; + } + if (sp.flags & FO_PORTABLE_DATA) { + ff_pkt->flags |= FO_PORTABLE_DATA; + } + ff_pkt->flags |= FO_PLUGIN; /* data from plugin */ + } + memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp)); - Dmsg1(dbglvl, "Save_file: file=%s\n", ff_pkt->fname); + Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname); + if (sp.object) { + Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object); + } + /* Call Bacula core code to backup the plugin's file */ save_file(jcr, ff_pkt, true); bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx); + if (rc == bRC_More || rc == bRC_OK) { + accurate_mark_file_as_seen(jcr, fname.c_str()); + } if (rc == bRC_More) { continue; } goto bail_out; - } + } /* end while loop */ goto bail_out; - } - Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd); + } /* end loop over all plugins */ + Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd); bail_out: jcr->cmd_plugin = false; @@ -300,7 +451,7 @@ bail_out: return 1; } -/* +/** * Send plugin name start/end record to SD */ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start) @@ -313,6 +464,9 @@ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start) Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n")); return false; } + if (jcr->is_job_canceled()) { + return false; + } if (start) { index++; /* JobFiles not incremented yet */ @@ -324,26 +478,26 @@ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start) sd->bstrerror()); return false; } - Dmsg1(50, "send: %s\n", sd->msg); + Dmsg1(50, "send plugin name hdr: %s\n", sd->msg); if (start) { /* Send data -- not much */ stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0); } else { /* Send end of data */ - stat = sd->fsend("0 0"); + stat = sd->fsend("%ld 0", jcr->JobFiles); } if (!stat) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); return false; } - Dmsg1(dbglvl, "send: %s\n", sd->msg); + Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg); sd->signal(BNET_EOD); /* indicate end of plugin name data */ return true; } -/* +/** * Plugin name stream found during restore. The record passed in * argument name was generated in send_plugin_name() above. * @@ -377,8 +531,10 @@ bool plugin_name_stream(JCR *jcr, char *name) * End of plugin data, notify plugin, then clear flags */ Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx); - if (jcr->plugin) { + if (jcr->plugin && jcr->plugin->restoreFileStarted) { plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx); + jcr->plugin->restoreFileStarted = false; + jcr->plugin->createFileCalled = false; } jcr->plugin_ctx = NULL; jcr->plugin = NULL; @@ -391,15 +547,7 @@ bool plugin_name_stream(JCR *jcr, char *name) /* * After this point, we are dealing with a restore start */ - -// Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd); - if (!(p = strchr(cmd, ':'))) { - Jmsg1(jcr, M_ERROR, 0, - _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd); - goto bail_out; - } - len = p - cmd; - if (len <= 0) { + if (!get_plugin_name(jcr, cmd, &len)) { goto bail_out; } @@ -409,7 +557,7 @@ bool plugin_name_stream(JCR *jcr, char *name) foreach_alist(plugin, plugin_list) { bEvent event; Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len); - if (strncmp(plugin->file, cmd, len) != 0) { + if (!for_this_plug(plugin, cmd, len)) { i++; continue; } @@ -424,8 +572,13 @@ bool plugin_name_stream(JCR *jcr, char *name) &event, cmd) != bRC_OK) { goto bail_out; } - /* ***FIXME**** check error code */ - plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd); + if (plugin->restoreFileStarted) + { + Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile\n"); + goto bail_out; + } + plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd); + plugin->restoreFileStarted = true; goto bail_out; } Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd); @@ -434,7 +587,7 @@ bail_out: return start; } -/* +/** * Tell the plugin to create the file. Return values are * This is called only during Restore * @@ -452,11 +605,13 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) int flags; int rc; - if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr)) { + if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) { return CF_ERROR; } + rp.pkt_size = sizeof(rp); rp.pkt_end = sizeof(rp); + rp.delta_seq = attr->delta_seq; rp.stream = attr->stream; rp.data_stream = attr->data_stream; rp.type = attr->type; @@ -471,13 +626,25 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) rp.RegexWhere = jcr->RegexWhere; rp.replace = jcr->replace; rp.create_status = CF_ERROR; - Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname); + Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", + rp.stream, rp.type, rp.LinkFI, rp.ofname); + if (rp.attrEx) { + Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx); + } + if (!plugin->restoreFileStarted || plugin->createFileCalled) + { + Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n"); + return CF_ERROR; + } rc = plug_func(plugin)->createFile(plugin_ctx, &rp); if (rc != bRC_OK) { Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"), rc, attr->ofname); return CF_ERROR; } + if (rp.create_status == CF_SKIP) { + return CF_SKIP; + } if (rp.create_status == CF_ERROR) { Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"), attr->ofname); @@ -507,7 +674,7 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) return CF_EXTRACT; } -/* +/** * Reset the file attributes after all file I/O is done -- this allows * the previous access time/dates to be set properly, and it also allows * us to properly set directory permissions. @@ -541,7 +708,7 @@ void dump_fd_plugin(Plugin *plugin, FILE *fp) fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description)); } -/* +/** * This entry point is called internally by Bacula to ensure * that the plugin IO calls come into this code. */ @@ -585,7 +752,7 @@ void load_fd_plugins(const char *plugin_dir) dbg_plugin_add_hook(dump_fd_plugin); } -/* +/** * Check if a plugin is compatible. Called by the load_plugin function * to allow us to verify the plugin. */ @@ -611,8 +778,8 @@ static bool is_plugin_compatible(Plugin *plugin) plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version); return false; } - if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 && - strcmp(info->plugin_license, "GPLv2") != 0) { + if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 && + strcmp(info->plugin_license, "AGPLv3") != 0) { Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"), plugin->file, info->plugin_license); Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n", @@ -624,7 +791,7 @@ static bool is_plugin_compatible(Plugin *plugin) } -/* +/** * Create a new instance of each plugin for this Job * Note, plugin_list can exist but jcr->plugin_ctx_list can * be NULL if no plugins were loaded. @@ -638,6 +805,9 @@ void new_plugins(JCR *jcr) Dmsg0(dbglvl, "plugin list is NULL\n"); return; } + if (jcr->is_job_canceled() || jcr->JobId == 0) { + return; + } int num = plugin_list->size(); @@ -663,7 +833,7 @@ void new_plugins(JCR *jcr) } } -/* +/** * Free the plugin instances for this Job */ void free_plugins(JCR *jcr) @@ -693,6 +863,9 @@ static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode struct io_pkt io; Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags); + if (!plugin || !jcr->plugin_ctx) { + return 0; + } io.pkt_size = sizeof(io); io.pkt_end = sizeof(io); io.func = IO_OPEN; @@ -720,7 +893,11 @@ static int my_plugin_bclose(BFILE *bfd) JCR *jcr = bfd->jcr; Plugin *plugin = (Plugin *)jcr->plugin; struct io_pkt io; + Dmsg0(dbglvl, "===== plugin_bclose\n"); + if (!plugin || !jcr->plugin_ctx) { + return 0; + } io.pkt_size = sizeof(io); io.pkt_end = sizeof(io); io.func = IO_CLOSE; @@ -745,15 +922,21 @@ static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count) JCR *jcr = bfd->jcr; Plugin *plugin = (Plugin *)jcr->plugin; struct io_pkt io; + Dmsg0(dbglvl, "plugin_bread\n"); + if (!plugin || !jcr->plugin_ctx) { + return 0; + } io.pkt_size = sizeof(io); io.pkt_end = sizeof(io); io.func = IO_READ; io.count = count; io.buf = (char *)buf; io.win32 = false; + io.offset = 0; io.lerror = 0; plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io); + bfd->offset = io.offset; bfd->berrno = io.io_errno; if (io.win32) { errno = b_errno_win32; @@ -769,7 +952,11 @@ static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count) JCR *jcr = bfd->jcr; Plugin *plugin = (Plugin *)jcr->plugin; struct io_pkt io; + Dmsg0(dbglvl, "plugin_bwrite\n"); + if (!plugin || !jcr->plugin_ctx) { + return 0; + } io.pkt_size = sizeof(io); io.pkt_end = sizeof(io); io.func = IO_WRITE; @@ -793,7 +980,11 @@ static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence) JCR *jcr = bfd->jcr; Plugin *plugin = (Plugin *)jcr->plugin; struct io_pkt io; + Dmsg0(dbglvl, "plugin_bseek\n"); + if (!plugin || !jcr->plugin_ctx) { + return 0; + } io.pkt_size = sizeof(io); io.pkt_end = sizeof(io); io.func = IO_SEEK; @@ -821,32 +1012,45 @@ static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence) static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value) { JCR *jcr; - if (!value || !ctx) { + if (!value) { return bRC_Error; } - jcr = ((bacula_ctx *)ctx->bContext)->jcr; -// Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var); + + switch (var) { /* General variables, no need of ctx */ + case bVarFDName: + *((char **)value) = my_name; + break; + case bVarWorkingDir: + *(void **)value = me->working_directory; + break; + case bVarExePath: + *(char **)value = exepath; + break; + default: + break; + } + + if (!ctx) { /* Other variables need context */ + return bRC_Error; + } + jcr = ((bacula_ctx *)ctx->bContext)->jcr; if (!jcr) { return bRC_Error; } -// Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); + switch (var) { case bVarJobId: *((int *)value) = jcr->JobId; Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId); break; - case bVarFDName: - *((char **)value) = my_name; - Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name); - break; case bVarLevel: - *((int *)value) = jcr->get_JobLevel(); - Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->get_JobLevel()); + *((int *)value) = jcr->getJobLevel(); + Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel()); break; case bVarType: - *((int *)value) = jcr->get_JobType(); - Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->get_JobType()); + *((int *)value) = jcr->getJobType(); + Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType()); break; case bVarClient: *((char **)value) = jcr->client_name; @@ -870,6 +1074,33 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value) break; case bVarFileSeen: break; /* a write only variable, ignore read request */ + case bVarVssObject: +#ifdef HAVE_WIN32 + if (g_pVSSClient) { + *(void **)value = g_pVSSClient->GetVssObject(); + break; + } +#endif + return bRC_Error; + case bVarVssDllHandle: +#ifdef HAVE_WIN32 + if (g_pVSSClient) { + *(void **)value = g_pVSSClient->GetVssDllHandle(); + break; + } +#endif + return bRC_Error; + case bVarWhere: + *(char **)value = jcr->where; + break; + case bVarRegexWhere: + *(char **)value = jcr->RegexWhere; + break; + + case bVarFDName: /* get warning with g++ if we missed one */ + case bVarWorkingDir: + case bVarExePath: + break; } return bRC_OK; } @@ -880,7 +1111,6 @@ static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value) if (!value || !ctx) { return bRC_Error; } - jcr = ((bacula_ctx *)ctx->bContext)->jcr; // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var); jcr = ((bacula_ctx *)ctx->bContext)->jcr; if (!jcr) { @@ -968,6 +1198,185 @@ static void baculaFree(bpContext *ctx, const char *file, int line, void *mem) #endif } +static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx) +{ + if (!ctx) { + return false; + } + bctx = (bacula_ctx *)ctx->bContext; + if (!bctx) { + return false; + } + jcr = bctx->jcr; + if (!jcr) { + return false; + } + return true; +} + +/** + * Let the plugin define files/directories to be excluded + * from the main backup. + */ +static bRC baculaAddExclude(bpContext *ctx, const char *file) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + if (!file) { + return bRC_Error; + } + if (!bctx->exclude) { + bctx->exclude = new_exclude(jcr); + new_options(jcr, bctx->exclude); + } + set_incexe(jcr, bctx->exclude); + add_file_to_fileset(jcr, file, true); + Dmsg1(100, "Add exclude file=%s\n", file); + return bRC_OK; +} + +/** + * Let the plugin define files/directories to be excluded + * from the main backup. + */ +static bRC baculaAddInclude(bpContext *ctx, const char *file) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + if (!file) { + return bRC_Error; + } + if (!bctx->include) { + bctx->include = new_preinclude(jcr); + new_options(jcr, bctx->include); + } + set_incexe(jcr, bctx->include); + add_file_to_fileset(jcr, file, true); + Dmsg1(100, "Add include file=%s\n", file); + return bRC_OK; +} + +static bRC baculaAddOptions(bpContext *ctx, const char *opts) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + if (!opts) { + return bRC_Error; + } + add_options_to_fileset(jcr, opts); + Dmsg1(1000, "Add options=%s\n", opts); + return bRC_OK; +} + +static bRC baculaAddRegex(bpContext *ctx, const char *item, int type) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + if (!item) { + return bRC_Error; + } + add_regex_to_fileset(jcr, item, type); + Dmsg1(100, "Add regex=%s\n", item); + return bRC_OK; +} + +static bRC baculaAddWild(bpContext *ctx, const char *item, int type) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + if (!item) { + return bRC_Error; + } + add_wild_to_fileset(jcr, item, type); + Dmsg1(100, "Add wild=%s\n", item); + return bRC_OK; +} + +static bRC baculaNewOptions(bpContext *ctx) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + (void)new_options(jcr, NULL); + return bRC_OK; +} + +static bRC baculaNewInclude(bpContext *ctx) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + (void)new_include(jcr); + return bRC_OK; +} + + +/* + * Check if a file have to be backuped using Accurate code + */ +static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp) +{ + JCR *jcr; + bacula_ctx *bctx; + FF_PKT *ff_pkt; + bRC ret = bRC_Error; + + if (!is_ctx_good(ctx, jcr, bctx)) { + goto bail_out; + } + if (!sp) { + goto bail_out; + } + + ff_pkt = jcr->ff; + /* + * Copy fname and link because save_file() zaps them. This + * avoids zaping the plugin's strings. + */ + ff_pkt->type = sp->type; + if (!sp->fname) { + Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n")); + goto bail_out; + } + + ff_pkt->fname = sp->fname; + ff_pkt->link = sp->link; + memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp)); + + if (check_changes(jcr, ff_pkt)) { + ret = bRC_OK; + } else { + ret = bRC_Seen; + } + + /* check_changes() can update delta sequence number, return it to the + * plugin + */ + sp->delta_seq = ff_pkt->delta_seq; + +bail_out: + Dmsg1(100, "checkChanges=%i\n", ret); + return ret; +} #ifdef TEST_PROGRAM @@ -1017,7 +1426,7 @@ int main(int argc, char *argv[]) Dmsg0(dbglvl, "bacula: OK ...\n"); close_memory_pool(); - sm_dump(false); + sm_dump(false); /* unit test */ return 0; }