2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- Run Command
22 * Kern Sibbald, December MMI
28 const char *get_command(int index);
32 char *job_name, *level_name, *jid, *store_name, *pool_name;
33 char *where, *fileset_name, *client_name, *bootstrap, *regexwhere;
34 char *restore_client_name, *comment, *media_type, *next_pool_name;
36 char *when, *verify_job_name, *catalog_name;
37 char *previous_job_name;
40 const char *verify_list;
65 int ignoreduplicatecheck;
66 bool ignoreduplicatecheck_set;
67 alist *plugin_config; /* List of all plugin_item */
69 run_ctx() { memset(this, 0, sizeof(run_ctx));
70 store = new USTORE; };
79 /* Forward referenced subroutines */
80 static void select_job_level(UAContext *ua, JCR *jcr);
81 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job,
82 const char *verify_list, char *jid, const char *replace,
84 static void select_where_regexp(UAContext *ua, JCR *jcr);
85 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc);
86 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc);
87 static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc);
88 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc);
90 /* Imported variables */
91 extern struct s_kw ReplaceOptions[];
94 * For Backup and Verify Jobs
95 * run [job=]<job-name> level=<level-name>
100 * Returns: 0 on error
104 int run_cmd(UAContext *ua, const char *cmd)
110 if (!open_client_db(ua)) {
114 if (!scan_run_command_line_arguments(ua, rc)) {
120 * Create JCR to run job. NOTE!!! after this point, free_jcr()
124 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
125 set_jcr_defaults(jcr, rc.job);
126 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
127 ua->jcr->unlink_bsr = false;
128 if (find_arg(ua, NT_("fdcalled")) > 0) {
132 /* Transfer JobIds to new restore Job */
133 if (ua->jcr->JobIds) {
135 free_pool_memory(jcr->JobIds);
137 jcr->JobIds = ua->jcr->JobIds;
138 ua->jcr->JobIds = NULL;
140 /* Transfer VSS component info */
141 if (ua->jcr->component_fname) {
142 jcr->component_fname = ua->jcr->component_fname;
143 ua->jcr->component_fname = NULL;
144 jcr->component_fd = ua->jcr->component_fd;
145 ua->jcr->component_fd = NULL;
147 /* Transfer Plugin Restore Configuration */
148 if (ua->jcr->plugin_config) {
149 jcr->plugin_config = ua->jcr->plugin_config;
150 ua->jcr->plugin_config = NULL;
153 if (!set_run_context_in_jcr(ua, jcr, rc)) {
154 break; /* error get out of while loop */
157 /* Run without prompting? */
158 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
159 return start_job(ua, jcr, rc);
163 * Prompt User to see if all run job parameters are correct, and
164 * allow him to modify them.
166 if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace,
168 break; /* error get out of while loop */
171 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
172 break; /* error get out of while loop */
175 if (strncasecmp(ua->cmd, ".mod ", 5) == 0 ||
176 (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) {
179 if (!scan_run_command_line_arguments(ua, rc)) {
180 break; /* error get out of while loop */
182 continue; /* another round with while loop */
185 /* Allow the user to modify the settings */
186 status = modify_job_parameters(ua, jcr, rc);
188 continue; /* another round with while loop */
190 if (status == -1) { /* error */
191 break; /* error get out of while loop */
194 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
195 return start_job(ua, jcr, rc);
197 if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
198 break; /* get out of while loop */
200 ua->send_msg(_("\nBad response: %s. You must answer yes, mod, or no.\n\n"), ua->cmd);
204 ua->send_msg(_("Job not run.\n"));
205 if (ua->jcr->component_fd) {
206 fclose(ua->jcr->component_fd);
207 ua->jcr->component_fd = NULL;
209 if (ua->jcr->component_fname) {
210 unlink(ua->jcr->component_fname);
211 free_and_null_pool_memory(ua->jcr->component_fname);
214 if (jcr->component_fd) {
215 fclose(jcr->component_fd);
216 jcr->component_fd = NULL;
218 if (jcr->component_fname) {
219 unlink(jcr->component_fname);
220 free_and_null_pool_memory(jcr->component_fname);
224 return 0; /* do not run */
227 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc)
232 /* Do a final check for the client, the job can change in the previous menu */
233 if (jcr->client && jcr->job) {
234 if (!acl_access_client_ok(ua, jcr->client->name(), jcr->job->JobType)) {
235 ua->error_msg(_("Job failed. Client \"%s\" not authorized on this console\n"), jcr->client->name());
241 /* Do a final check for the where/regexwhere, the job can change in the previous menu */
242 if (jcr->getJobType() == JT_RESTORE) {
243 char *p = jcr->RegexWhere ? jcr->RegexWhere : jcr->job->RegexWhere;
245 if (!acl_access_ok(ua, Where_ACL, p)) {
246 ua->error_msg(_("\"RegexWhere\" specification not authorized.\n"));
252 p = jcr->where ? jcr->where : jcr->job->RestoreWhere;
254 if (!acl_access_ok(ua, Where_ACL, p)) {
255 ua->error_msg(_("\"where\" specification not authorized.\n"));
263 /* If we use the fdcalled feature, we keep use the UA socket
264 * as a FileDaemon socket. We do not use dup_bsock() because
265 * it doesn't work, when the UA will do a free_bsock() all
266 * socket childs will be closed as well.
269 jcr->file_bsock = ua->UA_sock;
270 jcr->file_bsock->set_jcr(jcr);
272 if (rc.jr.JobStatus == JS_Incomplete) {
273 Dmsg1(100, "Ressuming JobId=%d\n", rc.jr.JobId);
274 JobId = resume_job(jcr, &rc.jr);
276 Dmsg1(100, "Starting JobId=%d\n", rc.jr.JobId);
277 JobId = run_job(jcr);
279 Dmsg4(100, "JobId=%u NewJobId=%d pool=%s priority=%d\n", (int)jcr->JobId,
280 JobId, jcr->pool->name(), jcr->JobPriority);
281 free_jcr(jcr); /* release jcr */
283 ua->error_msg(_("Job %s failed.\n"), edit_int64(rc.jr.JobId, ed1));
286 ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
289 ua->signal(BNET_FDCALLED); /* After this point, this is a new connection */
290 ua->UA_sock = new_bsock();
297 * If no job_name defined in the run context, ask
299 * Then put the job resource in the run context and
300 * check the access rights.
302 static bool get_job(UAContext *ua, run_ctx &rc)
306 rc.job = GetJobResWithName(rc.job_name);
308 if (*rc.job_name != 0) {
309 ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name);
311 rc.job = select_job_resource(ua);
313 Dmsg1(100, "Found job=%s\n", rc.job_name);
315 } else if (!rc.job) {
316 ua->send_msg(_("A job name must be specified.\n"));
317 rc.job = select_job_resource(ua);
321 } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) {
322 ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name());
329 * If no pool_name defined in the run context, ask
331 * Then put the pool resource in the run context and
332 * check the access rights.
334 static bool get_pool(UAContext *ua, run_ctx &rc)
337 rc.pool = GetPoolResWithName(rc.pool_name);
339 if (*rc.pool_name != 0) {
340 ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name);
342 rc.pool = select_pool_resource(ua);
344 } else if (!rc.pool) {
345 rc.pool = rc.job->pool; /* use default */
349 } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) {
350 ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name());
353 Dmsg1(100, "Using Pool=%s\n", rc.pool->name());
357 static bool get_next_pool(UAContext *ua, run_ctx &rc)
359 if (rc.next_pool_name) {
360 Dmsg1(100, "Have next pool=%s\n", rc.next_pool_name);
361 rc.next_pool = GetPoolResWithName(rc.next_pool_name);
363 if (*rc.next_pool_name != 0) {
364 ua->warning_msg(_("NextPool \"%s\" not found.\n"), rc.next_pool_name);
366 rc.next_pool = select_pool_resource(ua);
369 /* NextPool can come from Job resource NextPool or Pool resource NextPool */
371 if (rc.job->next_pool) {
372 rc.next_pool = rc.job->next_pool;
374 rc.next_pool = rc.pool->NextPool; /* use default */
377 if (rc.next_pool && !acl_access_ok(ua, Pool_ACL, rc.next_pool->name())) {
378 ua->error_msg(_("No authorization. NextPool \"%s\".\n"), rc.next_pool->name());
382 Dmsg1(100, "Using NextPool=%s\n", NPRT(rc.next_pool->name()));
389 * Fill in client data according to what is setup
390 * in the run context, and make sure the user
391 * has authorized access to it.
393 static bool get_client(UAContext *ua, run_ctx &rc)
395 bool authorized=false;
396 if (rc.client_name) {
397 rc.client = GetClientResWithName(rc.client_name);
399 if (*rc.client_name != 0) {
400 ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name);
402 rc.client = select_client_resource(ua, rc.job->JobType);
404 } else if (!rc.client) {
405 rc.client = rc.job->client; /* use default */
408 Dmsg1(800, "Using client=%s\n", rc.client->name());
410 if (rc.restore_client_name) {
411 rc.client = GetClientResWithName(rc.restore_client_name);
413 if (*rc.restore_client_name != 0) {
414 ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name);
416 rc.client = select_client_resource(ua, rc.job->JobType);
418 } else if (!rc.client) {
419 rc.client = rc.job->client; /* use default */
425 } else if (acl_access_client_ok(ua, rc.client->name(), rc.job->JobType)) {
429 ua->error_msg(_("No authorization. Client \"%s\".\n"),
433 Dmsg1(800, "Using restore client=%s\n", rc.client->name());
439 * Fill in fileset data according to what is setup
440 * in the run context, and make sure the user
441 * has authorized access to it.
443 static bool get_fileset(UAContext *ua, run_ctx &rc)
445 if (rc.fileset_name) {
446 rc.fileset = GetFileSetResWithName(rc.fileset_name);
448 ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
449 rc.fileset = select_fileset_resource(ua);
451 } else if (!rc.fileset) {
452 rc.fileset = rc.job->fileset; /* use default */
456 } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) {
457 ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
465 * Fill in storage data according to what is setup
466 * in the run context, and make sure the user
467 * has authorized access to it.
469 static bool get_storage(UAContext *ua, run_ctx &rc)
472 rc.store->store = GetStoreResWithName(rc.store_name);
473 pm_strcpy(rc.store->store_source, _("Command input"));
474 if (!rc.store->store) {
475 if (*rc.store_name != 0) {
476 ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
478 rc.store->store = select_storage_resource(ua);
479 pm_strcpy(rc.store->store_source, _("user selection"));
481 } else if (!rc.store->store) {
482 get_job_storage(rc.store, rc.job, NULL); /* use default */
484 if (!rc.store->store) {
485 ua->error_msg(_("No storage specified.\n"));
487 } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
488 ua->error_msg(_("No authorization. Storage \"%s\".\n"),
489 rc.store->store->name());
492 Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
497 * Get and pass back a list of Jobids in rc.jid
499 static bool get_jobid_list(UAContext *ua, sellist &sl, run_ctx &rc)
506 memset(&jr, 0, sizeof(jr));
508 /* See if any JobId is specified */
509 if ((i=find_arg(ua, "jobid")) >= 0) {
510 rc.jid = ua->argv[i];
512 ua->send_msg(_("No JobId specified.\n"));
515 if (!sl.set_string(ua->argv[i], true)) {
516 ua->send_msg("%s", sl.get_errmsg());
522 /* No JobId list give, so see if he specified a Job */
523 if ((i=find_arg(ua, "job")) >= 0) {
524 rc.job_name = ua->argv[i];
525 if (!get_job(ua, rc)) {
526 ua->send_msg(_("Invalid or no Job name specified.\n"));
531 if ((i=find_arg_with_value(ua, "limit")) >= 0) {
532 jr.limit = str_to_int64(ua->argv[i]);
535 jr.limit = 100; /* max 100 records */
539 bstrncpy(jr.Name, rc.job_name, sizeof(jr.Name));
543 jr.JobStatus = rc.jr.JobStatus;
544 Dmsg2(100, "JobStatus=%d JobName=%s\n", jr.JobStatus, jr.Name);
545 /* rc.JobIds is alist of all records found and printed */
546 rc.JobIds = db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, INCOMPLETE_JOBS);
547 if (!rc.JobIds || rc.JobIds->size()==0 ||
548 !get_selection_list(ua, sl, _("Enter the JobId list to select: "), false)) {
551 Dmsg1(100, "list=%s\n", sl.get_list());
553 * Make sure each item entered is in the JobIds list
555 while ( (JobId = sl.next()) > 0) {
556 foreach_alist(pJobId, rc.JobIds) {
557 if (JobId == str_to_int64(pJobId)) {
564 ua->error_msg(_("JobId=%d entered is not in the list.\n"), JobId);
568 sl.begin(); /* reset to walk list again */
573 static bool get_jobid_from_list(UAContext *ua, sellist &sl, run_ctx &rc)
580 if ((JobId = sl.next()) < 0) {
581 Dmsg1(100, "sl.next()=%d\n", JobId);
585 rc.jr.JobId = rc.JobId = JobId;
586 Dmsg1(100, "Next JobId=%d\n", rc.JobId);
587 if (!db_get_job_record(ua->jcr, ua->db, &rc.jr)) {
588 ua->error_msg(_("Could not get job record for selected JobId=%d. ERR=%s"),
589 rc.JobId, db_strerror(ua->db));
592 Dmsg3(100, "Job=%s JobId=%d JobStatus=%c\n", rc.jr.Name, rc.jr.JobId,
594 rc.job_name = rc.jr.Name;
595 if (!get_job(ua, rc)) {
598 if (!get_pool(ua, rc)) {
601 get_job_storage(rc.store, rc.job, NULL);
602 rc.client_name = rc.job->client->hdr.name;
603 if (!get_client(ua, rc)) {
606 if (!get_fileset(ua, rc)) {
609 if (!get_storage(ua, rc)) {
616 * Restart Canceled, Failed, or Incomplete Jobs
618 * Returns: 0 on error
622 int restart_cmd(UAContext *ua, const char *cmd)
630 const char *status_name;
634 {"Incomplete", JS_Incomplete},
635 {"Canceled", JS_Canceled},
636 {"Failed", JS_FatalError},
641 if (!open_client_db(ua)) {
646 for (i=1; i<ua->argc; i++) {
647 for (j=0; kw[j].status_name; j++) {
648 if (strcasecmp(ua->argk[i], kw[j].status_name) == 0) {
649 rc.jr.JobStatus = kw[j].job_status;
655 if (!got_kw) { /* Must prompt user */
656 start_prompt(ua, _("You have the following choices:\n"));
657 for (i=0; kw[i].status_name; i++) {
658 add_prompt(ua, kw[i].status_name);
660 i = do_prompt(ua, NULL, _("Select termination code: "), NULL, 0);
664 rc.jr.JobStatus = kw[i].job_status;
667 /* type now has what job termination code we want to look at */
668 Dmsg1(100, "Termination code=%c\n", rc.jr.JobStatus);
670 /* Get a list of JobIds to restore */
671 if (!get_jobid_list(ua, sl, rc)) {
673 rc.JobIds->destroy();
677 Dmsg1(100, "list=%s\n", sl.get_list());
679 while (get_jobid_from_list(ua, sl, rc)) {
681 * Create JCR to run job. NOTE!!! after this point, free_jcr()
685 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
686 set_jcr_defaults(jcr, rc.job);
687 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
688 ua->jcr->unlink_bsr = false;
691 if (!set_run_context_in_jcr(ua, jcr, rc)) {
694 start_job(ua, jcr, rc);
702 rc.JobIds->destroy();
704 return 0; /* do not run */
709 * Plugin restore option part
712 /* Free a plugin_config_item */
713 void free_plugin_config_item(plugin_config_item *elt)
715 free(elt->plugin_name);
716 free_pool_memory(elt->content);
720 /* Free a list of plugins (do not free the list itself) */
721 void free_plugin_config_items(alist *lst)
723 plugin_config_item *elt;
729 foreach_alist(elt, lst) {
730 free_plugin_config_item(elt);
734 /* Structure used in the sql query to get configuration restore objects */
735 struct plugin_config_handler_t
737 UAContext *ua; /* UAContext for user input */
738 POOLMEM *tmp; /* Used to store the config object */
739 alist *plugins; /* Configuration plugin list */
740 alist *content; /* Temp file used by each plugin */
743 /* DB handler to get all configuration restore objects for a given
746 static int plugin_config_handler(void *ctx, int num_fields, char **row)
748 struct plugin_config_handler_t *pch = (struct plugin_config_handler_t *)ctx;
749 UAContext *ua = pch->ua;
754 db_unescape_object(jcr, ua->db,
756 str_to_uint64(row[1]), /* Object length */
759 /* Is compressed ? */
760 if (str_to_int64(row[5]) > 0) {
761 int full_len = str_to_int64(row[2]);
762 int out_len = full_len + 100; /* full length */
763 char *obj = (char *)malloc(out_len);
764 Zinflate(pch->tmp, len, obj, out_len); /* out_len is updated */
765 if (out_len != full_len) {
766 ua->error_msg(_("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
767 full_len, out_len, row[9]);
770 pch->content->append(obj);
774 pch->content->append(bstrdup(pch->tmp));
777 pch->plugins->append(bstrdup(row[9]));
781 /* Save a Plugin Config object (ConfigFile) inside the JCR
782 * using a list of plugin_config_item
784 * We allow only one Plugin Config object per Plugin
786 static void plugin_config_save_jcr(UAContext *ua, JCR *jcr,
787 char *pname, ConfigFile *ini)
789 plugin_config_item *elt;
790 if (!jcr->plugin_config) {
791 jcr->plugin_config = New(alist(5, not_owned_by_alist));
794 /* Store only one Plugin Config object per plugin command */
795 for (int i = 0; i < jcr->plugin_config->size() ; i++) {
796 elt = (plugin_config_item *) jcr->plugin_config->get(i);
797 if (strcmp(elt->plugin_name, pname) == 0) {
798 jcr->plugin_config->remove(i);
799 free_plugin_config_item(elt);
804 elt = (plugin_config_item *) malloc (sizeof(plugin_config_item));
805 elt->plugin_name = bstrdup(pname);
806 elt->content = get_pool_memory(PM_FNAME);
807 ini->dump_results(&elt->content);
808 jcr->plugin_config->append(elt);
811 /* TODO: Allow to have sub-menus Advanced.restore_mode can be
812 * in a Advanced panel (sub menu)
815 /* Take the ConfigIni struture and display user menu for a given plugin */
816 static int plugin_display_options(UAContext *ua, JCR *jcr, ConfigFile *ini)
820 POOL_MEM prompt, tmp;
824 /* TODO: See how to work in API mode
826 ua->signal(BNET_RUN_CMD);
830 /* Take a look in the plugin_config list to see if we have something to
833 if (jcr->plugin_config) {
834 plugin_config_item *item=NULL;
836 for (jcr_pos = 0; jcr_pos < jcr->plugin_config->size() ; jcr_pos++) {
837 item = (plugin_config_item *)jcr->plugin_config->get(jcr_pos);
839 if (strcmp(item->plugin_name, ini->plugin_name) == 0) /* bpipe:xxx:yyyy */
841 if (!ini->dump_string(item->content, strlen(item->content)) ||
842 !ini->parse(ini->out_fname))
844 ua->error_msg(_("Unable to use current plugin configuration, "
847 /* When we are here, we can type yes (it will add it back), or no
848 * to not use this plugin configuration. So, don't keep it in the
851 jcr->plugin_config->remove(jcr_pos);
852 free_plugin_config_item(item);
859 ua->send_msg(_("Plugin Restore Options\n"));
861 for (nb=0; ini->items[nb].name; nb++) {
863 if (ini->items[nb].found) {
864 /* When calling the handler, It will convert the value
865 * to a string representation in ini->edit
867 ini->items[nb].handler(NULL, ini, &ini->items[nb]);
869 if (ini->items[nb].required) {
870 pm_strcpy(ini->edit, _("*None, but required*"));
873 pm_strcpy(ini->edit, _("*None*"));
877 Mmsg(tmp, "%s:", ini->items[nb].name);
879 Mmsg(prompt, "%-20s %-20s ",
880 tmp.c_str(), ini->edit);
882 if (ini->items[nb].default_value) {
883 Mmsg(tmp, "(%s)", ini->items[nb].default_value);
884 pm_strcat(prompt, tmp.c_str());
887 ua->send_msg("%s\n", prompt.c_str());
890 if (!get_cmd(ua, _("Use above plugin configuration? (yes/mod/no): "))) {
895 /* '', 'y', 'ye', and 'yes' are valid */
896 if (strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
900 if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
905 /* When using "mod", we display the list of parameters with their
906 * comments, and we let the user choose one entry to modify
908 if (strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
909 start_prompt(ua, _("You have the following choices:\n"));
911 for (nb=0; ini->items[nb].name; nb++) {
913 if (ini->items[nb].comment) {
914 Mmsg(tmp, " (%s)", ini->items[nb].comment);
919 Mmsg(prompt, "%s%s ",
920 ini->items[nb].name, tmp.c_str());
922 add_prompt(ua, prompt.c_str());
925 i = do_prompt(ua, NULL, _("Select parameter to modify"), NULL, 0);
932 Mmsg(prompt, _("Please enter a value for %s: "), ini->items[i].name);
934 /* Now use the handler to know how to ask the value to the user.
935 * For example, boolean will use get_yes_no(), pint32 will use get_pint()
937 h = ini->items[i].handler;
938 if (h == ini_store_int32 ||
939 h == ini_store_pint32) {
940 found = ini->items[i].found = get_pint(ua, prompt.c_str());
942 ini->items[i].val.int32val = ua->pint32_val;
945 } else if (h == ini_store_bool) {
946 found = ini->items[i].found = get_yesno(ua, prompt.c_str());
948 ini->items[i].val.boolval = ua->pint32_val;
951 } else if (h == ini_store_name) {
952 found = ini->items[i].found = get_cmd(ua, prompt.c_str());
954 strncpy(ini->items[i].val.nameval, ua->cmd, MAX_NAME_LENGTH -1);
955 ini->items[i].val.nameval[MAX_NAME_LENGTH - 1] = 0;
958 } else if (h == ini_store_str) {
959 found = ini->items[i].found = get_cmd(ua, prompt.c_str());
961 ini->items[i].val.strval = bstrdup(ua->cmd);
964 } else if (h == ini_store_int64 ||
965 h == ini_store_pint64) {
966 found = ini->items[i].found = get_pint(ua, prompt.c_str());
968 ini->items[i].val.int64val = ua->int64_val;
971 goto configure_again;
974 return 1; /* never reached */
977 /* Display a menu with all plugins */
978 static void plugin_config(UAContext *ua, JCR *jcr, run_ctx &rc)
982 ConfigFile *ini = NULL;
984 struct plugin_config_handler_t pch;
986 /* No jobids for this restore, probably wrong */
987 if (!jcr->JobIds || !jcr->JobIds[0]) {
991 if (!open_client_db(ua)) {
996 query = get_pool_memory(PM_FNAME);
997 pch.tmp = get_pool_memory(PM_MESSAGE);
998 pch.plugins = New(alist(10, owned_by_alist));
999 pch.content = New(alist(10, owned_by_alist));
1001 /* Get all RestoreObject PLUGIN_CONFIG for the given Job */
1002 Mmsg(query, get_restore_objects, jcr->JobIds, FT_PLUGIN_CONFIG);
1003 db_sql_query(ua->db, query, plugin_config_handler, &pch);
1005 if (!pch.plugins || pch.plugins->size() == 0) {
1006 ua->info_msg(_("No plugin to configure\n"));
1010 /* TODO: Let see if we want to configure plugins that were given in command
1014 start_prompt(ua, _("Plugins to configure:\n"));
1017 foreach_alist(elt, pch.plugins) {
1019 pm_strcpy(query, elt);
1020 add_prompt(ua, query);
1023 i = do_prompt(ua, "", _("Select plugin to configure"), NULL, 0);
1030 elt = (char *)pch.plugins->get(i);
1031 ini = new ConfigFile();
1032 /* Try to read the plugin configuration, if error, loop to configure
1033 * something else, or bail_out
1035 tmp = (char *)pch.content->get(i);
1036 if (!ini->dump_string(tmp, strlen(tmp)) || /* Send the string to a file */
1037 !ini->unserialize(ini->out_fname)) { /* Read the file to initialize the ConfigFile */
1039 ua->error_msg(_("Can't configure %32s\n"), elt);
1043 ini->set_plugin_name(elt);
1045 if (plugin_display_options(ua, jcr, ini)) {
1046 ini->dump_results(&query);
1047 Dmsg1(50, "plugin: %s\n", query);
1049 /* Save the plugin somewhere in the JCR */
1050 plugin_config_save_jcr(ua, jcr, elt, ini);
1054 free_pool_memory(pch.tmp);
1055 free_pool_memory(query);
1063 int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc)
1068 * At user request modify parameters of job to be run.
1070 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0){
1073 start_prompt(ua, _("Parameters to modify:\n"));
1074 add_prompt(ua, _("Level")); /* 0 */
1075 add_prompt(ua, _("Storage")); /* 1 */
1076 add_prompt(ua, _("Job")); /* 2 */
1077 add_prompt(ua, _("FileSet")); /* 3 */
1078 if (jcr->getJobType() == JT_RESTORE) {
1079 add_prompt(ua, _("Restore Client")); /* 4 */
1081 add_prompt(ua, _("Client")); /* 4 */
1083 add_prompt(ua, _("When")); /* 5 */
1084 add_prompt(ua, _("Priority")); /* 6 */
1085 if (jcr->getJobType() == JT_BACKUP ||
1086 jcr->getJobType() == JT_COPY ||
1087 jcr->getJobType() == JT_MIGRATE ||
1088 jcr->getJobType() == JT_VERIFY) {
1089 add_prompt(ua, _("Pool")); /* 7 */
1090 if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */
1091 jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
1092 jcr->getJobType() == JT_COPY ||
1093 jcr->getJobType() == JT_MIGRATE) {
1094 add_prompt(ua, _("NextPool")); /* 8 */
1095 } else if (jcr->getJobType() == JT_VERIFY) {
1096 add_prompt(ua, _("Verify Job")); /* 8 */
1098 } else if (jcr->getJobType() == JT_RESTORE) {
1099 add_prompt(ua, _("Bootstrap")); /* 7 */
1100 add_prompt(ua, _("Where")); /* 8 */
1101 add_prompt(ua, _("File Relocation"));/* 9 */
1102 add_prompt(ua, _("Replace")); /* 10 */
1103 add_prompt(ua, _("JobId")); /* 11 */
1105 if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE) {
1106 add_prompt(ua, _("Plugin Options")); /* 12 */
1108 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1111 select_job_level(ua, jcr);
1115 rc.store->store = select_storage_resource(ua);
1116 if (rc.store->store) {
1117 pm_strcpy(rc.store->store_source, _("user selection"));
1118 set_rwstorage(jcr, rc.store);
1124 rc.job = select_job_resource(ua);
1127 set_jcr_defaults(jcr, rc.job);
1133 rc.fileset = select_fileset_resource(ua);
1135 jcr->fileset = rc.fileset;
1142 int32_t jt = rc.job ? rc.job->JobType : JT_SYSTEM;
1143 rc.client = select_client_resource(ua, jt);
1145 jcr->client = rc.client;
1152 if (!get_cmd(ua, _("Please enter start time as a duration or YYYY-MM-DD HH:MM:SS or return for now: "))) {
1155 if (ua->cmd[0] == 0) {
1156 jcr->sched_time = time(NULL);
1159 jcr->sched_time = str_to_utime(ua->cmd);
1160 if (jcr->sched_time == 0) {
1161 if (duration_to_utime(ua->cmd, &duration)) {
1162 jcr->sched_time = time(NULL) + duration;
1164 ua->send_msg(_("Invalid time, using current time.\n"));
1165 jcr->sched_time = time(NULL);
1173 if (!get_pint(ua, _("Enter new Priority: "))) {
1176 if (ua->pint32_val == 0) {
1177 ua->send_msg(_("Priority must be a positive integer.\n"));
1179 jcr->JobPriority = ua->pint32_val;
1183 /* Pool or Bootstrap depending on JobType */
1184 if (jcr->getJobType() == JT_BACKUP ||
1185 jcr->getJobType() == JT_COPY ||
1186 jcr->getJobType() == JT_MIGRATE ||
1187 jcr->getJobType() == JT_VERIFY) { /* Pool */
1188 rc.pool = select_pool_resource(ua);
1190 jcr->pool = rc.pool;
1191 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
1198 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
1201 if (jcr->RestoreBootstrap) {
1202 free(jcr->RestoreBootstrap);
1203 jcr->RestoreBootstrap = NULL;
1205 if (ua->cmd[0] != 0) {
1206 jcr->RestoreBootstrap = bstrdup(ua->cmd);
1207 fd = bfopen(jcr->RestoreBootstrap, "rb");
1210 ua->send_msg(_("Warning cannot open %s: ERR=%s\n"),
1211 jcr->RestoreBootstrap, be.bstrerror());
1212 free(jcr->RestoreBootstrap);
1213 jcr->RestoreBootstrap = NULL;
1220 /* Specify Next Pool */
1221 if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */
1222 jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
1223 jcr->getJobType() == JT_COPY ||
1224 jcr->getJobType() == JT_MIGRATE) {
1225 rc.next_pool = select_pool_resource(ua);
1227 jcr->next_pool = rc.next_pool;
1232 if (jcr->getJobType() == JT_VERIFY) {
1233 rc.verify_job = select_job_resource(ua);
1234 if (rc.verify_job) {
1235 jcr->verify_job = rc.verify_job;
1240 if (!get_cmd(ua, _("Please enter the full path prefix for restore (/ for none): "))) {
1243 if (jcr->RegexWhere) { /* cannot use regexwhere and where */
1244 free(jcr->RegexWhere);
1245 jcr->RegexWhere = NULL;
1251 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1254 jcr->where = bstrdup(ua->cmd);
1257 /* File relocation */
1258 select_where_regexp(ua, jcr);
1262 start_prompt(ua, _("Replace:\n"));
1263 for (i=0; ReplaceOptions[i].name; i++) {
1264 add_prompt(ua, ReplaceOptions[i].name);
1266 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
1268 rc.replace = ReplaceOptions[opt].name;
1269 jcr->replace = ReplaceOptions[opt].token;
1274 rc.jid = NULL; /* force reprompt */
1275 jcr->RestoreJobId = 0;
1276 if (jcr->RestoreBootstrap) {
1277 ua->send_msg(_("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
1282 if (jcr->getJobType() == JT_RESTORE) {
1283 plugin_config(ua, jcr, rc);
1286 //generate_plugin_event(jcr, bEventJobConfig, &rc);
1288 /* Plugin Options */
1289 if (!get_cmd(ua, _("Please Plugin Options string: "))) {
1292 if (jcr->plugin_options) {
1293 free(jcr->plugin_options);
1294 jcr->plugin_options = NULL;
1296 jcr->plugin_options = bstrdup(ua->cmd);
1299 case -1: /* error or cancel */
1315 /* Not a good idea to start a job with the Scratch pool. It creates all kind
1316 * of recycling issues while the job is running. See Mantis #303
1318 bool check_pool(int32_t JobType, int32_t JobLevel, POOL *pool, POOL *next_pool,
1321 if (JobType == JT_BACKUP) {
1322 if (pool && strcmp(pool->name(), NT_("Scratch")) == 0) {
1323 *name = NT_("Pool");
1327 /* The NextPool should also not be a Scratch pool */
1328 if (JobType == JT_MIGRATE || JobType == JT_COPY ||
1329 (JobType == JT_BACKUP && JobLevel == L_VIRTUAL_FULL)) {
1330 if (next_pool && strcmp(next_pool->name(), NT_("Scratch")) == 0) {
1331 *name = NT_("NextPool");
1339 * Put the run context that we have at this point into the JCR.
1340 * That allows us to re-ask for the run context.
1341 * This subroutine can be called multiple times, so it
1342 * must keep any prior settings.
1344 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc)
1348 jcr->verify_job = rc.verify_job;
1349 jcr->previous_job = rc.previous_job;
1350 jcr->pool = rc.pool;
1351 jcr->next_pool = rc.next_pool;
1353 jcr->cmdline_next_pool_override = true;
1356 pm_strcpy(jcr->pool_source, _("Command input"));
1357 } else if (jcr->pool != jcr->job->pool) {
1358 pm_strcpy(jcr->pool_source, _("User input"));
1360 if (rc.next_pool_name) {
1361 pm_strcpy(jcr->next_pool_source, _("Command input"));
1362 } else if (jcr->next_pool == jcr->job->next_pool) {
1363 pm_strcpy(jcr->next_pool_source, _("Job resource"));
1364 } else if (jcr->next_pool != jcr->pool->NextPool) {
1365 pm_strcpy(jcr->next_pool_source, _("User input"));
1368 set_rwstorage(jcr, rc.store);
1369 jcr->client = rc.client;
1371 pm_strcpy(jcr->client_name, rc.client->name());
1373 pm_strcpy(jcr->client_name, "**Dummy**");
1375 if (rc.media_type) {
1376 if (!jcr->media_type) {
1377 jcr->media_type = get_pool_memory(PM_NAME);
1379 pm_strcpy(jcr->media_type, rc.media_type);
1381 jcr->fileset = rc.fileset;
1382 jcr->ExpectedFiles = rc.files;
1384 jcr->catalog = rc.catalog;
1385 pm_strcpy(jcr->catalog_source, _("User input"));
1388 pm_strcpy(jcr->comment, rc.comment);
1394 jcr->where = bstrdup(rc.where);
1398 if (rc.regexwhere) {
1399 if (jcr->RegexWhere) {
1400 free(jcr->RegexWhere);
1402 jcr->RegexWhere = bstrdup(rc.regexwhere);
1403 rc.regexwhere = NULL;
1408 jcr->sched_time = str_to_utime(rc.when);
1409 if (jcr->sched_time == 0) {
1410 if (duration_to_utime(rc.when, &duration)) {
1411 jcr->sched_time = time(NULL) + duration;
1413 ua->send_msg(_("Invalid time, using current time.\n"));
1414 jcr->sched_time = time(NULL);
1421 if (jcr->RestoreBootstrap) {
1422 free(jcr->RestoreBootstrap);
1424 jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
1425 rc.bootstrap = NULL;
1428 if (rc.plugin_options) {
1429 if (jcr->plugin_options) {
1430 free(jcr->plugin_options);
1432 jcr->plugin_options = bstrdup(rc.plugin_options);
1433 rc.plugin_options = NULL;
1436 if (rc.plugin_config) {
1437 if (jcr->plugin_config) {
1438 free_plugin_config_items(jcr->plugin_config);
1439 delete jcr->plugin_config;
1441 jcr->plugin_config = rc.plugin_config;
1442 rc.plugin_config = NULL;
1447 for (i=0; ReplaceOptions[i].name; i++) {
1448 if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
1449 jcr->replace = ReplaceOptions[i].token;
1452 if (!jcr->replace) {
1453 ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
1456 } else if (rc.job->replace) {
1457 jcr->replace = rc.job->replace;
1459 jcr->replace = REPLACE_ALWAYS;
1463 /* Set Snapshot Retention (Job <- Client) */
1465 jcr->snapshot_retention = jcr->client->SnapRetention;
1467 if (jcr->job && jcr->job->SnapRetention > 0) {
1468 jcr->snapshot_retention = jcr->job->SnapRetention;
1472 jcr->JobPriority = rc.Priority;
1478 jcr->stime = get_pool_memory(PM_MESSAGE);
1480 pm_strcpy(jcr->stime, rc.since);
1485 jcr->cloned = rc.cloned;
1489 /* If pool changed, update migration write storage */
1490 if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
1491 (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) {
1492 if (!set_mac_wstorage(ua, jcr, rc.pool, rc.next_pool,
1493 jcr->next_pool_source)) {
1497 rc.replace = ReplaceOptions[0].name;
1498 for (i=0; ReplaceOptions[i].name; i++) {
1499 if (ReplaceOptions[i].token == (int)jcr->replace) {
1500 rc.replace = ReplaceOptions[i].name;
1503 if (rc.level_name) {
1504 if (!get_level_from_name(jcr, rc.level_name)) {
1505 ua->send_msg(_("Level \"%s\" not valid.\n"), rc.level_name);
1508 rc.level_name = NULL;
1511 /* Note, this is also MigrateJobId and a VerifyJobId */
1512 jcr->RestoreJobId = str_to_int64(rc.jid);
1514 /* Copy also this parameter for VirtualFull in jcr->JobIds */
1516 jcr->JobIds = get_pool_memory(PM_FNAME);
1518 pm_strcpy(jcr->JobIds, rc.jid);
1519 jcr->use_all_JobIds = rc.alljobid; /* if we found the "alljobid=" kw */
1520 rc.alljobid = false;
1524 /* Some options are not available through the menu
1525 * TODO: Add an advanced menu?
1527 if (rc.spool_data_set) {
1528 jcr->spool_data = rc.spool_data;
1531 if (rc.accurate_set) {
1532 jcr->accurate = rc.accurate;
1535 /* Used by migration jobs that can have the same name,
1536 * but can run at the same time
1538 if (rc.ignoreduplicatecheck_set) {
1539 jcr->IgnoreDuplicateJobChecking = rc.ignoreduplicatecheck;
1542 /* Do not start a Backup job from the Scratch Pool */
1544 if (!check_pool(jcr->getJobType(), jcr->getJobLevel(),
1545 rc.pool, rc.next_pool, &name)) {
1546 ua->send_msg(_("%s \"Scratch\" not valid in Job \"%s\".\n"),
1547 name, rc.job->name());
1554 static void select_where_regexp(UAContext *ua, JCR *jcr)
1557 char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
1558 strip_prefix = add_suffix = rwhere = add_prefix = NULL;
1561 ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
1562 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
1564 start_prompt(ua, _("This will replace your current Where value\n"));
1565 add_prompt(ua, _("Strip prefix")); /* 0 */
1566 add_prompt(ua, _("Add prefix")); /* 1 */
1567 add_prompt(ua, _("Add file suffix")); /* 2 */
1568 add_prompt(ua, _("Enter a regexp")); /* 3 */
1569 add_prompt(ua, _("Test filename manipulation")); /* 4 */
1570 add_prompt(ua, _("Use this ?")); /* 5 */
1572 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1575 if (get_cmd(ua, _("Please enter the path prefix to strip: "))) {
1576 if (strip_prefix) bfree(strip_prefix);
1577 strip_prefix = bstrdup(ua->cmd);
1583 if (get_cmd(ua, _("Please enter the path prefix to add (/ for none): "))) {
1584 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1588 if (add_prefix) bfree(add_prefix);
1589 add_prefix = bstrdup(ua->cmd);
1594 if (get_cmd(ua, _("Please enter the file suffix to add: "))) {
1595 if (add_suffix) bfree(add_suffix);
1596 add_suffix = bstrdup(ua->cmd);
1601 if (get_cmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
1602 if (rwhere) bfree(rwhere);
1603 rwhere = bstrdup(ua->cmd);
1612 if (rwhere && rwhere[0] != '\0') {
1613 regs = get_bregexps(rwhere);
1614 ua->send_msg(_("regexwhere=%s\n"), NPRT(rwhere));
1616 int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1617 regexp = (char *) bmalloc (len * sizeof(char));
1618 bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
1619 regs = get_bregexps(regexp);
1620 ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
1621 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp));
1627 ua->send_msg(_("Cannot use your regexp\n"));
1630 ua->send_msg(_("Enter a period (.) to stop this test\n"));
1631 while (get_cmd(ua, _("Please enter filename to test: "))) {
1632 apply_bregexps(ua->cmd, regs, &result);
1633 ua->send_msg(_("%s -> %s\n"), ua->cmd, result);
1635 free_bregexps(regs);
1642 case -1: /* error or cancel */
1648 /* replace the existing where */
1654 /* replace the existing regexwhere */
1655 if (jcr->RegexWhere) {
1656 bfree(jcr->RegexWhere);
1657 jcr->RegexWhere = NULL;
1661 jcr->RegexWhere = bstrdup(rwhere);
1662 } else if (strip_prefix || add_prefix || add_suffix) {
1663 int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1664 jcr->RegexWhere = (char *) bmalloc(len*sizeof(char));
1665 bregexp_build_where(jcr->RegexWhere, len, strip_prefix, add_prefix, add_suffix);
1668 regs = get_bregexps(jcr->RegexWhere);
1670 free_bregexps(regs);
1673 if (jcr->RegexWhere) {
1674 bfree(jcr->RegexWhere);
1675 jcr->RegexWhere = NULL;
1677 ua->send_msg(_("Cannot use your regexp.\n"));
1681 if (strip_prefix) bfree(strip_prefix);
1682 if (add_prefix) bfree(add_prefix);
1683 if (add_suffix) bfree(add_suffix);
1684 if (rwhere) bfree(rwhere);
1687 static void select_job_level(UAContext *ua, JCR *jcr)
1689 if (jcr->getJobType() == JT_BACKUP) {
1690 start_prompt(ua, _("Levels:\n"));
1691 // add_prompt(ua, _("Base"));
1692 add_prompt(ua, _("Full"));
1693 add_prompt(ua, _("Incremental"));
1694 add_prompt(ua, _("Differential"));
1695 add_prompt(ua, _("Since"));
1696 add_prompt(ua, _("VirtualFull"));
1697 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
1699 // jcr->JobLevel = L_BASE;
1702 jcr->setJobLevel(L_FULL);
1705 jcr->setJobLevel(L_INCREMENTAL);
1708 jcr->setJobLevel(L_DIFFERENTIAL);
1711 jcr->setJobLevel(L_SINCE);
1714 jcr->setJobLevel(L_VIRTUAL_FULL);
1719 } else if (jcr->getJobType() == JT_VERIFY) {
1720 start_prompt(ua, _("Levels:\n"));
1721 add_prompt(ua, _("Initialize Catalog"));
1722 add_prompt(ua, _("Verify Catalog"));
1723 add_prompt(ua, _("Verify Volume to Catalog"));
1724 add_prompt(ua, _("Verify Disk to Catalog"));
1725 add_prompt(ua, _("Verify Volume Data"));
1726 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
1728 jcr->setJobLevel(L_VERIFY_INIT);
1731 jcr->setJobLevel(L_VERIFY_CATALOG);
1734 jcr->setJobLevel(L_VERIFY_VOLUME_TO_CATALOG);
1737 jcr->setJobLevel(L_VERIFY_DISK_TO_CATALOG);
1740 jcr->setJobLevel(L_VERIFY_DATA);
1746 ua->warning_msg(_("Level not appropriate for this Job. Cannot be changed.\n"));
1751 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char *verify_list,
1752 char *jid, const char *replace, char *client_name)
1755 char dt[MAX_TIME_LENGTH];
1757 Dmsg1(800, "JobType=%c\n", jcr->getJobType());
1758 switch (jcr->getJobType()) {
1761 ua->signal(BNET_RUN_CMD);
1762 ua->send_msg("Type: Admin\n"
1763 "Title: Run Admin Job\n"
1771 jcr->fileset->name(),
1772 NPRT(jcr->client->name()),
1773 jcr->wstore?jcr->wstore->name():"*None*",
1774 bstrutime(dt, sizeof(dt), jcr->sched_time),
1777 ua->send_msg(_("Run Admin Job\n"
1785 jcr->fileset->name(),
1786 NPRT(jcr->client->name()),
1787 jcr->wstore?jcr->wstore->name():"*None*",
1788 bstrutime(dt, sizeof(dt), jcr->sched_time),
1791 jcr->setJobLevel(L_FULL);
1795 char next_pool[MAX_NAME_LENGTH + 50];
1797 if (jcr->getJobType() == JT_BACKUP) {
1799 ua->signal(BNET_RUN_CMD);
1800 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1801 bsnprintf(next_pool, sizeof(next_pool), "NextPool: %s\n",
1802 jcr->next_pool ? jcr->next_pool->name() : "*None*");
1804 ua->send_msg("Type: Backup\n"
1805 "Title: Run Backup Job\n"
1817 level_to_str(jcr->getJobLevel()),
1818 jcr->client->name(),
1819 jcr->fileset->name(),
1820 NPRT(jcr->pool->name()),
1822 jcr->wstore?jcr->wstore->name():"*None*",
1823 bstrutime(dt, sizeof(dt), jcr->sched_time),
1825 jcr->plugin_options?"Plugin Options: ":"",
1826 jcr->plugin_options?jcr->plugin_options:"",
1827 jcr->plugin_options?"\n":"");
1829 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1830 bsnprintf(next_pool, sizeof(next_pool),
1831 "NextPool: %s (From %s)\n",
1832 jcr->next_pool ? jcr->next_pool->name() : "*None*",
1833 jcr->next_pool_source);
1835 ua->send_msg(_("Run Backup job\n"
1840 "Pool: %s (From %s)\n"
1842 "Storage: %s (From %s)\n"
1847 level_to_str(jcr->getJobLevel()),
1848 jcr->client->name(),
1849 jcr->fileset->name(),
1850 NPRT(jcr->pool->name()), jcr->pool_source,
1852 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1853 bstrutime(dt, sizeof(dt), jcr->sched_time),
1855 jcr->plugin_options?"Plugin Options: ":"",
1856 jcr->plugin_options?jcr->plugin_options:"",
1857 jcr->plugin_options?"\n":"");
1859 } else { /* JT_VERIFY */
1862 if (jcr->verify_job) {
1863 Name = jcr->verify_job->name();
1864 } else if (jcr->RestoreJobId) { /* Display job name if jobid requested */
1865 memset(&jr, 0, sizeof(jr));
1866 jr.JobId = jcr->RestoreJobId;
1867 if (!db_get_job_record(jcr, ua->db, &jr)) {
1868 ua->error_msg(_("Could not get job record for selected JobId. ERR=%s"),
1869 db_strerror(ua->db));
1877 verify_list = job->WriteVerifyList;
1883 ua->signal(BNET_RUN_CMD);
1884 ua->send_msg("Type: Verify\n"
1885 "Title: Run Verify Job\n"
1890 "Pool: %s (From %s)\n"
1891 "Storage: %s (From %s)\n"
1897 level_to_str(jcr->getJobLevel()),
1898 jcr->client->name(),
1899 jcr->fileset->name(),
1900 NPRT(jcr->pool->name()), jcr->pool_source,
1901 jcr->rstore->name(), jcr->rstore_source,
1904 bstrutime(dt, sizeof(dt), jcr->sched_time),
1907 ua->send_msg(_("Run Verify Job\n"
1912 "Pool: %s (From %s)\n"
1913 "Storage: %s (From %s)\n"
1919 level_to_str(jcr->getJobLevel()),
1920 jcr->client->name(),
1921 jcr->fileset->name(),
1922 NPRT(jcr->pool->name()), jcr->pool_source,
1923 jcr->rstore->name(), jcr->rstore_source,
1926 bstrutime(dt, sizeof(dt), jcr->sched_time),
1932 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
1934 jcr->RestoreJobId = str_to_int64(jid);
1936 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
1939 jcr->RestoreJobId = ua->int64_val;
1942 jcr->setJobLevel(L_FULL); /* default level */
1943 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
1944 if (jcr->RestoreJobId == 0) {
1945 /* RegexWhere is take before RestoreWhere */
1946 if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
1948 ua->signal(BNET_RUN_CMD);
1949 ua->send_msg("Type: Restore\n"
1950 "Title: Run Restore Job\n"
1956 "Backup Client: %s\n"
1957 "Restore Client: %s\n"
1962 "Plugin Options: %s\n",
1964 NPRT(jcr->RestoreBootstrap),
1965 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
1967 jcr->fileset->name(),
1969 jcr->client->name(),
1970 jcr->rstore->name(),
1971 bstrutime(dt, sizeof(dt), jcr->sched_time),
1972 jcr->catalog->name(),
1974 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
1975 _("User specified") : _("*None*"));
1977 ua->send_msg(_("Run Restore job\n"
1983 "Backup Client: %s\n"
1984 "Restore Client: %s\n"
1989 "Plugin Options: %s\n"),
1991 NPRT(jcr->RestoreBootstrap),
1992 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
1994 jcr->fileset->name(),
1996 jcr->client->name(),
1997 jcr->rstore->name(),
1998 bstrutime(dt, sizeof(dt), jcr->sched_time),
1999 jcr->catalog->name(),
2001 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2002 _("User specified") : _("*None*"));
2006 ua->signal(BNET_RUN_CMD);
2007 ua->send_msg("Type: Restore\n"
2008 "Title: Run Restore job\n"
2014 "Backup Client: %s\n"
2015 "Restore Client: %s\n"
2020 "Plugin Options: %s\n",
2022 NPRT(jcr->RestoreBootstrap),
2023 jcr->where?jcr->where:NPRT(job->RestoreWhere),
2025 jcr->fileset->name(),
2027 jcr->client->name(),
2028 jcr->rstore->name(),
2029 bstrutime(dt, sizeof(dt), jcr->sched_time),
2030 jcr->catalog->name(),
2032 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2033 _("User specified") : _("*None*"));
2035 ua->send_msg(_("Run Restore job\n"
2041 "Backup Client: %s\n"
2042 "Restore Client: %s\n"
2047 "Plugin Options: %s\n"),
2049 NPRT(jcr->RestoreBootstrap),
2050 jcr->where?jcr->where:NPRT(job->RestoreWhere),
2052 jcr->fileset->name(),
2054 jcr->client->name(),
2055 jcr->rstore->name(),
2056 bstrutime(dt, sizeof(dt), jcr->sched_time),
2057 jcr->catalog->name(),
2059 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2060 _("User specified") : _("*None*"));
2065 /* ***FIXME*** This needs to be fixed for bat */
2066 if (ua->api) ua->signal(BNET_RUN_CMD);
2067 ua->send_msg(_("Run Restore job\n"
2071 NPRT(jcr->RestoreBootstrap));
2073 /* RegexWhere is take before RestoreWhere */
2074 if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
2075 ua->send_msg(_("RegexWhere: %s\n"),
2076 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere);
2078 ua->send_msg(_("Where: %s\n"),
2079 jcr->where?jcr->where:NPRT(job->RestoreWhere));
2082 ua->send_msg(_("Replace: %s\n"
2089 "Plugin Options: %s\n"),
2091 jcr->client->name(),
2092 jcr->rstore->name(),
2093 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
2094 bstrutime(dt, sizeof(dt), jcr->sched_time),
2095 jcr->catalog->name(),
2097 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2098 _("User specified") : _("*None*"));
2104 jcr->setJobLevel(L_FULL); /* default level */
2106 ua->signal(BNET_RUN_CMD);
2107 if (jcr->getJobType() == JT_COPY) {
2108 prt_type = (char *)"Type: Copy\nTitle: Run Copy Job\n";
2110 prt_type = (char *)"Type: Migration\nTitle: Run Migration Job\n";
2119 "Read Storage: %s\n"
2120 "Write Storage: %s\n"
2127 NPRT(jcr->RestoreBootstrap),
2128 jcr->client->name(),
2129 jcr->fileset->name(),
2130 NPRT(jcr->pool->name()),
2131 jcr->next_pool?jcr->next_pool->name():"*None*",
2132 jcr->rstore->name(),
2133 jcr->wstore?jcr->wstore->name():"*None*",
2134 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
2135 bstrutime(dt, sizeof(dt), jcr->sched_time),
2136 jcr->catalog->name(),
2139 if (jcr->getJobType() == JT_COPY) {
2140 prt_type = _("Run Copy job\n");
2142 prt_type = _("Run Migration job\n");
2149 "Pool: %s (From %s)\n"
2150 "NextPool: %s (From %s)\n"
2151 "Read Storage: %s (From %s)\n"
2152 "Write Storage: %s (From %s)\n"
2159 NPRT(jcr->RestoreBootstrap),
2160 jcr->client->name(),
2161 jcr->fileset->name(),
2162 NPRT(jcr->pool->name()), jcr->pool_source,
2163 jcr->next_pool?jcr->next_pool->name():"*None*",
2164 NPRT(jcr->next_pool_source),
2165 jcr->rstore->name(), jcr->rstore_source,
2166 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
2167 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
2168 bstrutime(dt, sizeof(dt), jcr->sched_time),
2169 jcr->catalog->name(),
2174 ua->error_msg(_("Unknown Job Type=%d\n"), jcr->getJobType());
2181 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc)
2185 static const char *kw[] = { /* command line arguments */
2186 "alljobid", /* 0 Used in a switch() */
2194 "regexwhere", /* 8 where string as a bregexp */
2196 "bootstrap", /* 10 */
2199 "priority", /* 13 */
2200 "yes", /* 14 -- if you change this change YES_POS too */
2201 "verifyjob", /* 15 */
2202 "files", /* 16 number of files to restore */
2203 "catalog", /* 17 override catalog */
2204 "since", /* 18 since */
2205 "cloned", /* 19 cloned */
2206 "verifylist", /* 20 verify output list */
2207 "migrationjob", /* 21 migration job name */
2209 "backupclient", /* 23 */
2210 "restoreclient", /* 24 */
2211 "pluginoptions", /* 25 */
2212 "spooldata", /* 26 */
2214 "ignoreduplicatecheck", /* 28 */
2215 "accurate", /* 29 */
2217 "mediatype", /* 31 */
2218 "nextpool", /* 32 override next pool name */
2219 "fdcalled", /* 33 */
2225 rc.catalog_name = NULL;
2227 rc.pool_name = NULL;
2228 rc.next_pool_name = NULL;
2229 rc.store_name = NULL;
2230 rc.client_name = NULL;
2231 rc.media_type = NULL;
2232 rc.restore_client_name = NULL;
2233 rc.fileset_name = NULL;
2234 rc.verify_job_name = NULL;
2235 rc.previous_job_name = NULL;
2236 rc.accurate_set = false;
2237 rc.spool_data_set = false;
2238 rc.ignoreduplicatecheck = false;
2240 free_plugin_config_items(rc.plugin_config);
2242 for (i=1; i<ua->argc; i++) {
2243 Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
2245 /* Keep looking until we find a good keyword */
2246 for (j=0; !kw_ok && kw[j]; j++) {
2247 if (strcasecmp(ua->argk[i], kw[j]) == 0) {
2248 /* Note, yes and run have no value, so do not fail */
2249 if (!ua->argv[i] && j != YES_POS /*yes*/) {
2250 ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
2253 Dmsg2(800, "Got j=%d keyword=%s\n", j, NPRT(kw[j]));
2255 case 0: /* alljobid */
2257 /* Fall through wanted */
2259 if (rc.jid && !rc.mod) {
2260 ua->send_msg(_("JobId specified twice.\n"));
2263 rc.jid = ua->argv[i];
2266 case 2: /* client */
2268 if (rc.client_name) {
2269 ua->send_msg(_("Client specified twice.\n"));
2272 rc.client_name = ua->argv[i];
2275 case 4: /* fileset */
2276 if (rc.fileset_name) {
2277 ua->send_msg(_("FileSet specified twice.\n"));
2280 rc.fileset_name = ua->argv[i];
2284 if (rc.level_name) {
2285 ua->send_msg(_("Level specified twice.\n"));
2288 rc.level_name = ua->argv[i];
2291 case 6: /* storage */
2293 if (rc.store_name) {
2294 ua->send_msg(_("Storage specified twice.\n"));
2297 rc.store_name = ua->argv[i];
2300 case 8: /* regexwhere */
2301 if ((rc.regexwhere || rc.where) && !rc.mod) {
2302 ua->send_msg(_("RegexWhere or Where specified twice.\n"));
2305 rc.regexwhere = ua->argv[i];
2306 if (!acl_access_ok(ua, Where_ACL, rc.regexwhere)) {
2307 ua->send_msg(_("No authorization for \"regexwhere\" specification.\n"));
2313 if ((rc.where || rc.regexwhere) && !rc.mod) {
2314 ua->send_msg(_("Where or RegexWhere specified twice.\n"));
2317 rc.where = ua->argv[i];
2318 if (!acl_access_ok(ua, Where_ACL, rc.where)) {
2319 ua->send_msg(_("No authoriztion for \"where\" specification.\n"));
2324 case 10: /* bootstrap */
2325 if (rc.bootstrap && !rc.mod) {
2326 ua->send_msg(_("Bootstrap specified twice.\n"));
2329 rc.bootstrap = ua->argv[i];
2332 case 11: /* replace */
2333 if (rc.replace && !rc.mod) {
2334 ua->send_msg(_("Replace specified twice.\n"));
2337 rc.replace = ua->argv[i];
2341 if (rc.when && !rc.mod) {
2342 ua->send_msg(_("When specified twice.\n"));
2345 rc.when = ua->argv[i];
2348 case 13: /* Priority */
2349 if (rc.Priority && !rc.mod) {
2350 ua->send_msg(_("Priority specified twice.\n"));
2353 rc.Priority = atoi(ua->argv[i]);
2354 if (rc.Priority <= 0) {
2355 ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
2363 case 15: /* Verify Job */
2364 if (rc.verify_job_name) {
2365 ua->send_msg(_("Verify Job specified twice.\n"));
2368 rc.verify_job_name = ua->argv[i];
2371 case 16: /* files */
2372 rc.files = atoi(ua->argv[i]);
2375 case 17: /* catalog */
2376 rc.catalog_name = ua->argv[i];
2379 case 18: /* since */
2380 rc.since = ua->argv[i];
2383 case 19: /* cloned */
2387 case 20: /* write verify list output */
2388 rc.verify_list = ua->argv[i];
2391 case 21: /* Migration Job */
2392 if (rc.previous_job_name) {
2393 ua->send_msg(_("Migration Job specified twice.\n"));
2396 rc.previous_job_name = ua->argv[i];
2401 ua->send_msg(_("Pool specified twice.\n"));
2404 rc.pool_name = ua->argv[i];
2407 case 23: /* backupclient */
2408 if (rc.client_name) {
2409 ua->send_msg(_("Client specified twice.\n"));
2412 rc.client_name = ua->argv[i];
2415 case 24: /* restoreclient */
2416 if (rc.restore_client_name && !rc.mod) {
2417 ua->send_msg(_("Restore Client specified twice.\n"));
2420 rc.restore_client_name = ua->argv[i];
2423 case 25: /* pluginoptions */
2424 ua->send_msg(_("Plugin Options not yet implemented.\n"));
2426 if (rc.plugin_options) {
2427 ua->send_msg(_("Plugin Options specified twice.\n"));
2430 rc.plugin_options = ua->argv[i];
2431 if (!acl_access_ok(ua, PluginOptions_ACL, rc.plugin_options)) {
2432 ua->send_msg(_("No authoriztion for \"PluginOptions\" specification.\n"));
2437 case 26: /* spooldata */
2438 if (rc.spool_data_set) {
2439 ua->send_msg(_("Spool flag specified twice.\n"));
2442 if (is_yesno(ua->argv[i], &rc.spool_data)) {
2443 rc.spool_data_set = true;
2446 ua->send_msg(_("Invalid spooldata flag.\n"));
2449 case 27: /* comment */
2450 rc.comment = ua->argv[i];
2453 case 28: /* ignoreduplicatecheck */
2454 if (rc.ignoreduplicatecheck_set) {
2455 ua->send_msg(_("IgnoreDuplicateCheck flag specified twice.\n"));
2458 if (is_yesno(ua->argv[i], &rc.ignoreduplicatecheck)) {
2459 rc.ignoreduplicatecheck_set = true;
2462 ua->send_msg(_("Invalid ignoreduplicatecheck flag.\n"));
2465 case 29: /* accurate */
2466 if (rc.accurate_set) {
2467 ua->send_msg(_("Accurate flag specified twice.\n"));
2470 if (is_yesno(ua->argv[i], &rc.accurate)) {
2471 rc.accurate_set = true;
2474 ua->send_msg(_("Invalid accurate flag.\n"));
2479 ua->send_msg(_("Job name specified twice.\n"));
2482 rc.job_name = ua->argv[i];
2485 case 31: /* mediatype */
2486 if (rc.media_type) {
2487 ua->send_msg(_("Media Type specified twice.\n"));
2490 rc.media_type = ua->argv[i];
2493 case 32: /* Next Pool */
2494 if (rc.next_pool_name) {
2495 ua->send_msg(_("NextPool specified twice.\n"));
2498 rc.next_pool_name = ua->argv[i];
2501 case 33: /* fdcalled */
2507 } /* end strcase compare */
2508 } /* end keyword loop */
2511 * End of keyword for loop -- if not found, we got a bogus keyword
2514 Dmsg1(800, "%s not found\n", ua->argk[i]);
2516 * Special case for Job Name, it can be the first
2517 * keyword that has no value.
2519 if (!rc.job_name && !ua->argv[i]) {
2520 rc.job_name = ua->argk[i]; /* use keyword as job name */
2521 Dmsg1(800, "Set jobname=%s\n", rc.job_name);
2523 ua->send_msg(_("Invalid keyword: %s\n"), ua->argk[i]);
2527 } /* end argc loop */
2529 Dmsg0(800, "Done scan.\n");
2531 if (!is_comment_legal(ua, rc.comment)) {
2535 if (rc.catalog_name) {
2536 rc.catalog = GetCatalogResWithName(rc.catalog_name);
2537 if (rc.catalog == NULL) {
2538 ua->error_msg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
2541 if (!acl_access_ok(ua, Catalog_ACL, rc.catalog->name())) {
2542 ua->error_msg(_("No authorization. Catalog \"%s\".\n"), rc.catalog->name());
2546 Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
2548 if (!get_job(ua, rc)) {
2552 if (!get_pool(ua, rc)) {
2556 if (!get_next_pool(ua, rc)) {
2560 if (!get_storage(ua, rc)) {
2565 if (!get_client(ua, rc)) {
2569 if (!get_fileset(ua, rc)) {
2573 if (rc.verify_job_name) {
2574 rc.verify_job = GetJobResWithName(rc.verify_job_name);
2575 if (!rc.verify_job) {
2576 ua->send_msg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
2577 rc.verify_job = select_job_resource(ua);
2579 } else if (!rc.verify_job) {
2580 rc.verify_job = rc.job->verify_job;
2583 if (rc.previous_job_name) {
2584 rc.previous_job = GetJobResWithName(rc.previous_job_name);
2585 if (!rc.previous_job) {
2586 ua->send_msg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
2587 rc.previous_job = select_job_resource(ua);
2590 rc.previous_job = rc.job->verify_job;