From: Eric Bollengier Date: Thu, 7 Oct 2021 16:35:13 +0000 (+0200) Subject: Add new Verify Data plugin framework X-Git-Tag: Beta-15.0.0~855 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5371c3c2f4147e5dc4dcdb4a75c306323c12c604;p=thirdparty%2Fbacula.git Add new Verify Data plugin framework --- diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index d59a9a580..90e447121 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -1594,13 +1594,13 @@ void dird_free_jcr(JCR *jcr) delete jcr->store_mngr; - if (jcr->JobId != 0) + if (jcr->JobId != 0) { write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs)); - + } + bfree_and_null(jcr->plugin_options); if (jcr->plugin_config) { free_plugin_config_items(jcr->plugin_config); - delete jcr->plugin_config; - jcr->plugin_config = NULL; + bdelete_and_null(jcr->plugin_config); } free_plugins(jcr); /* release instantiated plugins */ diff --git a/bacula/src/dird/ua_run.c b/bacula/src/dird/ua_run.c index 8f6fa9bc4..c6d3c4132 100644 --- a/bacula/src/dird/ua_run.c +++ b/bacula/src/dird/ua_run.c @@ -1223,7 +1223,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) add_prompt(ua, _("Replace")); /* 10 */ add_prompt(ua, _("JobId")); /* 11 */ } - if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE) { + if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE || jcr->is_JobType(JT_VERIFY)) { add_prompt(ua, _("Plugin Options")); /* 12 */ } switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) { @@ -1558,9 +1558,7 @@ static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc) } if (rc.plugin_options) { - if (jcr->plugin_options) { - free(jcr->plugin_options); - } + bfree_and_null(jcr->plugin_options); jcr->plugin_options = bstrdup(rc.plugin_options); rc.plugin_options = NULL; } @@ -2017,16 +2015,17 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char ua->signal(BNET_RUN_CMD); ua->send_msg("Type: Verify\n" "Title: Run Verify Job\n" - "JobName: %s\n" - "Level: %s\n" - "Client: %s\n" - "FileSet: %s\n" - "Pool: %s (From %s)\n" - "Storage: %s (From %s)\n" - "Verify Job: %s\n" - "Verify List: %s\n" - "When: %s\n" - "Priority: %d\n", + "JobName: %s\n" + "Level: %s\n" + "Client: %s\n" + "FileSet: %s\n" + "Pool: %s (From %s)\n" + "Storage: %s (From %s)\n" + "Verify Job: %s\n" + "Verify List: %s\n" + "When: %s\n" + "Priority: %d\n" + "Plugin Options: %s\n", job->name(), level_to_str(edl, sizeof(edl), jcr->getJobLevel()), jcr->client->name(), @@ -2036,19 +2035,21 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char Name, verify_list, bstrutime(dt, sizeof(dt), jcr->sched_time), - jcr->JobPriority); + jcr->JobPriority, + NPRT(jcr->plugin_options)); } else { ua->send_msg(_("Run Verify Job\n" - "JobName: %s\n" - "Level: %s\n" - "Client: %s\n" - "FileSet: %s\n" - "Pool: %s (From %s)\n" - "Storage: %s (From %s)\n" - "Verify Job: %s\n" - "Verify List: %s\n" - "When: %s\n" - "Priority: %d\n"), + "JobName: %s\n" + "Level: %s\n" + "Client: %s\n" + "FileSet: %s\n" + "Pool: %s (From %s)\n" + "Storage: %s (From %s)\n" + "Verify Job: %s\n" + "Verify List: %s\n" + "When: %s\n" + "Priority: %d\n" + "Plugin Options: %s\n"), job->name(), level_to_str(edl, sizeof(edl), jcr->getJobLevel()), jcr->client->name(), @@ -2058,7 +2059,8 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char Name, verify_list, bstrutime(dt, sizeof(dt), jcr->sched_time), - jcr->JobPriority); + jcr->JobPriority, + NPRT(jcr->plugin_options)); } } break; @@ -2559,15 +2561,13 @@ static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc) kw_ok = true; break; case 25: /* pluginoptions */ - ua->send_msg(_("Plugin Options not yet implemented.\n")); - return false; if (rc.plugin_options) { ua->send_msg(_("Plugin Options specified twice.\n")); return false; } rc.plugin_options = ua->argv[i]; if (!acl_access_ok(ua, PluginOptions_ACL, rc.plugin_options)) { - ua->send_msg(_("No authoriztion for \"PluginOptions\" specification.\n")); + ua->send_msg(_("No authorization for \"PluginOptions\" specification.\n")); return false; } kw_ok = true; @@ -2734,6 +2734,10 @@ static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc) rc.verify_job = rc.job->verify_job; } + if (rc.job->JobType == JT_VERIFY && rc.job->PluginOptions) { + rc.plugin_options = rc.job->PluginOptions; + } + if (rc.previous_job_name) { rc.previous_job = GetJobResWithName(rc.previous_job_name); if (!rc.previous_job) { diff --git a/bacula/src/dird/verify.c b/bacula/src/dird/verify.c index cc26c46e8..eb7f54003 100644 --- a/bacula/src/dird/verify.c +++ b/bacula/src/dird/verify.c @@ -41,6 +41,7 @@ static char verifycmd[] = "verify level=%s\n"; /* Responses received from File daemon */ static char OKverify[] = "2000 OK verify\n"; +static char OKPluginOptions[] = "2000 OK plugin options\n"; /* Commands received from Storage daemon */ static char OKbootstrap[] = "3000 OK bootstrap\n"; @@ -369,6 +370,18 @@ bool do_verify(JCR *jcr) goto bail_out; } + if (jcr->plugin_options) { + if (jcr->FDVersion < 15) { + Jmsg1(jcr, M_FATAL, 0, _("Unable to send PluginOptions to FD. Please upgrade the FD from %d to 15.\n"), jcr->FDVersion); + goto bail_out; + } + bash_spaces(jcr->plugin_options); + fd->fsend("pluginoptions=%s\n", jcr->plugin_options); + if (!response(jcr, fd, BSOCK_TYPE_FD, OKPluginOptions, "Verify", DISPLAY_ERROR)) { + goto bail_out; + } + } + /* * Send verify command/level to File daemon */ diff --git a/bacula/src/filed/fd_plugins.c b/bacula/src/filed/fd_plugins.c index 2878e017d..a092e7dc9 100644 --- a/bacula/src/filed/fd_plugins.c +++ b/bacula/src/filed/fd_plugins.c @@ -75,6 +75,7 @@ static bool get_plugin_name(JCR *jcr, char *cmd, int *ret); static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp); static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp); static bRC baculaAccurateAttribs(bpContext *ctx, accurate_attribs_pkt *att); +static void plugin_register_verify_data(bpContext *ctx); /* * These will be plugged into the global pointer structure for @@ -126,9 +127,11 @@ struct bacula_ctx { bool disabled; /* set if plugin disabled */ bool restoreFileStarted; bool createFileCalled; + bool verifyFileCalled; /* true if startVerifyFile() has been called */ bool cancelCalled; /* true if the plugin got the cancel event */ findINCEXE *exclude; /* pointer to exclude files */ findINCEXE *include; /* pointer to include/exclude files */ + Plugin *plugin; /* pointer to the plugin itself */ }; static bacula_ctx *get_bacula_ctx(bpContext *plugin_ctx) @@ -231,6 +234,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) case bEventEndVerifyJob: call_if_canceled = true; /* plugin *must* see this call */ break; + case bEventStartVerifyJob: case bEventStartRestoreJob: break; case bEventEndRestoreJob: @@ -647,7 +651,10 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) if (is_plugin_disabled(jcr)) { goto bail_out; } - + if (!plug_func(plugin)->startBackupFile || !plug_func(plugin)->endBackupFile) { + Dmsg1(dbglvl, "Plugin not suitable for Backup job %s\n", cmd); + goto bail_out; + } Dmsg1(dbglvl, "Command plugin = %s\n", cmd); /* Send the backup command to the right plugin*/ if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) { @@ -813,7 +820,10 @@ int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level) if (is_plugin_disabled(jcr)) { goto bail_out; } - + if (!plug_func(plugin)->startBackupFile || !plug_func(plugin)->endBackupFile) { + Dmsg1(dbglvl, "Plugin not suitable for Estimate job %s\n", cmd); + goto bail_out; + } Dmsg1(dbglvl, "Command plugin = %s\n", cmd); /* Send the backup command to the right plugin*/ if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) { @@ -1038,10 +1048,14 @@ bool plugin_name_stream(JCR *jcr, char *name) Dmsg1(dbglvl, "Plugin %s disabled\n", cmd); goto bail_out; } + if (!plug_func(plugin)->startRestoreFile || !plug_func(plugin)->endRestoreFile) { + Dmsg1(dbglvl, "Plugin not suitable for Restore job %s\n", cmd); + goto bail_out; + } Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd); event.eventType = bEventRestoreCommand; if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, - &event, cmd) != bRC_OK) { + &event, cmd) != bRC_OK) { Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd); goto bail_out; } @@ -1071,6 +1085,31 @@ bail_out: return start; } +static void fill_restore_pkt(JCR *jcr, ATTR *attr, restore_pkt *rp) +{ + rp->pkt_size = sizeof(restore_pkt); + rp->pkt_end = sizeof(restore_pkt); + rp->delta_seq = attr->delta_seq; + rp->stream = attr->stream; + rp->data_stream = attr->data_stream; + rp->type = attr->type; + rp->file_index = attr->file_index; + rp->LinkFI = attr->LinkFI; + rp->uid = attr->uid; + rp->statp = attr->statp; /* structure assignment */ + rp->attrEx = attr->attrEx; + if (jcr->is_JobType(JT_VERIFY)) { + rp->ofname = attr->fname; + } else { + rp->ofname = attr->ofname; + rp->olname = attr->olname; + } + rp->where = jcr->where; + rp->RegexWhere = jcr->RegexWhere; + rp->replace = jcr->replace; + rp->create_status = CF_ERROR; +} + /** * Tell the plugin to create the file. Return values are * This is called only during Restore @@ -1094,24 +1133,13 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) if (!plugin || !plugin_ctx || jcr->is_job_canceled()) { return CF_ERROR; } + if (!plug_func(plugin)->createFile) { + Dmsg0(dbglvl, "Plugin not suitable for Restore job\n"); + return CF_ERROR; + } + + fill_restore_pkt(jcr, attr, &rp); - 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; - rp.file_index = attr->file_index; - rp.LinkFI = attr->LinkFI; - rp.uid = attr->uid; - rp.statp = attr->statp; /* structure assignment */ - rp.attrEx = attr->attrEx; - rp.ofname = attr->ofname; - rp.olname = attr->olname; - rp.where = jcr->where; - rp.RegexWhere = jcr->RegexWhere; - rp.replace = jcr->replace; - rp.create_status = CF_ERROR; 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) { @@ -1718,6 +1746,7 @@ void new_plugins(JCR *jcr) bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx)); memset(b_ctx, 0, sizeof(bacula_ctx)); b_ctx->jcr = jcr; + b_ctx->plugin = plugin; plugin_ctx_list[i].bContext = (void *)b_ctx; /* Bacula private context */ plugin_ctx_list[i].pContext = NULL; if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) { @@ -2102,14 +2131,18 @@ static bRC baculaRegisterEvents(bpContext *ctx, ...) va_list args; uint32_t event; - Dsm_check(999); - if (!ctx) { - return bRC_Error; - } - va_start(args, ctx); while ((event = va_arg(args, uint32_t))) { Dmsg1(dbglvl, "Plugin wants event=%u\n", event); + switch (event) { + case bEventVerifyStream: + /* We will call specific function, maybe VerifyFile() + pluginIO */ + plugin_register_verify_data(ctx); + break; + default: + Dmsg0(50, "Event registration not implemented\n"); + break; + } } va_end(args); Dsm_check(999); @@ -2522,6 +2555,182 @@ static bRC baculaAccurateAttribs(bpContext *ctx, accurate_attribs_pkt *att) return bRC_OK; } +bRC plugin_verify_data_close(JCR *jcr) +{ + bpContext *ctx; + bacula_ctx *bctx; + struct io_pkt io; + if (!jcr->plugin_verify) { + return bRC_Skip; // Nothing registered + } + foreach_alist(ctx, jcr->plugin_verify) { + bctx = get_bacula_ctx(ctx); + jcr->plugin_ctx = ctx; + jcr->plugin = bctx->plugin; + if (is_plugin_disabled(jcr)) { + continue; + } + if (bctx->verifyFileCalled) { + io.pkt_size = sizeof(io); + io.pkt_end = sizeof(io); + io.func = IO_CLOSE; + io.count = 0; + io.buf = NULL; + io.fname = NULL; + io.flags = 0; + io.mode = O_WRONLY; + io.win32 = false; + io.lerror = 0; + io.status = -1; + plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io); + if (io.win32) { + errno = b_errno_win32; + } else { + errno = io.io_errno; + } + bctx->verifyFileCalled = false; + if (io.status >= 0) { + // Do something useful? + } + if (plug_func(jcr->plugin)->endVerifyFile) { + plug_func(jcr->plugin)->endVerifyFile(jcr->plugin_ctx); // Check the return code? + } + } + } + jcr->plugin_ctx = NULL; + jcr->plugin = NULL; + return bRC_OK; +} + + +bRC plugin_verify_data_update(JCR *jcr, char *data, int size) +{ + bpContext *ctx; + bacula_ctx *bctx; + struct io_pkt io; + + if (!jcr->plugin_verify) { + return bRC_Skip; // Nothing registered + } + foreach_alist(ctx, jcr->plugin_verify) { + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + jcr->plugin_ctx = ctx; + jcr->plugin = bctx->plugin; + if (is_plugin_disabled(jcr)) { + continue; + } + if (bctx->verifyFileCalled) { + io.pkt_size = sizeof(io); + io.pkt_end = sizeof(io); + io.func = IO_WRITE; + io.count = size; + io.buf = data; + io.fname = NULL; + io.flags = 0; + io.mode = O_WRONLY; + io.win32 = false; + io.lerror = 0; + io.status = -1; + plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io); + if (io.win32) { + errno = b_errno_win32; + } else { + errno = io.io_errno; + } + if (io.status >= 0) { + // Do something useful? Maybe close + } + + } + } + jcr->plugin_ctx = NULL; + jcr->plugin = NULL; + return bRC_OK; +} + +/* + * @brief Bacula function called for each new file in verify data job + * @param JCR *jcr + * @param ATTR *attr + * @return int (file descriptor) + */ +bRC plugin_verify_data_open(JCR *jcr, ATTR *attr) +{ + bpContext *ctx; + restore_pkt rp; + bacula_ctx *bctx; + struct io_pkt io; + if (!jcr->plugin_verify) { + return bRC_Skip; // Nothing registered + } + foreach_alist(ctx, jcr->plugin_verify) { + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + jcr->plugin_ctx = ctx; + jcr->plugin = bctx->plugin; + bctx->verifyFileCalled = false; + if (is_plugin_disabled(jcr)) { + continue; + } + fill_restore_pkt(jcr, attr, &rp); + if (plug_func(jcr->plugin)->startVerifyFile) { + if (plug_func(jcr->plugin)->startVerifyFile(jcr->plugin_ctx, &rp) == bRC_OK) { + io.pkt_size = sizeof(io); + io.pkt_end = sizeof(io); + io.func = IO_OPEN; + io.count = 0; + io.buf = NULL; + io.fname = attr->fname; + io.flags = 0; + io.mode = O_WRONLY; + io.win32 = false; + io.lerror = 0; + io.status = -1; + plug_func(jcr->plugin)->pluginIO(jcr->plugin_ctx, &io); + if (io.win32) { + errno = b_errno_win32; + } else { + errno = io.io_errno; + } + if (io.status >= 0) { + bctx->verifyFileCalled = true; + } + } + } + } + jcr->plugin_ctx = NULL; + jcr->plugin = NULL; + return bRC_OK; +} + +/* + * @brief Bacula function to append a plugin registered to get a copy of the data + * during a Verify Data job + * @param JCR *jcr + * @param bpContext *ctx + * @return bool + */ +static void plugin_register_verify_data(bpContext *ctx) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return; + } + if (!jcr->is_JobLevel(L_VERIFY_DATA)) { + return; /* Cannot register for non DATA job */ + } + if (!jcr->plugin_verify) { + jcr->plugin_verify = New(alist(5, not_owned_by_alist)); + } + jcr->plugin_verify->append(ctx); + /* TODO: check in me->plugins if we have options for this plugin */ +} + + #ifdef TEST_PROGRAM int (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL; diff --git a/bacula/src/filed/fd_plugins.h b/bacula/src/filed/fd_plugins.h index 5c3f1a906..27329aa1a 100644 --- a/bacula/src/filed/fd_plugins.h +++ b/bacula/src/filed/fd_plugins.h @@ -466,7 +466,8 @@ typedef enum { bEventOptionPlugin = 23, bEventHandleBackupFile = 24, /* Used with Options Plugin */ bEventComponentInfo = 25, /* Plugin component */ - bEventFeatures = 26 /* Ask for file list, ... "xxx,yyy,zzz" */ + bEventFeatures = 26, /* Ask for file list, ... "xxx,yyy,zzz" */ + bEventVerifyStream = 27, /* Register to get a copy of the data stream during verify */ } bEventType; @@ -513,6 +514,9 @@ bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length); bool plugin_check_stream(JCR *jcr, int32_t &stream); bool plugin_query_parameter(JCR *jcr, char *command, char *param, void sendit(JCR *jcr, const char *str)); bool plugin_backup_metadata(JCR *jcr, FF_PKT *ff_pkt); +bRC plugin_verify_data_close(JCR *jcr); +bRC plugin_verify_data_open(JCR *jcr, ATTR *attr); +bRC plugin_verify_data_update(JCR *jcr, char *buf, int len); #endif #ifdef __cplusplus @@ -613,6 +617,8 @@ typedef struct s_pluginFuncs { bRC (*checkStream)(bpContext *ctx, struct stream_pkt *sp); bRC (*queryParameter)(bpContext *ctx, struct query_pkt *qp); bRC (*metadataRestore)(bpContext *ctx, struct meta_pkt *mp); + bRC (*startVerifyFile)(bpContext *ctx, struct restore_pkt *rp); + bRC (*endVerifyFile)(bpContext *ctx); } pFuncs; #define plug_func(plugin) ((pFuncs *)(plugin->pfuncs)) diff --git a/bacula/src/filed/filed_conf.c b/bacula/src/filed/filed_conf.c index cfdec034e..363a1d881 100644 --- a/bacula/src/filed/filed_conf.c +++ b/bacula/src/filed/filed_conf.c @@ -87,6 +87,7 @@ static RES_ITEM cli_items[] = { {"PidDirectory", store_dir, ITEM(res_client.pid_directory), 0, ITEM_REQUIRED, 0}, {"SubsysDirectory", store_dir, ITEM(res_client.subsys_directory), 0, 0, 0}, {"PluginDirectory", store_dir, ITEM(res_client.plugin_directory), 0, 0, 0}, + {"Plugin", store_alist_str, ITEM(res_client.plugins), 0, 0, 0}, {"SnapshotCommand", store_str, ITEM(res_client.snapshot_command), 0, 0, 0}, {"ScriptsDirectory", store_dir, ITEM(res_client.scripts_directory), 0, 0, 0}, {"MaximumConcurrentJobs", store_pint32, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 20}, @@ -584,6 +585,9 @@ void free_resource(RES *sres, int type) if (res->res_client.plugin_directory) { free(res->res_client.plugin_directory); } + if (res->res_client.plugins) { + delete res->res_client.plugins; + } if (res->res_client.dedup_index_dir) { free(res->res_client.dedup_index_dir); } diff --git a/bacula/src/filed/filed_conf.h b/bacula/src/filed/filed_conf.h index c17aaf6b7..a4acca622 100644 --- a/bacula/src/filed/filed_conf.h +++ b/bacula/src/filed/filed_conf.h @@ -127,6 +127,7 @@ struct CLIENT { char *pid_directory; char *subsys_directory; char *plugin_directory; /* Plugin directory */ + alist *plugins; /* Non job specific Plugin options */ char *scripts_directory; char *snapshot_command; char *dedup_index_dir; /* Directory for local dedup cache (deprecated) */ diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index ab2737658..354dedf85 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -68,6 +68,7 @@ extern int accurate_cmd(JCR *jcr); extern int collect_cmd(JCR *jcr); /* Forward referenced functions */ +static int pluginoptions_cmd(JCR *jcr); static int backup_cmd(JCR *jcr); static int component_cmd(JCR *jcr); static int cancel_cmd(JCR *jcr); @@ -148,6 +149,7 @@ struct s_cmds cmds[] = { {"restorefilelist", restorefilelist_cmd, 0}, {"statistics", collect_cmd, 0}, {"query", query_cmd, 0}, + {"pluginoptions", pluginoptions_cmd, 0}, #ifdef DEVELOPER {"exit", exit_cmd, 0}, #endif @@ -3471,7 +3473,10 @@ void filed_free_jcr(JCR *jcr) #ifdef WIN32_VSS VSSCleanup(jcr->pVSSClient); #endif + bdelete_and_null(jcr->plugin_verify); + bdelete_and_null(jcr->plugin_options_list); free_plugins(jcr); /* release instantiated plugins */ + free_runscripts(jcr->RunScripts); delete jcr->RunScripts; free_path_list(jcr); @@ -3529,6 +3534,30 @@ int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd) return 0; } +/* + * Get PluginOptions from the Director + * + */ +static int pluginoptions_cmd(JCR *jcr) +{ + POOL_MEM buf(PM_MESSAGE); + BSOCK *dir = jcr->dir_bsock; + buf.check_size(dir->msglen+1); + if (scan_string(dir->msg, "pluginoptions=%s", buf.c_str()) == 1) { + unbash_spaces(buf.c_str()); + if (!jcr->plugin_options_list) { + jcr->plugin_options_list = New(alist(5, owned_by_alist)); + } + jcr->plugin_options_list->append(bstrdup(buf.c_str())); + + } else { + dir->fsend(_("2992 Bad pluginoptions command\n")); + return 0; + } + dir->fsend("2000 OK plugin options\n"); + return 1; +} + /* * Small helper to manager a POLL request when the heartbeat is started * When we send POLL, we get a OK message, but if the heartbeat is started diff --git a/bacula/src/filed/verify_vol.c b/bacula/src/filed/verify_vol.c index 384a9f36b..889ab42fa 100644 --- a/bacula/src/filed/verify_vol.c +++ b/bacula/src/filed/verify_vol.c @@ -254,6 +254,7 @@ void do_verify_volume(JCR *jcr) char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; int stat; int bget_ret = 0; + char *opts; char *wbuf; /* write buffer */ uint32_t wsize; /* write size */ uint32_t rsize; /* read size */ @@ -270,6 +271,12 @@ void do_verify_volume(JCR *jcr) dir = jcr->dir_bsock; jcr->setJobStatus(JS_Running); + if (jcr->plugin_options_list) { + foreach_alist(opts, jcr->plugin_options_list) { + generate_plugin_event(jcr, bEventPluginCommand, (void *)opts); + } + } + LockRes(); CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL); UnlockRes(); @@ -337,6 +344,10 @@ void do_verify_volume(JCR *jcr) case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: Dmsg0(400, "Stream=Unix Attributes.\n"); + if (plugin_verify_data_close(jcr) == bRC_Error) { + goto bail_out; + } + if (!vctx.close_previous_stream()) { goto bail_out; } @@ -398,6 +409,11 @@ void do_verify_volume(JCR *jcr) Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror()); goto bail_out; } + + } else if (jcr->is_JobLevel(L_VERIFY_DATA) && attr->type == FT_REG) { + if (plugin_verify_data_open(jcr, attr) == bRC_Error) { + goto bail_out; + } } break; @@ -460,6 +476,9 @@ void do_verify_volume(JCR *jcr) if (!jcr->crypto.digest) { jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype); } + if (plugin_verify_data_close(jcr) == bRC_Error) { + goto bail_out; + } vctx.close_previous_stream(); if (strncmp(digest, vctx.digest, MIN(sizeof(digest), sizeof(vctx.digest))) != 0) @@ -538,6 +557,11 @@ void do_verify_volume(JCR *jcr) vctx.update_checksum(wbuf, wsize); + if (plugin_verify_data_update(jcr, wbuf, wsize) == bRC_Error) { + dequeue_messages(jcr); + goto bail_out; + } + if (vctx.stream == STREAM_WIN32_GZIP_DATA || vctx.stream == STREAM_WIN32_DATA || vctx.stream == STREAM_WIN32_COMPRESSED_DATA @@ -564,6 +588,9 @@ void do_verify_volume(JCR *jcr) if (bget_ret == BNET_EXT_TERMINATE) { goto bail_out; } + if (plugin_verify_data_close(jcr) == bRC_Error) { + goto bail_out; + } if (!vctx.close_previous_stream()) { goto bail_out; } diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 53038cf58..57d110dcf 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -489,6 +489,8 @@ public: DedupFiledInterface *dedup; /* help the FD to do deduplication */ bool dedup_use_cache; /* use client cache */ VSSClient *pVSSClient; /* VSS handler */ + alist *plugin_verify; /* Registered plugins that need a copy of the data in verify job */ + alist *plugin_options_list; /* list of the options to use in a job */ #endif /* FILE_DAEMON */ diff --git a/bacula/src/version.h b/bacula/src/version.h index add0453c6..0f477fd45 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -209,9 +209,9 @@ */ #ifdef COMMUNITY -#define FD_VERSION 14 /* make same as community Linux FD */ +#define FD_VERSION 15 /* make same as community Linux FD */ #else -#define FD_VERSION 14 /* Enterprise FD version */ +#define FD_VERSION 15 /* Enterprise FD version */ #endif /*