X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fua_run.c;h=4d79e5ef7db937670c2bf3d8732bca5efc383e81;hb=649ac682e935fd1b20bec0d3248a45d7925bee6a;hp=be7b5e89a0fe51d1cda8d641cab4cbc238025e3f;hpb=0af84b856321071939b45ad087802031b8547c6e;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_run.c b/bacula/src/dird/ua_run.c index be7b5e89a0..4d79e5ef7d 100644 --- a/bacula/src/dird/ua_run.c +++ b/bacula/src/dird/ua_run.c @@ -1,208 +1,1073 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2001-2008 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 - License as published by the Free Software Foundation and included - in the file LICENSE. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - 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 - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - - Bacula® is a registered trademark of Kern Sibbald. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. */ /* - * * Bacula Director -- Run Command * * Kern Sibbald, December MMI - * - * Version $Id$ */ #include "bacula.h" #include "dird.h" -class run_ctx { -public: - char *job_name, *level_name, *jid, *store_name, *pool_name; - char *where, *fileset_name, *client_name, *bootstrap, *regexwhere; - char *restore_client_name, *comment; - const char *replace; - char *when, *verify_job_name, *catalog_name; - char *previous_job_name; - char *since; - char *plugin_options; - const char *verify_list; - JOB *job; - JOB *verify_job; - JOB *previous_job; - USTORE *store; - CLIENT *client; - FILESET *fileset; - POOL *pool; - CAT *catalog; - int Priority; - int files; - bool cloned; - bool mod; - int spool_data; - bool spool_data_set; +const char *get_command(int index); + +class run_ctx { +public: + char *job_name, *level_name, *jid, *store_name, *pool_name; + char *where, *fileset_name, *client_name, *bootstrap, *regexwhere; + char *restore_client_name, *comment, *media_type, *next_pool_name; + const char *replace; + char *when, *verify_job_name, *catalog_name; + char *previous_job_name; + char *since; + char *plugin_options; + const char *verify_list; + JOB *job; + JOB *verify_job; + JOB *previous_job; + JOB_DBR jr; + USTORE *store; + CLIENT *client; + FILESET *fileset; + POOL *pool; + POOL *next_pool; + CAT *catalog; + JobId_t JobId; + alist *JobIds; + int Priority; + int files; + bool cloned; + bool mod; + bool restart; + bool done; + bool alljobid; + bool fdcalled; + int spool_data; + bool spool_data_set; + int accurate; + bool accurate_set; + int ignoreduplicatecheck; + bool ignoreduplicatecheck_set; + alist *plugin_config; /* List of all plugin_item */ + /* Methods */ + run_ctx() { memset(this, 0, sizeof(run_ctx)); + store = new USTORE; }; + ~run_ctx() { + delete store; + if (JobIds) { + delete JobIds; + } + }; +}; + +/* Forward referenced subroutines */ +static void select_job_level(UAContext *ua, JCR *jcr); +static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, + const char *verify_list, char *jid, const char *replace, + char *client_name); +static void select_where_regexp(UAContext *ua, JCR *jcr); +static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc); +static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc); +static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc); +static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc); + +/* Imported variables */ +extern struct s_kw ReplaceOptions[]; + +/* + * For Backup and Verify Jobs + * run [job=] level= + * + * For Restore Jobs + * run + * + * Returns: 0 on error + * JobId if OK + * + */ +int run_cmd(UAContext *ua, const char *cmd) +{ + JCR *jcr = NULL; + run_ctx rc; + int status; + + if (!open_client_db(ua)) { + goto bail_out; + } + + if (!scan_run_command_line_arguments(ua, rc)) { + goto bail_out; + } + + for ( ;; ) { + /* + * Create JCR to run job. NOTE!!! after this point, free_jcr() + * before returning. + */ + if (!jcr) { + jcr = new_jcr(sizeof(JCR), dird_free_jcr); + set_jcr_defaults(jcr, rc.job); + jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */ + ua->jcr->unlink_bsr = false; + if (find_arg(ua, NT_("fdcalled")) > 0) { + rc.fdcalled = true; + } + } + /* Transfer JobIds to new restore Job */ + if (ua->jcr->JobIds) { + if (jcr->JobIds) { + free_pool_memory(jcr->JobIds); + } + jcr->JobIds = ua->jcr->JobIds; + ua->jcr->JobIds = NULL; + } + /* Transfer VSS component info */ + if (ua->jcr->component_fname) { + jcr->component_fname = ua->jcr->component_fname; + ua->jcr->component_fname = NULL; + jcr->component_fd = ua->jcr->component_fd; + ua->jcr->component_fd = NULL; + } + /* Transfer Plugin Restore Configuration */ + if (ua->jcr->plugin_config) { + jcr->plugin_config = ua->jcr->plugin_config; + ua->jcr->plugin_config = NULL; + } + + if (!set_run_context_in_jcr(ua, jcr, rc)) { + break; /* error get out of while loop */ + } + + /* Run without prompting? */ + if (ua->batch || find_arg(ua, NT_("yes")) > 0) { + return start_job(ua, jcr, rc); + } + + /* + * Prompt User to see if all run job parameters are correct, and + * allow him to modify them. + */ + if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace, + rc.client_name)) { + break; /* error get out of while loop */ + } + + if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) { + break; /* error get out of while loop */ + } + + if (strncasecmp(ua->cmd, ".mod ", 5) == 0 || + (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) { + parse_ua_args(ua); + rc.mod = true; + if (!scan_run_command_line_arguments(ua, rc)) { + break; /* error get out of while loop */ + } + continue; /* another round with while loop */ + } + + /* Allow the user to modify the settings */ + status = modify_job_parameters(ua, jcr, rc); + if (status == 0) { + continue; /* another round with while loop */ + } + if (status == -1) { /* error */ + break; /* error get out of while loop */ + } + + if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) { + return start_job(ua, jcr, rc); + } + if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) { + break; /* get out of while loop */ + } + ua->send_msg(_("\nBad response: %s. You must answer yes, mod, or no.\n\n"), ua->cmd); + } + +bail_out: + ua->send_msg(_("Job not run.\n")); + if (ua->jcr->component_fd) { + fclose(ua->jcr->component_fd); + ua->jcr->component_fd = NULL; + } + if (ua->jcr->component_fname) { + unlink(ua->jcr->component_fname); + free_and_null_pool_memory(ua->jcr->component_fname); + } + if (jcr) { + if (jcr->component_fd) { + fclose(jcr->component_fd); + jcr->component_fd = NULL; + } + if (jcr->component_fname) { + unlink(jcr->component_fname); + free_and_null_pool_memory(jcr->component_fname); + } + free_jcr(jcr); + } + return 0; /* do not run */ +} + +static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc) +{ + JobId_t JobId; + char ed1[50]; + + /* Do a final check for the client, the job can change in the previous menu */ + if (jcr->client && jcr->job) { + if (!acl_access_client_ok(ua, jcr->client->name(), jcr->job->JobType)) { + ua->error_msg(_("Job failed. Client \"%s\" not authorized on this console\n"), jcr->client->name()); + free_jcr(jcr); + return 0; + } + } + + /* Do a final check for the where/regexwhere, the job can change in the previous menu */ + if (jcr->getJobType() == JT_RESTORE) { + char *p = jcr->RegexWhere ? jcr->RegexWhere : jcr->job->RegexWhere; + if (p) { + if (!acl_access_ok(ua, Where_ACL, p)) { + ua->error_msg(_("\"RegexWhere\" specification not authorized.\n")); + free_jcr(jcr); + return 0; + } + + } else { + p = jcr->where ? jcr->where : jcr->job->RestoreWhere; + if (p) { + if (!acl_access_ok(ua, Where_ACL, p)) { + ua->error_msg(_("\"where\" specification not authorized.\n")); + free_jcr(jcr); + return 0; + } + } + } + } + + /* If we use the fdcalled feature, we keep use the UA socket + * as a FileDaemon socket. We do not use dup_bsock() because + * it doesn't work, when the UA will do a free_bsock() all + * socket childs will be closed as well. + */ + if (rc.fdcalled) { + jcr->file_bsock = ua->UA_sock; + jcr->file_bsock->set_jcr(jcr); + } + if (rc.jr.JobStatus == JS_Incomplete) { + Dmsg1(100, "Ressuming JobId=%d\n", rc.jr.JobId); + JobId = resume_job(jcr, &rc.jr); + } else { + Dmsg1(100, "Starting JobId=%d\n", rc.jr.JobId); + JobId = run_job(jcr); + } + Dmsg4(100, "JobId=%u NewJobId=%d pool=%s priority=%d\n", (int)jcr->JobId, + JobId, jcr->pool->name(), jcr->JobPriority); + free_jcr(jcr); /* release jcr */ + if (JobId == 0) { + ua->error_msg(_("Job %s failed.\n"), edit_int64(rc.jr.JobId, ed1)); + + } else { + ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1)); + } + if (rc.fdcalled) { + ua->signal(BNET_FDCALLED); /* After this point, this is a new connection */ + ua->UA_sock = new_bsock(); + ua->quit = true; + } + return JobId; +} + +/* + * If no job_name defined in the run context, ask + * the user for it. + * Then put the job resource in the run context and + * check the access rights. + */ +static bool get_job(UAContext *ua, run_ctx &rc) +{ + if (rc.job_name) { + /* Find Job */ + rc.job = GetJobResWithName(rc.job_name); + if (!rc.job) { + if (*rc.job_name != 0) { + ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name); + } + rc.job = select_job_resource(ua); + } else { + Dmsg1(100, "Found job=%s\n", rc.job_name); + } + } else if (!rc.job) { + ua->send_msg(_("A job name must be specified.\n")); + rc.job = select_job_resource(ua); + } + if (!rc.job) { + return false; + } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) { + ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name()); + return false; + } + return true; +} + +/* + * If no pool_name defined in the run context, ask + * the user for it. + * Then put the pool resource in the run context and + * check the access rights. + */ +static bool get_pool(UAContext *ua, run_ctx &rc) +{ + if (rc.pool_name) { + rc.pool = GetPoolResWithName(rc.pool_name); + if (!rc.pool) { + if (*rc.pool_name != 0) { + ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name); + } + rc.pool = select_pool_resource(ua); + } + } else if (!rc.pool) { + rc.pool = rc.job->pool; /* use default */ + } + if (!rc.pool) { + return false; + } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) { + ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name()); + return false; + } + Dmsg1(100, "Using Pool=%s\n", rc.pool->name()); + return true; +} + +static bool get_next_pool(UAContext *ua, run_ctx &rc) +{ + if (rc.next_pool_name) { + Dmsg1(100, "Have next pool=%s\n", rc.next_pool_name); + rc.next_pool = GetPoolResWithName(rc.next_pool_name); + if (!rc.next_pool) { + if (*rc.next_pool_name != 0) { + ua->warning_msg(_("NextPool \"%s\" not found.\n"), rc.next_pool_name); + } + rc.next_pool = select_pool_resource(ua); + } + } + /* NextPool can come from Job resource NextPool or Pool resource NextPool */ + if (!rc.next_pool) { + if (rc.job->next_pool) { + rc.next_pool = rc.job->next_pool; + } else { + rc.next_pool = rc.pool->NextPool; /* use default */ + } + } + if (rc.next_pool && !acl_access_ok(ua, Pool_ACL, rc.next_pool->name())) { + ua->error_msg(_("No authorization. NextPool \"%s\".\n"), rc.next_pool->name()); + return false; + } + if (rc.next_pool) { + Dmsg1(100, "Using NextPool=%s\n", NPRT(rc.next_pool->name())); + } + return true; +} + + +/* + * Fill in client data according to what is setup + * in the run context, and make sure the user + * has authorized access to it. + */ +static bool get_client(UAContext *ua, run_ctx &rc) +{ + bool authorized=false; + if (rc.client_name) { + rc.client = GetClientResWithName(rc.client_name); + if (!rc.client) { + if (*rc.client_name != 0) { + ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name); + } + rc.client = select_client_resource(ua, rc.job->JobType); + } + } else if (!rc.client) { + rc.client = rc.job->client; /* use default */ + } + + Dmsg1(800, "Using client=%s\n", rc.client->name()); + + if (rc.restore_client_name) { + rc.client = GetClientResWithName(rc.restore_client_name); + if (!rc.client) { + if (*rc.restore_client_name != 0) { + ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name); + } + rc.client = select_client_resource(ua, rc.job->JobType); + } + } else if (!rc.client) { + rc.client = rc.job->client; /* use default */ + } + + if (!rc.client) { + return false; + + } else if (acl_access_client_ok(ua, rc.client->name(), rc.job->JobType)) { + authorized = true; + } + if (!authorized) { + ua->error_msg(_("No authorization. Client \"%s\".\n"), + rc.client->name()); + return false; + } + Dmsg1(800, "Using restore client=%s\n", rc.client->name()); + return true; +} + + +/* + * Fill in fileset data according to what is setup + * in the run context, and make sure the user + * has authorized access to it. + */ +static bool get_fileset(UAContext *ua, run_ctx &rc) +{ + if (rc.fileset_name) { + rc.fileset = GetFileSetResWithName(rc.fileset_name); + if (!rc.fileset) { + ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name); + rc.fileset = select_fileset_resource(ua); + } + } else if (!rc.fileset) { + rc.fileset = rc.job->fileset; /* use default */ + } + if (!rc.fileset) { + return false; + } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) { + ua->send_msg(_("No authorization. FileSet \"%s\".\n"), + rc.fileset->name()); + return false; + } + return true; +} + +/* + * Fill in storage data according to what is setup + * in the run context, and make sure the user + * has authorized access to it. + */ +static bool get_storage(UAContext *ua, run_ctx &rc) +{ + if (rc.store_name) { + rc.store->store = GetStoreResWithName(rc.store_name); + pm_strcpy(rc.store->store_source, _("Command input")); + if (!rc.store->store) { + if (*rc.store_name != 0) { + ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name); + } + rc.store->store = select_storage_resource(ua); + pm_strcpy(rc.store->store_source, _("user selection")); + } + } else if (!rc.store->store) { + get_job_storage(rc.store, rc.job, NULL); /* use default */ + } + if (!rc.store->store) { + ua->error_msg(_("No storage specified.\n")); + return false; + } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) { + ua->error_msg(_("No authorization. Storage \"%s\".\n"), + rc.store->store->name()); + return false; + } + Dmsg1(800, "Using storage=%s\n", rc.store->store->name()); + return true; +} + +/* + * Get and pass back a list of Jobids in rc.jid + */ +static bool get_jobid_list(UAContext *ua, sellist &sl, run_ctx &rc) +{ + int i, JobId; + JOB_DBR jr; + char *pJobId; + bool found = false; + + memset(&jr, 0, sizeof(jr)); + rc.jid = NULL; + /* See if any JobId is specified */ + if ((i=find_arg(ua, "jobid")) >= 0) { + rc.jid = ua->argv[i]; + if (!rc.jid) { + ua->send_msg(_("No JobId specified.\n")); + return false; + } + if (!sl.set_string(ua->argv[i], true)) { + ua->send_msg("%s", sl.get_errmsg()); + return false; + } + return true; + } + + /* No JobId list give, so see if he specified a Job */ + if ((i=find_arg(ua, "job")) >= 0) { + rc.job_name = ua->argv[i]; + if (!get_job(ua, rc)) { + ua->send_msg(_("Invalid or no Job name specified.\n")); + return false; + } + } + + if ((i=find_arg_with_value(ua, "limit")) >= 0) { + jr.limit = str_to_int64(ua->argv[i]); + + } else { + jr.limit = 100; /* max 100 records */ + } + + if (rc.job_name) { + bstrncpy(jr.Name, rc.job_name, sizeof(jr.Name)); + } else { + jr.Name[0] = 0; + } + jr.JobStatus = rc.jr.JobStatus; + Dmsg2(100, "JobStatus=%d JobName=%s\n", jr.JobStatus, jr.Name); + /* rc.JobIds is alist of all records found and printed */ + rc.JobIds = db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, INCOMPLETE_JOBS); + if (!rc.JobIds || rc.JobIds->size()==0 || + !get_selection_list(ua, sl, _("Enter the JobId list to select: "), false)) { + return false; + } + Dmsg1(100, "list=%s\n", sl.get_list()); + /* + * Make sure each item entered is in the JobIds list + */ + while ( (JobId = sl.next()) > 0) { + foreach_alist(pJobId, rc.JobIds) { + if (JobId == str_to_int64(pJobId)) { + pJobId[0] = 0; + found = true; + break; + } + } + if (!found) { + ua->error_msg(_("JobId=%d entered is not in the list.\n"), JobId); + return false; + } + } + sl.begin(); /* reset to walk list again */ + rc.done = false; + return true; +} + +static bool get_jobid_from_list(UAContext *ua, sellist &sl, run_ctx &rc) +{ + int JobId; + + if (rc.done) { + return false; + } + if ((JobId = sl.next()) < 0) { + Dmsg1(100, "sl.next()=%d\n", JobId); + rc.done = true; + return false; + } + rc.jr.JobId = rc.JobId = JobId; + Dmsg1(100, "Next JobId=%d\n", rc.JobId); + if (!db_get_job_record(ua->jcr, ua->db, &rc.jr)) { + ua->error_msg(_("Could not get job record for selected JobId=%d. ERR=%s"), + rc.JobId, db_strerror(ua->db)); + return false; + } + Dmsg3(100, "Job=%s JobId=%d JobStatus=%c\n", rc.jr.Name, rc.jr.JobId, + rc.jr.JobStatus); + rc.job_name = rc.jr.Name; + if (!get_job(ua, rc)) { + return false; + } + if (!get_pool(ua, rc)) { + return false; + } + get_job_storage(rc.store, rc.job, NULL); + rc.client_name = rc.job->client->hdr.name; + if (!get_client(ua, rc)) { + return false; + } + if (!get_fileset(ua, rc)) { + return false; + } + if (!get_storage(ua, rc)) { + return false; + } + return true; +} + +/* + * Restart Canceled, Failed, or Incomplete Jobs + * + * Returns: 0 on error + * JobId if OK + * + */ +int restart_cmd(UAContext *ua, const char *cmd) +{ + JCR *jcr = NULL; + run_ctx rc; + sellist sl; + int i, j; + bool got_kw = false; + struct s_js { + const char *status_name; + int32_t job_status; + }; + struct s_js kw[] = { + {"Incomplete", JS_Incomplete}, + {"Canceled", JS_Canceled}, + {"Failed", JS_FatalError}, + {"All", 0}, + {NULL, 0} + }; + + if (!open_client_db(ua)) { + return 0; + } + + rc.jr.JobStatus = 0; + for (i=1; iargc; i++) { + for (j=0; kw[j].status_name; j++) { + if (strcasecmp(ua->argk[i], kw[j].status_name) == 0) { + rc.jr.JobStatus = kw[j].job_status; + got_kw = true; + break; + } + } + } + if (!got_kw) { /* Must prompt user */ + start_prompt(ua, _("You have the following choices:\n")); + for (i=0; kw[i].status_name; i++) { + add_prompt(ua, kw[i].status_name); + } + i = do_prompt(ua, NULL, _("Select termination code: "), NULL, 0); + if (i < 0) { + return 0; + } + rc.jr.JobStatus = kw[i].job_status; + } + + /* type now has what job termination code we want to look at */ + Dmsg1(100, "Termination code=%c\n", rc.jr.JobStatus); + + /* Get a list of JobIds to restore */ + if (!get_jobid_list(ua, sl, rc)) { + if (rc.JobIds) { + rc.JobIds->destroy(); + } + return false; + } + Dmsg1(100, "list=%s\n", sl.get_list()); + + while (get_jobid_from_list(ua, sl, rc)) { + /* + * Create JCR to run job. NOTE!!! after this point, free_jcr() + * before returning. + */ + if (!jcr) { + jcr = new_jcr(sizeof(JCR), dird_free_jcr); + set_jcr_defaults(jcr, rc.job); + jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */ + ua->jcr->unlink_bsr = false; + } + + if (!set_run_context_in_jcr(ua, jcr, rc)) { + break; + } + start_job(ua, jcr, rc); + jcr = NULL; + } + + if (jcr) { + free_jcr(jcr); + } + if (rc.JobIds) { + rc.JobIds->destroy(); + } + return 0; /* do not run */ +} + + +/* + * Plugin restore option part + */ + +/* Free a plugin_config_item */ +void free_plugin_config_item(plugin_config_item *elt) +{ + free(elt->plugin_name); + free_pool_memory(elt->content); + free(elt); +} + +/* Free a list of plugins (do not free the list itself) */ +void free_plugin_config_items(alist *lst) +{ + plugin_config_item *elt; + + if (!lst) { + return; + } - /* Methods */ - run_ctx() { memset(this, 0, sizeof(run_ctx)); - store = new USTORE; }; - ~run_ctx() { delete store; }; + foreach_alist(elt, lst) { + free_plugin_config_item(elt); + } +} + +/* Structure used in the sql query to get configuration restore objects */ +struct plugin_config_handler_t +{ + UAContext *ua; /* UAContext for user input */ + POOLMEM *tmp; /* Used to store the config object */ + alist *plugins; /* Configuration plugin list */ + alist *content; /* Temp file used by each plugin */ }; -/* Forward referenced subroutines */ -static void select_job_level(UAContext *ua, JCR *jcr); -static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, - const char *verify_list, char *jid, const char *replace, - char *client_name); -static void select_where_regexp(UAContext *ua, JCR *jcr); -static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc); -static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc); -static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc); +/* DB handler to get all configuration restore objects for a given + * set of jobids + */ +static int plugin_config_handler(void *ctx, int num_fields, char **row) +{ + struct plugin_config_handler_t *pch = (struct plugin_config_handler_t *)ctx; + UAContext *ua = pch->ua; + JCR *jcr = ua->jcr; + int32_t len; + + /* object */ + db_unescape_object(jcr, ua->db, + row[8], /* Object */ + str_to_uint64(row[1]), /* Object length */ + &pch->tmp, &len); + + /* Is compressed ? */ + if (str_to_int64(row[5]) > 0) { + int full_len = str_to_int64(row[2]); + int out_len = full_len + 100; /* full length */ + char *obj = (char *)malloc(out_len); + Zinflate(pch->tmp, len, obj, out_len); /* out_len is updated */ + if (out_len != full_len) { + ua->error_msg(_("Decompression failed. Len wanted=%d got=%d. Object=%s\n"), + full_len, out_len, row[9]); + } + obj[out_len] = 0; + pch->content->append(obj); -/* Imported variables */ -extern struct s_kw ReplaceOptions[]; + } else { + pch->tmp[len]=0; + pch->content->append(bstrdup(pch->tmp)); + } -/* - * For Backup and Verify Jobs - * run [job=] level= - * - * For Restore Jobs - * run - * - * Returns: 0 on error - * JobId if OK + pch->plugins->append(bstrdup(row[9])); + return 0; +} + +/* Save a Plugin Config object (ConfigFile) inside the JCR + * using a list of plugin_config_item * + * We allow only one Plugin Config object per Plugin */ -int run_cmd(UAContext *ua, const char *cmd) +static void plugin_config_save_jcr(UAContext *ua, JCR *jcr, + char *pname, ConfigFile *ini) { - JCR *jcr = NULL; - run_ctx rc; - int status; + plugin_config_item *elt; + if (!jcr->plugin_config) { + jcr->plugin_config = New(alist(5, not_owned_by_alist)); + } - if (!open_client_db(ua)) { - return 1; + /* Store only one Plugin Config object per plugin command */ + for (int i = 0; i < jcr->plugin_config->size() ; i++) { + elt = (plugin_config_item *) jcr->plugin_config->get(i); + if (strcmp(elt->plugin_name, pname) == 0) { + jcr->plugin_config->remove(i); + free_plugin_config_item(elt); + break; + } } - if (!scan_command_line_arguments(ua, rc)) { + elt = (plugin_config_item *) malloc (sizeof(plugin_config_item)); + elt->plugin_name = bstrdup(pname); + elt->content = get_pool_memory(PM_FNAME); + ini->dump_results(&elt->content); + jcr->plugin_config->append(elt); +} + +/* TODO: Allow to have sub-menus Advanced.restore_mode can be + * in a Advanced panel (sub menu) + */ + +/* Take the ConfigIni struture and display user menu for a given plugin */ +static int plugin_display_options(UAContext *ua, JCR *jcr, ConfigFile *ini) +{ + int i, nb; + int jcr_pos = -1; + POOL_MEM prompt, tmp; + bool found; + INI_ITEM_HANDLER *h; + + /* TODO: See how to work in API mode + if (ua->api) { + ua->signal(BNET_RUN_CMD); + } + */ + + /* Take a look in the plugin_config list to see if we have something to + * initialize + */ + if (jcr->plugin_config) { + plugin_config_item *item=NULL; + + for (jcr_pos = 0; jcr_pos < jcr->plugin_config->size() ; jcr_pos++) { + item = (plugin_config_item *)jcr->plugin_config->get(jcr_pos); + + if (strcmp(item->plugin_name, ini->plugin_name) == 0) /* bpipe:xxx:yyyy */ + { + if (!ini->dump_string(item->content, strlen(item->content)) || + !ini->parse(ini->out_fname)) + { + ua->error_msg(_("Unable to use current plugin configuration, " + "discarding it.")); + } + /* When we are here, we can type yes (it will add it back), or no + * to not use this plugin configuration. So, don't keep it in the + * list. + */ + jcr->plugin_config->remove(jcr_pos); + free_plugin_config_item(item); + break; + } + } + } + +configure_again: + ua->send_msg(_("Plugin Restore Options\n")); + + for (nb=0; ini->items[nb].name; nb++) { + + if (ini->items[nb].found) { + /* When calling the handler, It will convert the value + * to a string representation in ini->edit + */ + ini->items[nb].handler(NULL, ini, &ini->items[nb]); + } else { + if (ini->items[nb].required) { + pm_strcpy(ini->edit, _("*None, but required*")); + + } else { + pm_strcpy(ini->edit, _("*None*")); + } + } + + Mmsg(tmp, "%s:", ini->items[nb].name); + + Mmsg(prompt, "%-20s %-20s ", + tmp.c_str(), ini->edit); + + if (ini->items[nb].default_value) { + Mmsg(tmp, "(%s)", ini->items[nb].default_value); + pm_strcat(prompt, tmp.c_str()); + } + + ua->send_msg("%s\n", prompt.c_str()); + } + + if (!get_cmd(ua, _("Use above plugin configuration? (yes/mod/no): "))) { + ini->clear_items(); return 0; } - if (find_arg(ua, NT_("fdcalled")) > 0) { - jcr->file_bsock = dup_bsock(ua->UA_sock); - ua->quit = true; + /* '', 'y', 'ye', and 'yes' are valid */ + if (strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) { + return 1; } - /* - * Create JCR to run job. NOTE!!! after this point, free_jcr() - * before returning. + if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) { + ini->clear_items(); + return 0; + } + + /* When using "mod", we display the list of parameters with their + * comments, and we let the user choose one entry to modify */ - if (!jcr) { - jcr = new_jcr(sizeof(JCR), dird_free_jcr); - set_jcr_defaults(jcr, rc.job); - jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */ - ua->jcr->unlink_bsr = false; + if (strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) { + start_prompt(ua, _("You have the following choices:\n")); + + for (nb=0; ini->items[nb].name; nb++) { + + if (ini->items[nb].comment) { + Mmsg(tmp, " (%s)", ini->items[nb].comment); + } else { + pm_strcpy(tmp, ""); + } + + Mmsg(prompt, "%s%s ", + ini->items[nb].name, tmp.c_str()); + + add_prompt(ua, prompt.c_str()); + } + + i = do_prompt(ua, NULL, _("Select parameter to modify"), NULL, 0); + + if (i < 0) { + ini->clear_items(); + return 0; + } + + Mmsg(prompt, _("Please enter a value for %s: "), ini->items[i].name); + + /* Now use the handler to know how to ask the value to the user. + * For example, boolean will use get_yes_no(), pint32 will use get_pint() + */ + h = ini->items[i].handler; + if (h == ini_store_int32 || + h == ini_store_pint32) { + found = ini->items[i].found = get_pint(ua, prompt.c_str()); + if (found) { + ini->items[i].val.int32val = ua->pint32_val; + } + + } else if (h == ini_store_bool) { + found = ini->items[i].found = get_yesno(ua, prompt.c_str()); + if (found) { + ini->items[i].val.boolval = ua->pint32_val; + } + + } else if (h == ini_store_name) { + found = ini->items[i].found = get_cmd(ua, prompt.c_str()); + if (found) { + strncpy(ini->items[i].val.nameval, ua->cmd, MAX_NAME_LENGTH -1); + ini->items[i].val.nameval[MAX_NAME_LENGTH - 1] = 0; + } + + } else if (h == ini_store_str) { + found = ini->items[i].found = get_cmd(ua, prompt.c_str()); + if (found) { + ini->items[i].val.strval = bstrdup(ua->cmd); + } + + } else if (h == ini_store_int64 || + h == ini_store_pint64) { + found = ini->items[i].found = get_pint(ua, prompt.c_str()); + if (found) { + ini->items[i].val.int64val = ua->int64_val; + } + } + goto configure_again; } -try_again: - if (!reset_restore_context(ua, jcr, rc)) { - goto bail_out; + return 1; /* never reached */ +} + +/* Display a menu with all plugins */ +static void plugin_config(UAContext *ua, JCR *jcr, run_ctx &rc) +{ + int i, nb; + char *elt, *tmp; + ConfigFile *ini = NULL; + POOLMEM *query=NULL; + struct plugin_config_handler_t pch; + + /* No jobids for this restore, probably wrong */ + if (!jcr->JobIds || !jcr->JobIds[0]) { + return; } + if (!open_client_db(ua)) { + return; + } + + pch.ua = ua; + query = get_pool_memory(PM_FNAME); + pch.tmp = get_pool_memory(PM_MESSAGE); + pch.plugins = New(alist(10, owned_by_alist)); + pch.content = New(alist(10, owned_by_alist)); + + /* Get all RestoreObject PLUGIN_CONFIG for the given Job */ + Mmsg(query, get_restore_objects, jcr->JobIds, FT_PLUGIN_CONFIG); + db_sql_query(ua->db, query, plugin_config_handler, &pch); - /* Run without prompting? */ - if (ua->batch || find_arg(ua, NT_("yes")) > 0) { - goto start_job; + if (!pch.plugins || pch.plugins->size() == 0) { + ua->info_msg(_("No plugin to configure\n")); + goto bail_out; } - /* - * Prompt User to see if all run job parameters are correct, and - * allow him to modify them. + /* TODO: Let see if we want to configure plugins that were given in command + * line. */ - if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace, - rc.client_name)) { - goto bail_out; + + start_prompt(ua, _("Plugins to configure:\n")); + + nb=0; + foreach_alist(elt, pch.plugins) { + nb++; + pm_strcpy(query, elt); + add_prompt(ua, query); } - if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) { + i = do_prompt(ua, "", _("Select plugin to configure"), NULL, 0); + + if (i < 0) { goto bail_out; } - if (strncasecmp(ua->cmd, ".mod ", 5) == 0 || - (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) { - parse_ua_args(ua); - rc.mod = true; - if (!scan_command_line_arguments(ua, rc)) { - return 0; - } - goto try_again; - } - /* Allow the user to modify the settings */ - status = modify_job_parameters(ua, jcr, rc); - switch (status) { - case 0: - goto try_again; - case 1: - break; - case -1: + elt = (char *)pch.plugins->get(i); + ini = new ConfigFile(); + /* Try to read the plugin configuration, if error, loop to configure + * something else, or bail_out + */ + tmp = (char *)pch.content->get(i); + if (!ini->dump_string(tmp, strlen(tmp)) || /* Send the string to a file */ + !ini->unserialize(ini->out_fname)) { /* Read the file to initialize the ConfigFile */ + + ua->error_msg(_("Can't configure %32s\n"), elt); goto bail_out; } + ini->set_plugin_name(elt); - if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) { - JobId_t JobId; - Dmsg1(800, "Calling run_job job=%x\n", jcr->job); + if (plugin_display_options(ua, jcr, ini)) { + ini->dump_results(&query); + Dmsg1(50, "plugin: %s\n", query); -start_job: - Dmsg3(100, "JobId=%u using pool %s priority=%d\n", (int)jcr->JobId, - jcr->pool->name(), jcr->JobPriority); - Dmsg1(900, "Running a job; its spool_data = %d\n", jcr->spool_data); - JobId = run_job(jcr); - Dmsg4(100, "JobId=%u NewJobId=%d using pool %s priority=%d\n", (int)jcr->JobId, - JobId, jcr->pool->name(), jcr->JobPriority); - free_jcr(jcr); /* release jcr */ - if (JobId == 0) { - ua->error_msg(_("Job failed.\n")); - } else { - char ed1[50]; - ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1)); - } - return JobId; + /* Save the plugin somewhere in the JCR */ + plugin_config_save_jcr(ua, jcr, elt, ini); } bail_out: - ua->send_msg(_("Job not run.\n")); - free_jcr(jcr); - return 0; /* do not run */ + free_pool_memory(pch.tmp); + free_pool_memory(query); + if (ini) { + delete ini; + } + delete pch.plugins; + delete pch.content; } int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) { int i, opt; + /* * At user request modify parameters of job to be run. */ - if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) { + if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0){ FILE *fd; start_prompt(ua, _("Parameters to modify:\n")); @@ -222,13 +1087,18 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) jcr->getJobType() == JT_MIGRATE || jcr->getJobType() == JT_VERIFY) { add_prompt(ua, _("Pool")); /* 7 */ - if (jcr->getJobType() == JT_VERIFY) { + if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */ + jcr->is_JobLevel(L_VIRTUAL_FULL)) || + jcr->getJobType() == JT_COPY || + jcr->getJobType() == JT_MIGRATE) { + add_prompt(ua, _("NextPool")); /* 8 */ + } else if (jcr->getJobType() == JT_VERIFY) { add_prompt(ua, _("Verify Job")); /* 8 */ } } else if (jcr->getJobType() == JT_RESTORE) { add_prompt(ua, _("Bootstrap")); /* 7 */ add_prompt(ua, _("Where")); /* 8 */ - add_prompt(ua, _("File Relocation"));/* 9 */ + add_prompt(ua, _("File Relocation"));/* 9 */ add_prompt(ua, _("Replace")); /* 10 */ add_prompt(ua, _("JobId")); /* 11 */ } @@ -268,26 +1138,35 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) break; case 4: /* Client */ - rc.client = select_client_resource(ua); + { + int32_t jt = rc.job ? rc.job->JobType : JT_SYSTEM; + rc.client = select_client_resource(ua, jt); if (rc.client) { jcr->client = rc.client; goto try_again; } + } break; case 5: /* When */ - if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) { + if (!get_cmd(ua, _("Please enter start time as a duration or YYYY-MM-DD HH:MM:SS or return for now: "))) { break; } if (ua->cmd[0] == 0) { jcr->sched_time = time(NULL); } else { + utime_t duration; jcr->sched_time = str_to_utime(ua->cmd); if (jcr->sched_time == 0) { - ua->send_msg(_("Invalid time, using current time.\n")); - jcr->sched_time = time(NULL); + if (duration_to_utime(ua->cmd, &duration)) { + jcr->sched_time = time(NULL) + duration; + } else { + ua->send_msg(_("Invalid time, using current time.\n")); + jcr->sched_time = time(NULL); + } } } + goto try_again; case 6: /* Priority */ @@ -300,7 +1179,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) jcr->JobPriority = ua->pint32_val; } goto try_again; - case 7: + case 7: /* Pool or Bootstrap depending on JobType */ if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_COPY || @@ -325,7 +1204,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) } if (ua->cmd[0] != 0) { jcr->RestoreBootstrap = bstrdup(ua->cmd); - fd = fopen(jcr->RestoreBootstrap, "rb"); + fd = bfopen(jcr->RestoreBootstrap, "rb"); if (!fd) { berrno be; ua->send_msg(_("Warning cannot open %s: ERR=%s\n"), @@ -338,6 +1217,17 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) } goto try_again; case 8: + /* Specify Next Pool */ + if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */ + jcr->is_JobLevel(L_VIRTUAL_FULL)) || + jcr->getJobType() == JT_COPY || + jcr->getJobType() == JT_MIGRATE) { + rc.next_pool = select_pool_resource(ua); + if (rc.next_pool) { + jcr->next_pool = rc.next_pool; + goto try_again; + } + } /* Verify Job */ if (jcr->getJobType() == JT_VERIFY) { rc.verify_job = select_job_resource(ua); @@ -347,7 +1237,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) goto try_again; } /* Where */ - if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) { + if (!get_cmd(ua, _("Please enter the full path prefix for restore (/ for none): "))) { break; } if (jcr->RegexWhere) { /* cannot use regexwhere and where */ @@ -363,7 +1253,7 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) } jcr->where = bstrdup(ua->cmd); goto try_again; - case 9: + case 9: /* File relocation */ select_where_regexp(ua, jcr); goto try_again; @@ -387,16 +1277,24 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) ua->send_msg(_("You must set the bootstrap file to NULL to be able to specify a JobId.\n")); } goto try_again; - case 12: - /* Plugin Options */ - if (!get_cmd(ua, _("Please Plugin Options string: "))) { - break; - } - if (jcr->plugin_options) { - free(jcr->plugin_options); - jcr->plugin_options = NULL; + case 12: + + if (jcr->getJobType() == JT_RESTORE) { + plugin_config(ua, jcr, rc); + + } else { + //generate_plugin_event(jcr, bEventJobConfig, &rc); + + /* Plugin Options */ + if (!get_cmd(ua, _("Please Plugin Options string: "))) { + break; + } + if (jcr->plugin_options) { + free(jcr->plugin_options); + jcr->plugin_options = NULL; + } + jcr->plugin_options = bstrdup(ua->cmd); } - jcr->plugin_options = bstrdup(ua->cmd); goto try_again; case -1: /* error or cancel */ goto bail_out; @@ -409,29 +1307,77 @@ int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc) bail_out: return -1; - try_again: return 0; } + +/* Not a good idea to start a job with the Scratch pool. It creates all kind + * of recycling issues while the job is running. See Mantis #303 + */ +bool check_pool(int32_t JobType, int32_t JobLevel, POOL *pool, POOL *next_pool, + const char **name) +{ + if (JobType == JT_BACKUP) { + if (pool && strcmp(pool->name(), NT_("Scratch")) == 0) { + *name = NT_("Pool"); + return false; + } + } + /* The NextPool should also not be a Scratch pool */ + if (JobType == JT_MIGRATE || JobType == JT_COPY || + (JobType == JT_BACKUP && JobLevel == L_VIRTUAL_FULL)) { + if (next_pool && strcmp(next_pool->name(), NT_("Scratch")) == 0) { + *name = NT_("NextPool"); + return false; + } + } + return true; +} + /* - * Reset the restore context. + * Put the run context that we have at this point into the JCR. + * That allows us to re-ask for the run context. * This subroutine can be called multiple times, so it * must keep any prior settings. */ -static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) +static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc) { int i; jcr->verify_job = rc.verify_job; jcr->previous_job = rc.previous_job; jcr->pool = rc.pool; - if (jcr->pool != jcr->job->pool) { + jcr->next_pool = rc.next_pool; + if (rc.next_pool) { + jcr->cmdline_next_pool_override = true; + } + if (rc.pool_name) { + pm_strcpy(jcr->pool_source, _("Command input")); + } else if (jcr->pool != jcr->job->pool) { pm_strcpy(jcr->pool_source, _("User input")); } + if (rc.next_pool_name) { + pm_strcpy(jcr->next_pool_source, _("Command input")); + } else if (jcr->next_pool == jcr->job->next_pool) { + pm_strcpy(jcr->next_pool_source, _("Job resource")); + } else if (jcr->next_pool != jcr->pool->NextPool) { + pm_strcpy(jcr->next_pool_source, _("User input")); + } + set_rwstorage(jcr, rc.store); jcr->client = rc.client; - pm_strcpy(jcr->client_name, rc.client->name()); + if (jcr->client) { + pm_strcpy(jcr->client_name, rc.client->name()); + } else { + pm_strcpy(jcr->client_name, "**Dummy**"); + } + if (rc.media_type) { + if (!jcr->media_type) { + jcr->media_type = get_pool_memory(PM_NAME); + } + pm_strcpy(jcr->media_type, rc.media_type); + } jcr->fileset = rc.fileset; jcr->ExpectedFiles = rc.files; if (rc.catalog) { @@ -439,9 +1385,6 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) pm_strcpy(jcr->catalog_source, _("User input")); } - if (!jcr->comment) { - jcr->comment = get_pool_memory(PM_MESSAGE); - } pm_strcpy(jcr->comment, rc.comment); if (rc.where) { @@ -456,15 +1399,20 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) if (jcr->RegexWhere) { free(jcr->RegexWhere); } - jcr->RegexWhere = bstrdup(rc.regexwhere); + jcr->RegexWhere = bstrdup(rc.regexwhere); rc.regexwhere = NULL; } if (rc.when) { + utime_t duration; jcr->sched_time = str_to_utime(rc.when); if (jcr->sched_time == 0) { - ua->send_msg(_("Invalid time, using current time.\n")); - jcr->sched_time = time(NULL); + if (duration_to_utime(rc.when, &duration)) { + jcr->sched_time = time(NULL) + duration; + } else { + ua->send_msg(_("Invalid time, using current time.\n")); + jcr->sched_time = time(NULL); + } } rc.when = NULL; } @@ -485,6 +1433,14 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) rc.plugin_options = NULL; } + if (rc.plugin_config) { + if (jcr->plugin_config) { + free_plugin_config_items(jcr->plugin_config); + delete jcr->plugin_config; + } + jcr->plugin_config = rc.plugin_config; + rc.plugin_config = NULL; + } if (rc.replace) { jcr->replace = 0; @@ -504,6 +1460,14 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) } rc.replace = NULL; + /* Set Snapshot Retention (Job <- Client) */ + if (jcr->client) { + jcr->snapshot_retention = jcr->client->SnapRetention; + } + if (jcr->job && jcr->job->SnapRetention > 0) { + jcr->snapshot_retention = jcr->job->SnapRetention; + } + if (rc.Priority) { jcr->JobPriority = rc.Priority; rc.Priority = 0; @@ -522,17 +1486,17 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) rc.cloned = false; } - /* If pool changed, update migration write storage */ - if (jcr->getJobType() == JT_MIGRATE || jcr->getJobType() == JT_COPY || - (jcr->getJobType() == JT_BACKUP && jcr->getJobLevel() == L_VIRTUAL_FULL)) { - if (!set_migration_wstorage(jcr, rc.pool)) { + if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) || + (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) { + if (!set_mac_wstorage(ua, jcr, rc.pool, rc.next_pool, + jcr->next_pool_source)) { return false; } } rc.replace = ReplaceOptions[0].name; for (i=0; ReplaceOptions[i].name; i++) { - if (ReplaceOptions[i].token == jcr->replace) { + if (ReplaceOptions[i].token == (int)jcr->replace) { rc.replace = ReplaceOptions[i].name; } } @@ -544,10 +1508,46 @@ static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc) rc.level_name = NULL; } if (rc.jid) { - /* Note, this is also MigrateJobId */ + /* Note, this is also MigrateJobId and a VerifyJobId */ jcr->RestoreJobId = str_to_int64(rc.jid); + + /* Copy also this parameter for VirtualFull in jcr->JobIds */ + if (!jcr->JobIds) { + jcr->JobIds = get_pool_memory(PM_FNAME); + } + pm_strcpy(jcr->JobIds, rc.jid); + jcr->use_all_JobIds = rc.alljobid; /* if we found the "alljobid=" kw */ + rc.alljobid = false; rc.jid = 0; } + + /* Some options are not available through the menu + * TODO: Add an advanced menu? + */ + if (rc.spool_data_set) { + jcr->spool_data = rc.spool_data; + } + + if (rc.accurate_set) { + jcr->accurate = rc.accurate; + } + + /* Used by migration jobs that can have the same name, + * but can run at the same time + */ + if (rc.ignoreduplicatecheck_set) { + jcr->IgnoreDuplicateJobChecking = rc.ignoreduplicatecheck; + } + + /* Do not start a Backup job from the Scratch Pool */ + const char *name; + if (!check_pool(jcr->getJobType(), jcr->getJobLevel(), + rc.pool, rc.next_pool, &name)) { + ua->send_msg(_("%s \"Scratch\" not valid in Job \"%s\".\n"), + name, rc.job->name()); + return false; + } + return true; } @@ -568,19 +1568,19 @@ try_again_reg: add_prompt(ua, _("Enter a regexp")); /* 3 */ add_prompt(ua, _("Test filename manipulation")); /* 4 */ add_prompt(ua, _("Use this ?")); /* 5 */ - + switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) { case 0: /* Strip prefix */ - if (get_cmd(ua, _("Please enter path prefix to strip: "))) { + if (get_cmd(ua, _("Please enter the path prefix to strip: "))) { if (strip_prefix) bfree(strip_prefix); strip_prefix = bstrdup(ua->cmd); } - + goto try_again_reg; case 1: /* Add prefix */ - if (get_cmd(ua, _("Please enter path prefix to add (/ for none): "))) { + if (get_cmd(ua, _("Please enter the path prefix to add (/ for none): "))) { if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') { ua->cmd[0] = 0; } @@ -591,10 +1591,10 @@ try_again_reg: goto try_again_reg; case 2: /* Add suffix */ - if (get_cmd(ua, _("Please enter file suffix to add: "))) { + if (get_cmd(ua, _("Please enter the file suffix to add: "))) { if (add_suffix) bfree(add_suffix); add_suffix = bstrdup(ua->cmd); - } + } goto try_again_reg; case 3: /* Add rwhere */ @@ -602,13 +1602,13 @@ try_again_reg: if (rwhere) bfree(rwhere); rwhere = bstrdup(ua->cmd); } - - goto try_again_reg; + + goto try_again_reg; case 4: - /* Test regexp */ + /* Test regexp */ char *result; char *regexp; - + if (rwhere && rwhere[0] != '\0') { regs = get_bregexps(rwhere); ua->send_msg(_("regexwhere=%s\n"), NPRT(rwhere)); @@ -619,7 +1619,7 @@ try_again_reg: regs = get_bregexps(regexp); ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"), NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp)); - + bfree(regexp); } @@ -699,19 +1699,19 @@ static void select_job_level(UAContext *ua, JCR *jcr) // jcr->JobLevel = L_BASE; // break; case 0: - jcr->set_JobLevel(L_FULL); + jcr->setJobLevel(L_FULL); break; case 1: - jcr->set_JobLevel(L_INCREMENTAL); + jcr->setJobLevel(L_INCREMENTAL); break; case 2: - jcr->set_JobLevel(L_DIFFERENTIAL); + jcr->setJobLevel(L_DIFFERENTIAL); break; case 3: - jcr->set_JobLevel(L_SINCE); + jcr->setJobLevel(L_SINCE); break; case 4: - jcr->set_JobLevel(L_VIRTUAL_FULL); + jcr->setJobLevel(L_VIRTUAL_FULL); break; default: break; @@ -722,22 +1722,22 @@ static void select_job_level(UAContext *ua, JCR *jcr) add_prompt(ua, _("Verify Catalog")); add_prompt(ua, _("Verify Volume to Catalog")); add_prompt(ua, _("Verify Disk to Catalog")); - add_prompt(ua, _("Verify Volume Data (not yet implemented)")); + add_prompt(ua, _("Verify Volume Data")); switch (do_prompt(ua, "", _("Select level"), NULL, 0)) { case 0: - jcr->set_JobLevel(L_VERIFY_INIT); + jcr->setJobLevel(L_VERIFY_INIT); break; case 1: - jcr->set_JobLevel(L_VERIFY_CATALOG); + jcr->setJobLevel(L_VERIFY_CATALOG); break; case 2: - jcr->set_JobLevel(L_VERIFY_VOLUME_TO_CATALOG); + jcr->setJobLevel(L_VERIFY_VOLUME_TO_CATALOG); break; case 3: - jcr->set_JobLevel(L_VERIFY_DISK_TO_CATALOG); + jcr->setJobLevel(L_VERIFY_DISK_TO_CATALOG); break; case 4: - jcr->set_JobLevel(L_VERIFY_DATA); + jcr->setJobLevel(L_VERIFY_DATA); break; default: break; @@ -749,60 +1749,127 @@ static void select_job_level(UAContext *ua, JCR *jcr) } static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char *verify_list, - char *jid, const char *replace, char *client_name) + char *jid, const char *replace, char *client_name) { + char ec1[30]; + char dt[MAX_TIME_LENGTH]; + Dmsg1(800, "JobType=%c\n", jcr->getJobType()); switch (jcr->getJobType()) { - char ec1[30]; - char dt[MAX_TIME_LENGTH]; case JT_ADMIN: - if (ua->api) ua->signal(BNET_RUN_CMD); - ua->send_msg(_("Run %s job\n" + if (ua->api) { + ua->signal(BNET_RUN_CMD); + ua->send_msg("Type: Admin\n" + "Title: Run Admin Job\n" + "JobName: %s\n" + "FileSet: %s\n" + "Client: %s\n" + "Storage: %s\n" + "When: %s\n" + "Priority: %d\n", + job->name(), + jcr->fileset->name(), + NPRT(jcr->client->name()), + jcr->wstore?jcr->wstore->name():"*None*", + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->JobPriority); + } else { + ua->send_msg(_("Run Admin Job\n" "JobName: %s\n" "FileSet: %s\n" "Client: %s\n" "Storage: %s\n" "When: %s\n" "Priority: %d\n"), - _("Admin"), job->name(), jcr->fileset->name(), - NPRT(jcr->client->name()), + NPRT(jcr->client->name()), + jcr->wstore?jcr->wstore->name():"*None*", + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->JobPriority); + } + jcr->setJobLevel(L_FULL); + break; + case JT_BACKUP: + case JT_VERIFY: + char next_pool[MAX_NAME_LENGTH + 50]; + next_pool[0] = 0; + if (jcr->getJobType() == JT_BACKUP) { + if (ua->api) { + ua->signal(BNET_RUN_CMD); + if (jcr->is_JobLevel(L_VIRTUAL_FULL)) { + bsnprintf(next_pool, sizeof(next_pool), "NextPool: %s\n", + jcr->next_pool ? jcr->next_pool->name() : "*None*"); + } + ua->send_msg("Type: Backup\n" + "Title: Run Backup Job\n" + "JobName: %s\n" + "Level: %s\n" + "Client: %s\n" + "FileSet: %s\n" + "Pool: %s\n" + "%s" + "Storage: %s\n" + "When: %s\n" + "Priority: %d\n" + "%s%s%s", + job->name(), + level_to_str(jcr->getJobLevel()), + jcr->client->name(), + jcr->fileset->name(), + NPRT(jcr->pool->name()), + next_pool, jcr->wstore?jcr->wstore->name():"*None*", bstrutime(dt, sizeof(dt), jcr->sched_time), - jcr->JobPriority); - jcr->set_JobLevel(L_FULL); - break; - case JT_BACKUP: - case JT_VERIFY: - if (jcr->getJobType() == JT_BACKUP) { - if (ua->api) ua->signal(BNET_RUN_CMD); - ua->send_msg(_("Run %s job\n" + jcr->JobPriority, + jcr->plugin_options?"Plugin Options: ":"", + jcr->plugin_options?jcr->plugin_options:"", + jcr->plugin_options?"\n":""); + } else { + if (jcr->is_JobLevel(L_VIRTUAL_FULL)) { + bsnprintf(next_pool, sizeof(next_pool), + "NextPool: %s (From %s)\n", + jcr->next_pool ? jcr->next_pool->name() : "*None*", + jcr->next_pool_source); + } + ua->send_msg(_("Run Backup job\n" "JobName: %s\n" "Level: %s\n" "Client: %s\n" "FileSet: %s\n" "Pool: %s (From %s)\n" + "%s" "Storage: %s (From %s)\n" "When: %s\n" "Priority: %d\n" "%s%s%s"), - _("Backup"), job->name(), level_to_str(jcr->getJobLevel()), jcr->client->name(), jcr->fileset->name(), NPRT(jcr->pool->name()), jcr->pool_source, + next_pool, jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source, bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority, jcr->plugin_options?"Plugin Options: ":"", jcr->plugin_options?jcr->plugin_options:"", jcr->plugin_options?"\n":""); + } } else { /* JT_VERIFY */ + JOB_DBR jr; const char *Name; if (jcr->verify_job) { Name = jcr->verify_job->name(); + } else if (jcr->RestoreJobId) { /* Display job name if jobid requested */ + memset(&jr, 0, sizeof(jr)); + jr.JobId = jcr->RestoreJobId; + if (!db_get_job_record(jcr, ua->db, &jr)) { + ua->error_msg(_("Could not get job record for selected JobId. ERR=%s"), + db_strerror(ua->db)); + return false; + } + Name = jr.Job; } else { Name = ""; } @@ -812,8 +1879,32 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char if (!verify_list) { verify_list = ""; } - if (ua->api) ua->signal(BNET_RUN_CMD); - ua->send_msg(_("Run %s job\n" + if (ua->api) { + 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", + job->name(), + level_to_str(jcr->getJobLevel()), + jcr->client->name(), + jcr->fileset->name(), + NPRT(jcr->pool->name()), jcr->pool_source, + jcr->rstore->name(), jcr->rstore_source, + Name, + verify_list, + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->JobPriority); + } else { + ua->send_msg(_("Run Verify Job\n" "JobName: %s\n" "Level: %s\n" "Client: %s\n" @@ -824,7 +1915,6 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char "Verify List: %s\n" "When: %s\n" "Priority: %d\n"), - _("Verify"), job->name(), level_to_str(jcr->getJobLevel()), jcr->client->name(), @@ -835,6 +1925,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char verify_list, bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority); + } } break; case JT_RESTORE: @@ -848,13 +1939,42 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char jcr->RestoreJobId = ua->int64_val; } } - jcr->set_JobLevel(L_FULL); /* default level */ + jcr->setJobLevel(L_FULL); /* default level */ Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId); if (jcr->RestoreJobId == 0) { - if (ua->api) ua->signal(BNET_RUN_CMD); /* RegexWhere is take before RestoreWhere */ if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) { - ua->send_msg(_("Run Restore job\n" + if (ua->api) { + ua->signal(BNET_RUN_CMD); + ua->send_msg("Type: Restore\n" + "Title: Run Restore Job\n" + "JobName: %s\n" + "Bootstrap: %s\n" + "RegexWhere: %s\n" + "Replace: %s\n" + "FileSet: %s\n" + "Backup Client: %s\n" + "Restore Client: %s\n" + "Storage: %s\n" + "When: %s\n" + "Catalog: %s\n" + "Priority: %d\n" + "Plugin Options: %s\n", + job->name(), + NPRT(jcr->RestoreBootstrap), + jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere, + replace, + jcr->fileset->name(), + client_name, + jcr->client->name(), + jcr->rstore->name(), + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->catalog->name(), + jcr->JobPriority, + (jcr->plugin_config && jcr->plugin_config->size() > 0) ? + _("User specified") : _("*None*")); + } else { + ua->send_msg(_("Run Restore job\n" "JobName: %s\n" "Bootstrap: %s\n" "RegexWhere: %s\n" @@ -868,7 +1988,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char "Priority: %d\n" "Plugin Options: %s\n"), job->name(), - NPRT(jcr->RestoreBootstrap), + NPRT(jcr->RestoreBootstrap), jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere, replace, jcr->fileset->name(), @@ -878,10 +1998,41 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->catalog->name(), jcr->JobPriority, - NPRT(jcr->plugin_options)); - + (jcr->plugin_config && jcr->plugin_config->size() > 0) ? + _("User specified") : _("*None*")); + } } else { - ua->send_msg(_("Run Restore job\n" + if (ua->api) { + ua->signal(BNET_RUN_CMD); + ua->send_msg("Type: Restore\n" + "Title: Run Restore job\n" + "JobName: %s\n" + "Bootstrap: %s\n" + "Where: %s\n" + "Replace: %s\n" + "FileSet: %s\n" + "Backup Client: %s\n" + "Restore Client: %s\n" + "Storage: %s\n" + "When: %s\n" + "Catalog: %s\n" + "Priority: %d\n" + "Plugin Options: %s\n", + job->name(), + NPRT(jcr->RestoreBootstrap), + jcr->where?jcr->where:NPRT(job->RestoreWhere), + replace, + jcr->fileset->name(), + client_name, + jcr->client->name(), + jcr->rstore->name(), + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->catalog->name(), + jcr->JobPriority, + (jcr->plugin_config && jcr->plugin_config->size() > 0) ? + _("User specified") : _("*None*")); + } else { + ua->send_msg(_("Run Restore job\n" "JobName: %s\n" "Bootstrap: %s\n" "Where: %s\n" @@ -895,8 +2046,8 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char "Priority: %d\n" "Plugin Options: %s\n"), job->name(), - NPRT(jcr->RestoreBootstrap), - jcr->where?jcr->where:NPRT(job->RestoreWhere), + NPRT(jcr->RestoreBootstrap), + jcr->where?jcr->where:NPRT(job->RestoreWhere), replace, jcr->fileset->name(), client_name, @@ -905,17 +2056,20 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->catalog->name(), jcr->JobPriority, - NPRT(jcr->plugin_options)); + (jcr->plugin_config && jcr->plugin_config->size() > 0) ? + _("User specified") : _("*None*")); + } } } else { - if (ua->api) ua->signal(BNET_RUN_CMD); + /* ***FIXME*** This needs to be fixed for bat */ + if (ua->api) ua->signal(BNET_RUN_CMD); ua->send_msg(_("Run Restore job\n" "JobName: %s\n" "Bootstrap: %s\n"), job->name(), NPRT(jcr->RestoreBootstrap)); - + /* RegexWhere is take before RestoreWhere */ if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) { ua->send_msg(_("RegexWhere: %s\n"), @@ -940,25 +2094,60 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->catalog->name(), jcr->JobPriority, - NPRT(jcr->plugin_options)); - } + (jcr->plugin_config && jcr->plugin_config->size() > 0) ? + _("User specified") : _("*None*")); + } break; case JT_COPY: case JT_MIGRATE: - char *prt_type; - if (jcr->getJobType() == JT_COPY) { - prt_type = _("Run Copy job\n"); + const char *prt_type; + jcr->setJobLevel(L_FULL); /* default level */ + if (ua->api) { + ua->signal(BNET_RUN_CMD); + if (jcr->getJobType() == JT_COPY) { + prt_type = (char *)"Type: Copy\nTitle: Run Copy Job\n"; + } else { + prt_type = (char *)"Type: Migration\nTitle: Run Migration Job\n"; + } + ua->send_msg("%s" + "JobName: %s\n" + "Bootstrap: %s\n" + "Client: %s\n" + "FileSet: %s\n" + "Pool: %s\n" + "NextPool: %s\n" + "Read Storage: %s\n" + "Write Storage: %s\n" + "JobId: %s\n" + "When: %s\n" + "Catalog: %s\n" + "Priority: %d\n", + prt_type, + job->name(), + NPRT(jcr->RestoreBootstrap), + jcr->client->name(), + jcr->fileset->name(), + NPRT(jcr->pool->name()), + jcr->next_pool?jcr->next_pool->name():"*None*", + jcr->rstore->name(), + jcr->wstore?jcr->wstore->name():"*None*", + jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1), + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->catalog->name(), + jcr->JobPriority); } else { - prt_type = _("Run Migration job\n"); - } - jcr->set_JobLevel(L_FULL); /* default level */ - if (ua->api) ua->signal(BNET_RUN_CMD); - ua->send_msg("%s" + if (jcr->getJobType() == JT_COPY) { + prt_type = _("Run Copy job\n"); + } else { + prt_type = _("Run Migration job\n"); + } + ua->send_msg("%s" "JobName: %s\n" "Bootstrap: %s\n" "Client: %s\n" "FileSet: %s\n" "Pool: %s (From %s)\n" + "NextPool: %s (From %s)\n" "Read Storage: %s (From %s)\n" "Write Storage: %s (From %s)\n" "JobId: %s\n" @@ -971,12 +2160,15 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char jcr->client->name(), jcr->fileset->name(), NPRT(jcr->pool->name()), jcr->pool_source, + jcr->next_pool?jcr->next_pool->name():"*None*", + NPRT(jcr->next_pool_source), jcr->rstore->name(), jcr->rstore_source, jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source, jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1), bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->catalog->name(), jcr->JobPriority); + } break; default: ua->error_msg(_("Unknown Job Type=%d\n"), jcr->getJobType()); @@ -986,12 +2178,12 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char } -static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) +static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc) { bool kw_ok; int i, j; static const char *kw[] = { /* command line arguments */ - "job", /* Used in a switch() */ + "alljobid", /* 0 Used in a switch() */ "jobid", /* 1 */ "client", /* 2 */ "fd", @@ -1005,7 +2197,7 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) "replace", /* 11 */ "when", /* 12 */ "priority", /* 13 */ - "yes", /* 14 -- if you change this change YES_POS too */ + "yes", /* 14 -- if you change this change YES_POS too */ "verifyjob", /* 15 */ "files", /* 16 number of files to restore */ "catalog", /* 17 override catalog */ @@ -1019,6 +2211,13 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) "pluginoptions", /* 25 */ "spooldata", /* 26 */ "comment", /* 27 */ + "ignoreduplicatecheck", /* 28 */ + "accurate", /* 29 */ + "job", /* 30 */ + "mediatype", /* 31 */ + "nextpool", /* 32 override next pool name */ + "fdcalled", /* 33 */ + NULL}; #define YES_POS 14 @@ -1026,14 +2225,19 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) rc.catalog_name = NULL; rc.job_name = NULL; rc.pool_name = NULL; + rc.next_pool_name = NULL; rc.store_name = NULL; rc.client_name = NULL; + rc.media_type = NULL; rc.restore_client_name = NULL; rc.fileset_name = NULL; rc.verify_job_name = NULL; rc.previous_job_name = NULL; - rc.spool_data_set = 0; + rc.accurate_set = false; + rc.spool_data_set = false; + rc.ignoreduplicatecheck = false; rc.comment = NULL; + free_plugin_config_items(rc.plugin_config); for (i=1; iargc; i++) { Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]); @@ -1046,17 +2250,12 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]); return false; } - Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j])); + Dmsg2(800, "Got j=%d keyword=%s\n", j, NPRT(kw[j])); switch (j) { - case 0: /* job */ - if (rc.job_name) { - ua->send_msg(_("Job name specified twice.\n")); - return false; - } - rc.job_name = ua->argv[i]; - kw_ok = true; - break; - case 1: /* JobId */ + case 0: /* alljobid */ + rc.alljobid = true; + /* Fall through wanted */ + case 1: /* JobId */ if (rc.jid && !rc.mod) { ua->send_msg(_("JobId specified twice.\n")); return false; @@ -1173,22 +2372,18 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) rc.files = atoi(ua->argv[i]); kw_ok = true; break; - case 17: /* catalog */ rc.catalog_name = ua->argv[i]; kw_ok = true; break; - case 18: /* since */ rc.since = ua->argv[i]; - kw_ok = true; + kw_ok = true; break; - case 19: /* cloned */ rc. cloned = true; kw_ok = true; break; - case 20: /* write verify list output */ rc.verify_list = ua->argv[i]; kw_ok = true; @@ -1245,7 +2440,7 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) return false; } if (is_yesno(ua->argv[i], &rc.spool_data)) { - rc.spool_data_set = 1; + rc.spool_data_set = true; kw_ok = true; } else { ua->send_msg(_("Invalid spooldata flag.\n")); @@ -1254,11 +2449,64 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) case 27: /* comment */ rc.comment = ua->argv[i]; kw_ok = true; + break; + case 28: /* ignoreduplicatecheck */ + if (rc.ignoreduplicatecheck_set) { + ua->send_msg(_("IgnoreDuplicateCheck flag specified twice.\n")); + return false; + } + if (is_yesno(ua->argv[i], &rc.ignoreduplicatecheck)) { + rc.ignoreduplicatecheck_set = true; + kw_ok = true; + } else { + ua->send_msg(_("Invalid ignoreduplicatecheck flag.\n")); + } + break; + case 29: /* accurate */ + if (rc.accurate_set) { + ua->send_msg(_("Accurate flag specified twice.\n")); + return false; + } + if (is_yesno(ua->argv[i], &rc.accurate)) { + rc.accurate_set = true; + kw_ok = true; + } else { + ua->send_msg(_("Invalid accurate flag.\n")); + } + break; + case 30: /* job */ + if (rc.job_name) { + ua->send_msg(_("Job name specified twice.\n")); + return false; + } + rc.job_name = ua->argv[i]; + kw_ok = true; + break; + case 31: /* mediatype */ + if (rc.media_type) { + ua->send_msg(_("Media Type specified twice.\n")); + return false; + } + rc.media_type = ua->argv[i]; + kw_ok = true; + break; + case 32: /* Next Pool */ + if (rc.next_pool_name) { + ua->send_msg(_("NextPool specified twice.\n")); + return false; + } + rc.next_pool_name = ua->argv[i]; + kw_ok = true; + break; + case 33: /* fdcalled */ + kw_ok = true; + break; default: break; } } /* end strcase compare */ } /* end keyword loop */ + /* * End of keyword for loop -- if not found, we got a bogus keyword */ @@ -1277,7 +2525,7 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) } } } /* end argc loop */ - + Dmsg0(800, "Done scan.\n"); if (rc.comment) { if (!is_comment_legal(ua, rc.comment)) { @@ -1297,130 +2545,28 @@ static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc) } Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name)); - if (rc.job_name) { - /* Find Job */ - rc.job = GetJobResWithName(rc.job_name); - if (!rc.job) { - if (*rc.job_name != 0) { - ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name); - } - rc.job = select_job_resource(ua); - } else { - Dmsg1(800, "Found job=%s\n", rc.job_name); - } - } else if (!rc.job) { - ua->send_msg(_("A job name must be specified.\n")); - rc.job = select_job_resource(ua); - } - if (!rc.job) { - return false; - } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) { - ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name()); + if (!get_job(ua, rc)) { return false; } - if (rc.pool_name) { - rc.pool = GetPoolResWithName(rc.pool_name); - if (!rc.pool) { - if (*rc.pool_name != 0) { - ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name); - } - rc.pool = select_pool_resource(ua); - } - } else if (!rc.pool) { - rc.pool = rc.job->pool; /* use default */ - } - if (!rc.pool) { - return false; - } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) { - ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name()); + if (!get_pool(ua, rc)) { return false; } - Dmsg1(100, "Using pool %s\n", rc.pool->name()); - if (rc.spool_data_set) { - rc.job->spool_data = rc.spool_data; - } - Dmsg1(900, "Spooling data: %s\n", (rc.job->spool_data ? "Yes" : "No")); - - if (rc.store_name) { - rc.store->store = GetStoreResWithName(rc.store_name); - pm_strcpy(rc.store->store_source, _("command line")); - if (!rc.store->store) { - if (*rc.store_name != 0) { - ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name); - } - rc.store->store = select_storage_resource(ua); - pm_strcpy(rc.store->store_source, _("user selection")); - } - } else if (!rc.store->store) { - get_job_storage(rc.store, rc.job, NULL); /* use default */ - } - if (!rc.store->store) { - ua->error_msg(_("No storage specified.\n")); - return false; - } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) { - ua->error_msg(_("No authorization. Storage \"%s\".\n"), - rc.store->store->name()); + if (!get_next_pool(ua, rc)) { return false; } - Dmsg1(800, "Using storage=%s\n", rc.store->store->name()); - if (rc.client_name) { - rc.client = GetClientResWithName(rc.client_name); - if (!rc.client) { - if (*rc.client_name != 0) { - ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name); - } - rc.client = select_client_resource(ua); - } - } else if (!rc.client) { - rc.client = rc.job->client; /* use default */ - } - if (!rc.client) { - return false; - } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) { - ua->error_msg(_("No authorization. Client \"%s\".\n"), - rc.client->name()); + if (!get_storage(ua, rc)) { return false; } - Dmsg1(800, "Using client=%s\n", rc.client->name()); - if (rc.restore_client_name) { - rc.client = GetClientResWithName(rc.restore_client_name); - if (!rc.client) { - if (*rc.restore_client_name != 0) { - ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name); - } - rc.client = select_client_resource(ua); - } - } else if (!rc.client) { - rc.client = rc.job->client; /* use default */ - } - if (!rc.client) { - return false; - } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) { - ua->error_msg(_("No authorization. Client \"%s\".\n"), - rc.client->name()); + + if (!get_client(ua, rc)) { return false; } - Dmsg1(800, "Using restore client=%s\n", rc.client->name()); - - if (rc.fileset_name) { - rc.fileset = GetFileSetResWithName(rc.fileset_name); - if (!rc.fileset) { - ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name); - rc.fileset = select_fileset_resource(ua); - } - } else if (!rc.fileset) { - rc.fileset = rc.job->fileset; /* use default */ - } - if (!rc.fileset) { - return false; - } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) { - ua->send_msg(_("No authorization. FileSet \"%s\".\n"), - rc.fileset->name()); + if (!get_fileset(ua, rc)) { return false; }