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.
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.
#include "filed.h"
extern CLIENT *me;
+extern DLL_IMP_EXP char *exepath;
const int dbglvl = 150;
#ifdef HAVE_WIN32
#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);
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
baculaAddRegex,
baculaAddWild,
baculaNewOptions,
- baculaNewInclude
+ baculaNewInclude,
+ baculaCheckChanges
};
/*
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;
+ char *name = NULL;
+ int len = 0;
+ bool call_if_canceled = false;
bRC rc;
- if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
+ if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
return; /* Return if no plugins loaded */
}
- /* Some events are sent to only a particular plugin */
+ /*
+ * 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;
return;
}
break;
+ case bEventEndBackupJob:
+ case bEventEndVerifyJob:
+ call_if_canceled = true;
+ 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;
+ }
+ break;
default:
break;
}
+ if (!call_if_canceled && jcr->is_job_canceled()) {
+ return;
+ }
+
bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
event.eventType = eventType;
i++;
continue;
}
- jcr->plugin_ctx = &plugin_ctx_list[i++];
- jcr->plugin = plugin;
- if (is_plugin_disabled(jcr)) {
+ 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;
}
return rc == bRC_Seen;
}
+/* Get the first part of the the plugin command
+ * systemstate:/@SYSTEMSTATE/
+ * => ret = 11
+ * => can use strncmp(plugin_name, 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) {
+ if (!cmd || (*cmd == '\0')) {
return false;
}
/* 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);
- return false;
- }
- len = p - cmd;
- if (len <= 0) {
- return false;
+ 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;
POOL_MEM link(PM_FNAME);
if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
- Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
+ Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
return 1; /* Return if no plugins loaded */
}
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);
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;
+ }
}
memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
continue;
}
goto bail_out;
- }
+ } /* end while loop */
goto bail_out;
- }
+ } /* end loop over all plugins */
Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
bail_out:
* 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_ctx = NULL;
jcr->plugin = NULL;
/*
* 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;
}
goto bail_out;
}
/* ***FIXME**** check error code */
+ if (plugin->restoreFileStarted) {
+ plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
+ }
plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
+ plugin->restoreFileStarted = true;
goto bail_out;
}
Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
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);
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",
static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
{
JCR *jcr;
- if (!value || !ctx) {
+ if (!value) {
return bRC_Error;
}
-// 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->getJobLevel();
Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
}
#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:
- *(void **)value = me->working_directory;
+ case bVarExePath:
break;
}
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
int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
Dmsg0(dbglvl, "bacula: OK ...\n");
close_memory_pool();
- sm_dump(false);
+ sm_dump(false); /* unit test */
return 0;
}