1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
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.
8
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.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 *
21 * Bacula Director -- Run Command
22 *
23 * Kern Sibbald, December MMI
24 *
25 */
26
27 #include "bacula.h"
28 #include "dird.h"
29
30 const char *get_command(int index);
31
32 class run_ctx {
33 public:
34 const char *level_name;
35 char *job_name, *jid, *store_name, *pool_name;
36 char *where, *fileset_name, *client_name, *bootstrap, *regexwhere;
37 char *restore_client_name, *comment, *media_type, *next_pool_name;
38 char *job_user, *job_group;
39 const char *replace;
40 char *when, *verify_job_name, *catalog_name;
41 char *previous_job_name;
42 char *since;
43 char *plugin_options;
44 const char *verify_list;
45 JOB *job;
46 JOB *verify_job;
47 JOB *previous_job;
48 JOB_DBR jr;
49 POOL_DBR pr;
50 USTORE *store;
51 CLIENT *client;
52 FILESET *fileset;
53 POOL *pool;
54 POOL *next_pool;
55 CAT *catalog;
56 JobId_t JobId;
57 alist *JobIds;
58 int Priority;
59 int files;
60 bool cloned;
61 bool mod;
62 bool restart;
63 bool done;
64 bool alljobid;
65 bool fdcalled;
66 int spool_data;
67 bool spool_data_set;
68 int accurate;
69 bool accurate_set;
70 int ignoreduplicatecheck;
71 bool ignoreduplicatecheck_set;
72 alist *plugin_config; /* List of all plugin_item */
73 /* Methods */
run_ctx()74 run_ctx() { bmemset(this, 0, sizeof(run_ctx));
75 store = new USTORE; };
~run_ctx()76 ~run_ctx() {
77 delete store;
78 if (JobIds) {
79 delete JobIds;
80 }
81 };
82 };
83
84 /* Forward referenced subroutines */
85 static void select_job_level(UAContext *ua, JCR *jcr);
86 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job,
87 const char *verify_list, char *jid, const char *replace,
88 char *client_name);
89 static void select_where_regexp(UAContext *ua, JCR *jcr);
90 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc);
91 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc);
92 static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc);
93 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc);
94
95 /* Imported variables */
96 extern struct s_kw ReplaceOptions[];
97
98 /*
99 * For Backup and Verify Jobs
100 * run [job=]<job-name> level=<level-name>
101 *
102 * For Restore Jobs
103 * run <job-name>
104 *
105 * Returns: 0 on error
106 * JobId if OK
107 *
108 */
run_cmd(UAContext * ua,const char * cmd)109 int run_cmd(UAContext *ua, const char *cmd)
110 {
111 JCR *jcr = NULL;
112 run_ctx rc;
113 int status;
114
115 if (!open_client_db(ua)) {
116 goto bail_out;
117 }
118
119 if (!scan_run_command_line_arguments(ua, rc)) {
120 goto bail_out;
121 }
122
123 for ( ;; ) {
124 /*
125 * Create JCR to run job. NOTE!!! after this point, free_jcr()
126 * before returning.
127 */
128 if (!jcr) {
129 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
130 set_jcr_defaults(jcr, rc.job);
131 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
132 ua->jcr->unlink_bsr = false;
133 if (find_arg(ua, NT_("fdcalled")) > 0) {
134 rc.fdcalled = true;
135 }
136 }
137 /* Transfer JobIds to new restore Job */
138 if (ua->jcr->JobIds) {
139 if (jcr->JobIds) {
140 free_pool_memory(jcr->JobIds);
141 }
142 jcr->JobIds = ua->jcr->JobIds;
143 ua->jcr->JobIds = NULL;
144 }
145 /* Transfer VSS component info */
146 if (ua->jcr->component_fname) {
147 jcr->component_fname = ua->jcr->component_fname;
148 ua->jcr->component_fname = NULL;
149 jcr->component_fd = ua->jcr->component_fd;
150 ua->jcr->component_fd = NULL;
151 }
152 /* Transfer Plugin Restore Configuration */
153 if (ua->jcr->plugin_config) {
154 jcr->plugin_config = ua->jcr->plugin_config;
155 ua->jcr->plugin_config = NULL;
156 }
157 /* Transfer the BSR memory structure */
158 if (ua->jcr->bsr_list) {
159 jcr->bsr_list = ua->jcr->bsr_list;
160 ua->jcr->bsr_list = NULL;
161 }
162 if (!set_run_context_in_jcr(ua, jcr, rc)) {
163 break; /* error get out of while loop */
164 }
165
166 /* Run without prompting? */
167 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
168 return start_job(ua, jcr, rc);
169 }
170
171 /*
172 * Prompt User to see if all run job parameters are correct, and
173 * allow him to modify them.
174 */
175 if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace,
176 rc.client_name ? rc.client_name : jcr->job->client->hdr.name)) {
177 break; /* error get out of while loop */
178 }
179
180 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
181 break; /* error get out of while loop */
182 }
183
184 if (strncasecmp(ua->cmd, ".mod ", 5) == 0 ||
185 (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) {
186 parse_ua_args(ua);
187 rc.mod = true;
188 if (!scan_run_command_line_arguments(ua, rc)) {
189 break; /* error get out of while loop */
190 }
191 continue; /* another round with while loop */
192 }
193
194 /* Allow the user to modify the settings */
195 status = modify_job_parameters(ua, jcr, rc);
196 if (status == 0) {
197 continue; /* another round with while loop */
198 }
199 if (status == -1) { /* error */
200 break; /* error get out of while loop */
201 }
202
203 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
204 return start_job(ua, jcr, rc);
205 }
206 if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
207 break; /* get out of while loop */
208 }
209 ua->send_msg(_("\nBad response: %s. You must answer yes, mod, or no.\n\n"), ua->cmd);
210 }
211
212 bail_out:
213 ua->send_msg(_("Job not run.\n"));
214 if (ua->jcr->component_fd) {
215 fclose(ua->jcr->component_fd);
216 ua->jcr->component_fd = NULL;
217 }
218 if (ua->jcr->component_fname) {
219 unlink(ua->jcr->component_fname);
220 free_and_null_pool_memory(ua->jcr->component_fname);
221 }
222 if (jcr) {
223 if (jcr->component_fd) {
224 fclose(jcr->component_fd);
225 jcr->component_fd = NULL;
226 }
227 if (jcr->component_fname) {
228 unlink(jcr->component_fname);
229 free_and_null_pool_memory(jcr->component_fname);
230 }
231 free_jcr(jcr);
232 }
233 return 0; /* do not run */
234 }
235
start_job(UAContext * ua,JCR * jcr,run_ctx & rc)236 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc)
237 {
238 JobId_t JobId;
239 char ed1[50];
240
241 /* Do a final check for the client, the job can change in the previous menu */
242 if (jcr->client && jcr->job) {
243 if (!acl_access_client_ok(ua, jcr->client->name(), jcr->job->JobType)) {
244 ua->error_msg(_("Job failed. Client \"%s\" not authorized on this console\n"), jcr->client->name());
245 free_jcr(jcr);
246 return 0;
247 }
248 }
249
250 /* Do a final check for the where/regexwhere, the job can change in the previous menu */
251 if (jcr->getJobType() == JT_RESTORE) {
252 char *p = jcr->RegexWhere ? jcr->RegexWhere : jcr->job->RegexWhere;
253 if (p) {
254 if (!acl_access_ok(ua, Where_ACL, p)) {
255 ua->error_msg(_("\"RegexWhere\" specification not authorized.\n"));
256 free_jcr(jcr);
257 return 0;
258 }
259
260 } else {
261 p = jcr->where ? jcr->where : jcr->job->RestoreWhere;
262 if (p) {
263 if (!acl_access_ok(ua, Where_ACL, p)) {
264 ua->error_msg(_("\"where\" specification not authorized.\n"));
265 free_jcr(jcr);
266 return 0;
267 }
268 }
269 }
270 }
271
272 /* If we use the fdcalled feature, we keep use the UA socket
273 * as a FileDaemon socket. We do not use dup_bsock() because
274 * it doesn't work, when the UA will do a free_bsock() all
275 * socket childs will be closed as well.
276 */
277 if (rc.fdcalled) {
278 jcr->file_bsock = ua->UA_sock;
279 jcr->file_bsock->set_jcr(jcr);
280 }
281
282 if (rc.jr.JobStatus == JS_Incomplete) {
283 Dmsg1(100, "Ressuming JobId=%d\n", rc.jr.JobId);
284
285 /* Keep track of the important events */
286 ua->send_events("DJ0003",
287 EVENTS_TYPE_COMMAND,
288 "resume jobid=%d job=%s fileset=%s client=%s",
289 rc.jr.JobId, jcr->job->name(), jcr->fileset->name(), jcr->client->name());
290
291 JobId = resume_job(jcr, &rc.jr);
292
293 } else {
294 Dmsg1(100, "Starting JobId=%d\n", rc.jr.JobId);
295
296 /* Keep track of the important events */
297 ua->send_events("DJ0004",
298 EVENTS_TYPE_COMMAND,
299 "run job=%s fileset=%s client=%s",
300 jcr->job->name(), jcr->fileset->name(), jcr->client->name());
301
302 JobId = run_job(jcr);
303 }
304 Dmsg4(100, "JobId=%u NewJobId=%d pool=%s priority=%d\n", (int)jcr->JobId,
305 JobId, jcr->pool->name(), jcr->JobPriority);
306 free_jcr(jcr); /* release jcr */
307 if (JobId == 0) {
308 ua->error_msg(_("Job %s failed.\n"), edit_int64(rc.jr.JobId, ed1));
309
310 } else {
311 ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
312 }
313 if (rc.fdcalled) {
314 ua->signal(BNET_FDCALLED); /* After this point, this is a new connection */
315 ua->UA_sock = new_bsock();
316 ua->quit = true;
317 }
318 return JobId;
319 }
320
321 /*
322 * If no job_name defined in the run context, ask
323 * the user for it.
324 * Then put the job resource in the run context and
325 * check the access rights.
326 */
get_job(UAContext * ua,run_ctx & rc)327 static bool get_job(UAContext *ua, run_ctx &rc)
328 {
329 if (rc.job_name) {
330 /* Find Job */
331 rc.job = GetJobResWithName(rc.job_name);
332 if (!rc.job) {
333 if (*rc.job_name != 0) {
334 ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name);
335 }
336 rc.job = select_job_resource(ua);
337 } else {
338 Dmsg1(100, "Found job=%s\n", rc.job_name);
339 }
340 } else if (!rc.job) {
341 ua->send_msg(_("A job name must be specified.\n"));
342 rc.job = select_job_resource(ua);
343 }
344 if (!rc.job) {
345 return false;
346 } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) {
347 ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name());
348 return false;
349 }
350 return true;
351 }
352
353 /*
354 * If no pool_name defined in the run context, ask
355 * the user for it.
356 * Then put the pool resource in the run context and
357 * check the access rights.
358 */
get_pool(UAContext * ua,run_ctx & rc)359 static bool get_pool(UAContext *ua, run_ctx &rc)
360 {
361 if (rc.pool_name) {
362 rc.pool = GetPoolResWithName(rc.pool_name);
363 if (!rc.pool) {
364 if (*rc.pool_name != 0) {
365 ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name);
366 }
367 rc.pool = select_pool_resource(ua);
368 }
369 } else if (!rc.pool) {
370 rc.pool = rc.job->pool; /* use default */
371 }
372 if (!rc.pool) {
373 return false;
374 } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) {
375 ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name());
376 return false;
377 }
378 Dmsg1(100, "Using Pool=%s\n", rc.pool->name());
379 return true;
380 }
381
get_next_pool(UAContext * ua,run_ctx & rc)382 static bool get_next_pool(UAContext *ua, run_ctx &rc)
383 {
384 if (rc.next_pool_name) {
385 Dmsg1(100, "Have next pool=%s\n", rc.next_pool_name);
386 rc.next_pool = GetPoolResWithName(rc.next_pool_name);
387 if (!rc.next_pool) {
388 if (*rc.next_pool_name != 0) {
389 ua->warning_msg(_("NextPool \"%s\" not found.\n"), rc.next_pool_name);
390 }
391 rc.next_pool = select_pool_resource(ua);
392 }
393 }
394 /* NextPool can come from Job resource NextPool or Pool resource NextPool */
395 if (!rc.next_pool) {
396 if (rc.job->next_pool) {
397 rc.next_pool = rc.job->next_pool;
398 } else {
399 rc.next_pool = rc.pool->NextPool; /* use default */
400 }
401 }
402 if (rc.next_pool && !acl_access_ok(ua, Pool_ACL, rc.next_pool->name())) {
403 ua->error_msg(_("No authorization. NextPool \"%s\".\n"), rc.next_pool->name());
404 return false;
405 }
406 if (rc.next_pool) {
407 Dmsg1(100, "Using NextPool=%s\n", NPRT(rc.next_pool->name()));
408 }
409 return true;
410 }
411
412
413 /*
414 * Fill in client data according to what is setup
415 * in the run context, and make sure the user
416 * has authorized access to it.
417 */
get_client(UAContext * ua,run_ctx & rc)418 static bool get_client(UAContext *ua, run_ctx &rc)
419 {
420 bool authorized=false;
421 if (rc.client_name) {
422 rc.client = GetClientResWithName(rc.client_name);
423 if (!rc.client) {
424 if (*rc.client_name != 0) {
425 ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name);
426 }
427 rc.client = select_client_resource(ua, rc.job->JobType);
428 }
429 } else if (!rc.client) {
430 rc.client = rc.job->client; /* use default */
431 }
432
433 Dmsg1(800, "Using client=%s\n", rc.client->name());
434
435 if (rc.job->RestoreClient){
436 /* Use restoreclient defined in config Job resource */
437 rc.restore_client_name = rc.job->RestoreClient;
438 }
439 if (rc.restore_client_name) {
440 rc.client = GetClientResWithName(rc.restore_client_name);
441 if (!rc.client) {
442 if (*rc.restore_client_name != 0) {
443 ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name);
444 }
445 rc.client = select_client_resource(ua, rc.job->JobType);
446 }
447 } else if (!rc.client) {
448 rc.client = rc.job->client; /* use default */
449 }
450
451 if (!rc.client) {
452 return false;
453
454 } else if (acl_access_client_ok(ua, rc.client->name(), rc.job->JobType)) {
455 authorized = true;
456 }
457 if (!authorized) {
458 ua->error_msg(_("No authorization. Client \"%s\".\n"),
459 rc.client->name());
460 return false;
461 }
462 Dmsg1(800, "Using restore client=%s\n", rc.client->name());
463 return true;
464 }
465
466
467 /*
468 * Fill in fileset data according to what is setup
469 * in the run context, and make sure the user
470 * has authorized access to it.
471 */
get_fileset(UAContext * ua,run_ctx & rc)472 static bool get_fileset(UAContext *ua, run_ctx &rc)
473 {
474 if (rc.fileset_name) {
475 rc.fileset = GetFileSetResWithName(rc.fileset_name);
476 if (!rc.fileset) {
477 ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
478 rc.fileset = select_fileset_resource(ua);
479 }
480 } else if (!rc.fileset) {
481 rc.fileset = rc.job->fileset; /* use default */
482 }
483 if (!rc.fileset) {
484 return false;
485 } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) {
486 ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
487 rc.fileset->name());
488 return false;
489 }
490 return true;
491 }
492
493 /*
494 * Fill in storage data according to what is setup
495 * in the run context, and make sure the user
496 * has authorized access to it.
497 */
get_storage(UAContext * ua,run_ctx & rc)498 static bool get_storage(UAContext *ua, run_ctx &rc)
499 {
500 if (rc.store_name) {
501 rc.store->store = GetStoreResWithName(rc.store_name);
502 pm_strcpy(rc.store->store_source, _("Command input"));
503 if (!rc.store->store) {
504 if (*rc.store_name != 0) {
505 ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
506 }
507 rc.store->store = select_storage_resource(ua);
508 pm_strcpy(rc.store->store_source, _("user selection"));
509 }
510 } else if (!rc.store->store) {
511 get_job_storage(rc.store, rc.job, NULL); /* use default */
512 }
513 if (!rc.store->store) {
514 ua->error_msg(_("No storage specified.\n"));
515 return false;
516 } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
517 ua->error_msg(_("No authorization. Storage \"%s\".\n"),
518 rc.store->store->name());
519 return false;
520 }
521 Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
522 return true;
523 }
524
525 /*
526 * Get and pass back a list of Jobids in rc.jid
527 */
get_jobid_list(UAContext * ua,sellist & sl,run_ctx & rc)528 static bool get_jobid_list(UAContext *ua, sellist &sl, run_ctx &rc)
529 {
530 int i, JobId;
531 JOB_DBR jr;
532 char *pJobId;
533 bool found = false;
534
535 memset(&jr, 0, sizeof(jr));
536 rc.jid = NULL;
537 /* See if any JobId is specified */
538 if ((i=find_arg(ua, "jobid")) >= 0) {
539 rc.jid = ua->argv[i];
540 if (!rc.jid) {
541 ua->send_msg(_("No JobId specified.\n"));
542 return false;
543 }
544 if (!sl.set_string(ua->argv[i], true)) {
545 ua->send_msg("%s", sl.get_errmsg());
546 return false;
547 }
548 return true;
549 }
550
551 /* No JobId list give, so see if he specified a Job */
552 if ((i=find_arg(ua, "job")) >= 0) {
553 rc.job_name = ua->argv[i];
554 if (!get_job(ua, rc)) {
555 ua->send_msg(_("Invalid or no Job name specified.\n"));
556 return false;
557 }
558 }
559
560 if ((i=find_arg_with_value(ua, "limit")) >= 0) {
561 jr.limit = str_to_int64(ua->argv[i]);
562
563 } else {
564 jr.limit = 100; /* max 100 records */
565 }
566
567 if (rc.job_name) {
568 bstrncpy(jr.Name, rc.job_name, sizeof(jr.Name));
569 } else {
570 jr.Name[0] = 0;
571 }
572 jr.JobStatus = rc.jr.JobStatus;
573 Dmsg2(100, "JobStatus=%d JobName=%s\n", jr.JobStatus, jr.Name);
574 /* rc.JobIds is alist of all records found and printed */
575 rc.JobIds = db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, INCOMPLETE_JOBS);
576 if (!rc.JobIds || rc.JobIds->size()==0 ||
577 !get_selection_list(ua, sl, _("Enter the JobId list to select: "), false)) {
578 return false;
579 }
580 Dmsg1(100, "list=%s\n", sl.get_list());
581 /*
582 * Make sure each item entered is in the JobIds list
583 */
584 while ( (JobId = sl.next()) > 0) {
585 foreach_alist(pJobId, rc.JobIds) {
586 if (JobId == str_to_int64(pJobId)) {
587 pJobId[0] = 0;
588 found = true;
589 break;
590 }
591 }
592 if (!found) {
593 ua->error_msg(_("JobId=%d entered is not in the list.\n"), JobId);
594 return false;
595 }
596 }
597 sl.begin(); /* reset to walk list again */
598 rc.done = false;
599 return true;
600 }
601
get_jobid_from_list(UAContext * ua,sellist & sl,run_ctx & rc)602 static bool get_jobid_from_list(UAContext *ua, sellist &sl, run_ctx &rc)
603 {
604 int JobId;
605 CLIENT_DBR cr;
606 FILESET_DBR fr;
607
608 if (rc.done) {
609 return false;
610 }
611 if ((JobId = sl.next()) < 0) {
612 Dmsg1(100, "sl.next()=%d\n", JobId);
613 rc.done = true;
614 return false;
615 }
616 bmemset(&rc.jr, 0, sizeof(rc.jr));
617 rc.jr.JobId = rc.JobId = JobId;
618 Dmsg1(100, "Next JobId=%d\n", rc.JobId);
619 if (!db_get_job_record(ua->jcr, ua->db, &rc.jr)) {
620 ua->error_msg(_("Could not get job record for selected JobId=%d. ERR=%s"),
621 rc.JobId, db_strerror(ua->db));
622 return false;
623 }
624 Dmsg3(100, "Job=%s JobId=%d JobStatus=%c\n", rc.jr.Name, rc.jr.JobId,
625 rc.jr.JobStatus);
626
627 rc.level_name = level_to_static_str(rc.jr.JobLevel);
628 rc.job_name = rc.jr.Name;
629
630 if (!get_job(ua, rc)) {
631 return false;
632 }
633 bmemset(&rc.pr, 0, sizeof(rc.pr));
634 rc.pr.PoolId = rc.jr.PoolId;
635 if (!db_get_pool_record(ua->jcr, ua->db, &rc.pr)) {
636 ua->error_msg(_("Could not get pool record for selected JobId=%d. ERR=%s"),
637 rc.JobId, db_strerror(ua->db));
638 return false;
639 }
640 rc.pool_name = rc.pr.Name;
641 if (!get_pool(ua, rc)) {
642 return false;
643 }
644 get_job_storage(rc.store, rc.job, NULL);
645
646 bmemset(&cr, 0, sizeof(cr));
647 cr.ClientId = rc.jr.ClientId;
648 if (!db_get_client_record(ua->jcr, ua->db, &cr)) {
649 ua->error_msg(_("Could not get client record for selected JobId=%d. ERR=%s"),
650 rc.JobId, db_strerror(ua->db));
651 return false;
652 }
653 rc.client_name = cr.Name;
654 if (!get_client(ua, rc)) {
655 return false;
656 }
657
658 bmemset(&fr, 0, sizeof(fr));
659 fr.FileSetId = rc.jr.FileSetId;
660 if (!db_get_fileset_record(ua->jcr, ua->db, &fr)) {
661 ua->error_msg(_("Could not get fileset record for selected JobId=%d. ERR=%s"),
662 rc.JobId, db_strerror(ua->db));
663 return false;
664 }
665 rc.fileset_name = fr.FileSet;
666 if (!get_fileset(ua, rc)) {
667 return false;
668 }
669 if (!get_storage(ua, rc)) {
670 return false;
671 }
672 return true;
673 }
674
675 /*
676 * Restart Canceled, Failed, or Incomplete Jobs
677 *
678 * Returns: 0 on error
679 * JobId if OK
680 *
681 */
restart_cmd(UAContext * ua,const char * cmd)682 int restart_cmd(UAContext *ua, const char *cmd)
683 {
684 JCR *jcr = NULL;
685 run_ctx rc;
686 sellist sl;
687 int i, j;
688 bool got_kw = false;
689 struct s_js {
690 const char *status_name;
691 int32_t job_status;
692 };
693 struct s_js kw[] = {
694 {"Incomplete", JS_Incomplete},
695 {"Canceled", JS_Canceled},
696 {"Failed", JS_FatalError},
697 {"All", 0},
698 {NULL, 0}
699 };
700
701 if (!open_client_db(ua)) {
702 return 0;
703 }
704
705 rc.jr.JobStatus = 0;
706
707 /* Users can set the jobid list in command line */
708 if ((i = find_arg_with_value(ua, "jobid")) >= 0) {
709 Dmsg1(100, "Will resume jobid=%s\n", ua->argv[i]);
710
711 } else {
712 for (i=1; i<ua->argc; i++) {
713 for (j=0; kw[j].status_name; j++) {
714 if (strcasecmp(ua->argk[i], kw[j].status_name) == 0) {
715 rc.jr.JobStatus = kw[j].job_status;
716 got_kw = true;
717 break;
718 }
719 }
720 }
721
722 if (!got_kw) { /* Must prompt user */
723 start_prompt(ua, _("You have the following choices:\n"));
724 for (i=0; kw[i].status_name; i++) {
725 add_prompt(ua, kw[i].status_name);
726 }
727 i = do_prompt(ua, NULL, _("Select termination code: "), NULL, 0);
728 if (i < 0) {
729 return 0;
730 }
731 rc.jr.JobStatus = kw[i].job_status;
732 }
733 /* type now has what job termination code we want to look at */
734 Dmsg1(100, "Termination code=%c\n", rc.jr.JobStatus);
735 }
736
737 /* Get a list of JobIds to restore */
738 if (!get_jobid_list(ua, sl, rc)) {
739 if (rc.JobIds) {
740 rc.JobIds->destroy();
741 }
742 return false;
743 }
744 Dmsg1(100, "list=%s\n", sl.get_list());
745
746 while (get_jobid_from_list(ua, sl, rc)) {
747 /*
748 * Create JCR to run job. NOTE!!! after this point, free_jcr()
749 * before returning.
750 */
751 if (!jcr) {
752 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
753 set_jcr_defaults(jcr, rc.job);
754 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
755 ua->jcr->unlink_bsr = false;
756 }
757
758 if (!set_run_context_in_jcr(ua, jcr, rc)) {
759 break;
760 }
761 start_job(ua, jcr, rc);
762 jcr = NULL;
763 }
764
765 if (jcr) {
766 free_jcr(jcr);
767 }
768 if (rc.JobIds) {
769 rc.JobIds->destroy();
770 }
771 return 0; /* do not run */
772 }
773
774
775 /*
776 * Plugin restore option part
777 */
778
779 /* Free a plugin_config_item */
free_plugin_config_item(plugin_config_item * elt)780 void free_plugin_config_item(plugin_config_item *elt)
781 {
782 free(elt->plugin_name);
783 free_pool_memory(elt->content);
784 free(elt);
785 }
786
787 /* Free a list of plugins (do not free the list itself) */
free_plugin_config_items(alist * lst)788 void free_plugin_config_items(alist *lst)
789 {
790 plugin_config_item *elt;
791
792 if (!lst) {
793 return;
794 }
795
796 foreach_alist(elt, lst) {
797 free_plugin_config_item(elt);
798 }
799 }
800
801 /* Structure used in the sql query to get configuration restore objects */
802 struct plugin_config_handler_t
803 {
804 UAContext *ua; /* UAContext for user input */
805 POOLMEM *tmp; /* Used to store the config object */
806 alist *plugins; /* Configuration plugin list */
807 alist *content; /* Temp file used by each plugin */
808 };
809
810 /* DB handler to get all configuration restore objects for a given
811 * set of jobids
812 */
plugin_config_handler(void * ctx,int num_fields,char ** row)813 static int plugin_config_handler(void *ctx, int num_fields, char **row)
814 {
815 struct plugin_config_handler_t *pch = (struct plugin_config_handler_t *)ctx;
816 UAContext *ua = pch->ua;
817 JCR *jcr = ua->jcr;
818 int32_t len;
819
820 /* object */
821 db_unescape_object(jcr, ua->db,
822 row[8], /* Object */
823 str_to_uint64(row[1]), /* Object length */
824 &pch->tmp, &len);
825
826 /* Is compressed ? */
827 if (str_to_int64(row[5]) > 0) {
828 int full_len = str_to_int64(row[2]);
829 int out_len = full_len + 100; /* full length */
830 char *obj = (char *)malloc(out_len);
831 Zinflate(pch->tmp, len, obj, out_len); /* out_len is updated */
832 if (out_len != full_len) {
833 ua->error_msg(_("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
834 full_len, out_len, row[9]);
835 }
836 obj[out_len] = 0;
837 pch->content->append(obj);
838
839 } else {
840 pch->tmp[len]=0;
841 pch->content->append(bstrdup(pch->tmp));
842 }
843
844 pch->plugins->append(bstrdup(row[9]));
845 return 0;
846 }
847
848 /* Save a Plugin Config object (ConfigFile) inside the JCR
849 * using a list of plugin_config_item
850 *
851 * We allow only one Plugin Config object per Plugin
852 */
plugin_config_save_jcr(UAContext * ua,JCR * jcr,char * pname,ConfigFile * ini)853 static void plugin_config_save_jcr(UAContext *ua, JCR *jcr,
854 char *pname, ConfigFile *ini)
855 {
856 plugin_config_item *elt;
857 if (!jcr->plugin_config) {
858 jcr->plugin_config = New(alist(5, not_owned_by_alist));
859 }
860
861 /* Store only one Plugin Config object per plugin command */
862 for (int i = 0; i < jcr->plugin_config->size() ; i++) {
863 elt = (plugin_config_item *) jcr->plugin_config->get(i);
864 if (strcmp(elt->plugin_name, pname) == 0) {
865 jcr->plugin_config->remove(i);
866 free_plugin_config_item(elt);
867 break;
868 }
869 }
870
871 elt = (plugin_config_item *) malloc (sizeof(plugin_config_item));
872 elt->plugin_name = bstrdup(pname);
873 elt->content = get_pool_memory(PM_FNAME);
874 ini->dump_results(&elt->content);
875 jcr->plugin_config->append(elt);
876 }
877
878 /* TODO: Allow to have sub-menus Advanced.restore_mode can be
879 * in a Advanced panel (sub menu)
880 */
881
882 /* Take the ConfigIni struture and display user menu for a given plugin */
plugin_display_options(UAContext * ua,JCR * jcr,ConfigFile * ini)883 static int plugin_display_options(UAContext *ua, JCR *jcr, ConfigFile *ini)
884 {
885 int i, nb;
886 int jcr_pos = -1;
887 POOL_MEM prompt, tmp;
888 bool found;
889 INI_ITEM_HANDLER *h;
890
891 /* TODO: See how to work in API mode
892 if (ua->api) {
893 ua->signal(BNET_RUN_CMD);
894 }
895 */
896
897 /* Take a look in the plugin_config list to see if we have something to
898 * initialize
899 */
900 if (jcr->plugin_config) {
901 plugin_config_item *item=NULL;
902
903 for (jcr_pos = 0; jcr_pos < jcr->plugin_config->size() ; jcr_pos++) {
904 item = (plugin_config_item *)jcr->plugin_config->get(jcr_pos);
905
906 if (strcmp(item->plugin_name, ini->plugin_name) == 0) /* bpipe:xxx:yyyy */
907 {
908 if (!ini->dump_string(item->content, strlen(item->content)) ||
909 !ini->parse(ini->out_fname))
910 {
911 ua->error_msg(_("Unable to use current plugin configuration, "
912 "discarding it."));
913 }
914 /* When we are here, we can type yes (it will add it back), or no
915 * to not use this plugin configuration. So, don't keep it in the
916 * list.
917 */
918 jcr->plugin_config->remove(jcr_pos);
919 free_plugin_config_item(item);
920 break;
921 }
922 }
923 }
924
925 configure_again:
926 ua->send_msg(_("Plugin Restore Options\n"));
927 ua->send_msg(_("Option Current Value Default Value\n"));
928 for (nb=0; ini->items[nb].name; nb++) {
929
930 if (ini->items[nb].found) {
931 /* When calling the handler, It will convert the value
932 * to a string representation in ini->edit
933 */
934 ini->items[nb].handler(NULL, ini, &ini->items[nb]);
935 } else {
936 if (ini->items[nb].required) {
937 pm_strcpy(ini->edit, _("*None, but required*"));
938
939 } else {
940 pm_strcpy(ini->edit, _("*None*"));
941 }
942 }
943
944 Mmsg(tmp, "%s:", ini->items[nb].name);
945
946 Mmsg(prompt, "%-20s %-20s ",
947 tmp.c_str(), ini->edit);
948
949 if (ini->items[nb].default_value) {
950 Mmsg(tmp, "(%s)", ini->items[nb].default_value);
951 pm_strcat(prompt, tmp.c_str());
952 }
953
954 ua->send_msg("%s\n", prompt.c_str());
955 }
956
957 if (!get_cmd(ua, _("Use above plugin configuration? (yes/mod/no): "))) {
958 ini->clear_items();
959 return 0;
960 }
961
962 /* '', 'y', 'ye', and 'yes' are valid */
963 if (strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
964 return 1;
965 }
966
967 if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
968 ini->clear_items();
969 return 0;
970 }
971
972 /* When using "mod", we display the list of parameters with their
973 * comments, and we let the user choose one entry to modify
974 */
975 if (strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
976 start_prompt(ua, _("You have the following choices:\n"));
977
978 for (nb=0; ini->items[nb].name; nb++) {
979
980 if (ini->items[nb].comment) {
981 Mmsg(tmp, " (%s)", ini->items[nb].comment);
982 } else {
983 pm_strcpy(tmp, "");
984 }
985
986 Mmsg(prompt, "%s%s ",
987 ini->items[nb].name, tmp.c_str());
988
989 add_prompt(ua, prompt.c_str());
990 }
991
992 i = do_prompt(ua, NULL, _("Select parameter to modify"), NULL, 0);
993
994 if (i < 0) {
995 ini->clear_items();
996 return 0;
997 }
998
999 Mmsg(prompt, _("Please enter a value for %s: "), ini->items[i].name);
1000
1001 /* Now use the handler to know how to ask the value to the user.
1002 * For example, boolean will use get_yes_no(), pint32 will use get_pint()
1003 */
1004 h = ini->items[i].handler;
1005 if (h == ini_store_int32 ||
1006 h == ini_store_pint32) {
1007 found = ini->items[i].found = get_pint(ua, prompt.c_str());
1008 if (found) {
1009 ini->items[i].val.int32val = ua->pint32_val;
1010 }
1011
1012 } else if (h == ini_store_bool) {
1013 found = ini->items[i].found = get_yesno(ua, prompt.c_str());
1014 if (found) {
1015 ini->items[i].val.boolval = ua->pint32_val;
1016 }
1017
1018 } else if (h == ini_store_name) {
1019 found = ini->items[i].found = get_cmd(ua, prompt.c_str());
1020 if (found) {
1021 bstrncpy(ini->items[i].val.nameval, ua->cmd, MAX_NAME_LENGTH);
1022 }
1023
1024 } else if (h == ini_store_str) {
1025 found = ini->items[i].found = get_cmd(ua, prompt.c_str());
1026 if (found) {
1027 ini->items[i].val.strval = bstrdup(ua->cmd);
1028 }
1029
1030 } else if (h == ini_store_int64 ||
1031 h == ini_store_pint64) {
1032 found = ini->items[i].found = get_pint(ua, prompt.c_str());
1033 if (found) {
1034 ini->items[i].val.int64val = ua->int64_val;
1035 }
1036
1037 } else if (h == ini_store_alist_str) {
1038 found = ini->items[i].found = get_cmd(ua, prompt.c_str());
1039 if (found) {
1040 if (!ini->items[i].val.alistval) {
1041 ini->items[i].val.alistval = New(alist(10, owned_by_alist));
1042 }
1043 ini->items[i].val.alistval->append(bstrdup(ua->cmd));
1044 }
1045 }
1046 goto configure_again;
1047 }
1048
1049 return 1; /* never reached */
1050 }
1051
1052 /* Display a menu with all plugins */
plugin_config(UAContext * ua,JCR * jcr,run_ctx & rc)1053 static void plugin_config(UAContext *ua, JCR *jcr, run_ctx &rc)
1054 {
1055 int i, nb;
1056 char *elt, *tmp;
1057 ConfigFile *ini = NULL;
1058 POOLMEM *query=NULL;
1059 struct plugin_config_handler_t pch;
1060
1061 /* No jobids for this restore, probably wrong */
1062 if (!jcr->JobIds || !jcr->JobIds[0]) {
1063 return;
1064 }
1065
1066 if (!open_client_db(ua)) {
1067 return;
1068 }
1069
1070 pch.ua = ua;
1071 query = get_pool_memory(PM_FNAME);
1072 pch.tmp = get_pool_memory(PM_MESSAGE);
1073 pch.plugins = New(alist(10, owned_by_alist));
1074 pch.content = New(alist(10, owned_by_alist));
1075
1076 /* Get all RestoreObject PLUGIN_CONFIG for the given Job */
1077 Mmsg(query, get_restore_objects, jcr->JobIds, FT_PLUGIN_CONFIG);
1078 db_sql_query(ua->db, query, plugin_config_handler, &pch);
1079
1080 if (!pch.plugins || pch.plugins->size() == 0) {
1081 ua->info_msg(_("No plugin to configure\n"));
1082 goto bail_out;
1083 }
1084
1085 /* TODO: Let see if we want to configure plugins that were given in command
1086 * line.
1087 */
1088
1089 start_prompt(ua, _("Plugins to configure:\n"));
1090
1091 nb=0;
1092 foreach_alist(elt, pch.plugins) {
1093 nb++;
1094 pm_strcpy(query, elt);
1095 add_prompt(ua, query);
1096 }
1097
1098 i = do_prompt(ua, "", _("Select plugin to configure"), NULL, 0);
1099
1100 if (i < 0) {
1101 goto bail_out;
1102 }
1103
1104
1105 elt = (char *)pch.plugins->get(i);
1106 ini = new ConfigFile();
1107 /* Try to read the plugin configuration, if error, loop to configure
1108 * something else, or bail_out
1109 */
1110 tmp = (char *)pch.content->get(i);
1111 if (!ini->dump_string(tmp, strlen(tmp)) || /* Send the string to a file */
1112 !ini->unserialize(ini->out_fname)) { /* Read the file to initialize the ConfigFile */
1113
1114 ua->error_msg(_("Can't configure %32s\n"), elt);
1115 goto bail_out;
1116 }
1117
1118 ini->set_plugin_name(elt);
1119
1120 if (plugin_display_options(ua, jcr, ini)) {
1121 ini->dump_results(&query);
1122 Dmsg1(50, "plugin: %s\n", query);
1123
1124 /* Save the plugin somewhere in the JCR */
1125 plugin_config_save_jcr(ua, jcr, elt, ini);
1126 }
1127
1128 bail_out:
1129 free_pool_memory(pch.tmp);
1130 free_pool_memory(query);
1131 if (ini) {
1132 delete ini;
1133 }
1134 delete pch.plugins;
1135 delete pch.content;
1136 }
1137
modify_job_parameters(UAContext * ua,JCR * jcr,run_ctx & rc)1138 int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc)
1139 {
1140 int i, opt;
1141
1142 /*
1143 * At user request modify parameters of job to be run.
1144 */
1145 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0){
1146 FILE *fd;
1147
1148 start_prompt(ua, _("Parameters to modify:\n"));
1149 add_prompt(ua, _("Level")); /* 0 */
1150 add_prompt(ua, _("Storage")); /* 1 */
1151 add_prompt(ua, _("Job")); /* 2 */
1152 add_prompt(ua, _("FileSet")); /* 3 */
1153 if (jcr->getJobType() == JT_RESTORE) {
1154 add_prompt(ua, _("Restore Client")); /* 4 */
1155 } else {
1156 add_prompt(ua, _("Client")); /* 4 */
1157 }
1158 add_prompt(ua, _("When")); /* 5 */
1159 add_prompt(ua, _("Priority")); /* 6 */
1160 if (jcr->getJobType() == JT_BACKUP ||
1161 jcr->getJobType() == JT_COPY ||
1162 jcr->getJobType() == JT_MIGRATE ||
1163 jcr->getJobType() == JT_VERIFY) {
1164 add_prompt(ua, _("Pool")); /* 7 */
1165 if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */
1166 jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
1167 jcr->getJobType() == JT_COPY ||
1168 jcr->getJobType() == JT_MIGRATE) {
1169 add_prompt(ua, _("NextPool")); /* 8 */
1170 } else if (jcr->getJobType() == JT_VERIFY) {
1171 add_prompt(ua, _("Verify Job")); /* 8 */
1172 }
1173 } else if (jcr->getJobType() == JT_RESTORE) {
1174 add_prompt(ua, _("Bootstrap")); /* 7 */
1175 add_prompt(ua, _("Where")); /* 8 */
1176 add_prompt(ua, _("File Relocation"));/* 9 */
1177 add_prompt(ua, _("Replace")); /* 10 */
1178 add_prompt(ua, _("JobId")); /* 11 */
1179 }
1180 if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE) {
1181 add_prompt(ua, _("Plugin Options")); /* 12 */
1182 }
1183 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1184 case 0:
1185 /* Level */
1186 select_job_level(ua, jcr);
1187 goto try_again;
1188 case 1:
1189 /* Storage */
1190 rc.store->store = select_storage_resource(ua);
1191 if (rc.store->store) {
1192 pm_strcpy(rc.store->store_source, _("user selection"));
1193 set_rwstorage(jcr, rc.store);
1194 goto try_again;
1195 }
1196 break;
1197 case 2:
1198 /* Job */
1199 rc.job = select_job_resource(ua);
1200 if (rc.job) {
1201 jcr->job = rc.job;
1202 set_jcr_defaults(jcr, rc.job);
1203 goto try_again;
1204 }
1205 break;
1206 case 3:
1207 /* FileSet */
1208 rc.fileset = select_fileset_resource(ua);
1209 if (rc.fileset) {
1210 jcr->fileset = rc.fileset;
1211 goto try_again;
1212 }
1213 break;
1214 case 4:
1215 /* Client */
1216 {
1217 int32_t jt = rc.job ? rc.job->JobType : JT_SYSTEM;
1218 rc.client = select_client_resource(ua, jt);
1219 if (rc.client) {
1220 jcr->client = rc.client;
1221 goto try_again;
1222 }
1223 }
1224 break;
1225 case 5:
1226 /* When */
1227 if (!get_cmd(ua, _("Please enter start time as a duration or YYYY-MM-DD HH:MM:SS or return for now: "))) {
1228 break;
1229 }
1230 if (ua->cmd[0] == 0) {
1231 jcr->sched_time = time(NULL);
1232 } else {
1233 utime_t duration;
1234 jcr->sched_time = str_to_utime(ua->cmd);
1235 if (jcr->sched_time == 0) {
1236 if (duration_to_utime(ua->cmd, &duration)) {
1237 jcr->sched_time = time(NULL) + duration;
1238 } else {
1239 ua->send_msg(_("Invalid time, using current time.\n"));
1240 jcr->sched_time = time(NULL);
1241 }
1242 }
1243 }
1244
1245 goto try_again;
1246 case 6:
1247 /* Priority */
1248 if (!get_pint(ua, _("Enter new Priority: "))) {
1249 break;
1250 }
1251 if (ua->pint32_val == 0) {
1252 ua->send_msg(_("Priority must be a positive integer.\n"));
1253 } else {
1254 jcr->JobPriority = ua->pint32_val;
1255 }
1256 goto try_again;
1257 case 7:
1258 /* Pool or Bootstrap depending on JobType */
1259 if (jcr->getJobType() == JT_BACKUP ||
1260 jcr->getJobType() == JT_COPY ||
1261 jcr->getJobType() == JT_MIGRATE ||
1262 jcr->getJobType() == JT_VERIFY) { /* Pool */
1263 rc.pool = select_pool_resource(ua);
1264 if (rc.pool) {
1265 jcr->pool = rc.pool;
1266 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
1267 goto try_again;
1268 }
1269 break;
1270 }
1271
1272 /* Bootstrap */
1273 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
1274 break;
1275 }
1276 if (jcr->RestoreBootstrap) {
1277 free(jcr->RestoreBootstrap);
1278 jcr->RestoreBootstrap = NULL;
1279 }
1280 if (ua->cmd[0] != 0) {
1281 jcr->RestoreBootstrap = bstrdup(ua->cmd);
1282 fd = bfopen(jcr->RestoreBootstrap, "rb");
1283 if (!fd) {
1284 berrno be;
1285 ua->send_msg(_("Warning cannot open %s: ERR=%s\n"),
1286 jcr->RestoreBootstrap, be.bstrerror());
1287 free(jcr->RestoreBootstrap);
1288 jcr->RestoreBootstrap = NULL;
1289 } else {
1290 fclose(fd);
1291 }
1292 }
1293 goto try_again;
1294 case 8:
1295 /* Specify Next Pool */
1296 if ((jcr->getJobType() == JT_BACKUP && /* Virtual full */
1297 jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
1298 jcr->getJobType() == JT_COPY ||
1299 jcr->getJobType() == JT_MIGRATE) {
1300 rc.next_pool = select_pool_resource(ua);
1301 if (rc.next_pool) {
1302 jcr->next_pool = rc.next_pool;
1303 goto try_again;
1304 }
1305 }
1306 /* Verify Job */
1307 if (jcr->getJobType() == JT_VERIFY) {
1308 rc.verify_job = select_job_resource(ua);
1309 if (rc.verify_job) {
1310 jcr->verify_job = rc.verify_job;
1311 }
1312 goto try_again;
1313 }
1314 /* Where */
1315 if (!get_cmd(ua, _("Please enter the full path prefix for restore (/ for none): "))) {
1316 break;
1317 }
1318 if (jcr->RegexWhere) { /* cannot use regexwhere and where */
1319 free(jcr->RegexWhere);
1320 jcr->RegexWhere = NULL;
1321 }
1322 if (jcr->where) {
1323 free(jcr->where);
1324 jcr->where = NULL;
1325 }
1326 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1327 ua->cmd[0] = 0;
1328 }
1329 jcr->where = bstrdup(ua->cmd);
1330 goto try_again;
1331 case 9:
1332 /* File relocation */
1333 select_where_regexp(ua, jcr);
1334 goto try_again;
1335 case 10:
1336 /* Replace */
1337 start_prompt(ua, _("Replace:\n"));
1338 for (i=0; ReplaceOptions[i].name; i++) {
1339 add_prompt(ua, ReplaceOptions[i].name);
1340 }
1341 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
1342 if (opt >= 0) {
1343 rc.replace = ReplaceOptions[opt].name;
1344 jcr->replace = ReplaceOptions[opt].token;
1345 }
1346 goto try_again;
1347 case 11:
1348 /* JobId */
1349 rc.jid = NULL; /* force reprompt */
1350 jcr->RestoreJobId = 0;
1351 if (jcr->RestoreBootstrap) {
1352 ua->send_msg(_("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
1353 }
1354 goto try_again;
1355 case 12:
1356
1357 if (jcr->getJobType() == JT_RESTORE) {
1358 plugin_config(ua, jcr, rc);
1359
1360 } else {
1361 //generate_plugin_event(jcr, bEventJobConfig, &rc);
1362
1363 /* Plugin Options */
1364 if (!get_cmd(ua, _("Please Plugin Options string: "))) {
1365 break;
1366 }
1367 if (jcr->plugin_options) {
1368 free(jcr->plugin_options);
1369 jcr->plugin_options = NULL;
1370 }
1371 jcr->plugin_options = bstrdup(ua->cmd);
1372 }
1373 goto try_again;
1374 case -1: /* error or cancel */
1375 goto bail_out;
1376 default:
1377 goto try_again;
1378 }
1379 goto bail_out;
1380 }
1381 return 1;
1382
1383 bail_out:
1384 return -1;
1385 try_again:
1386 return 0;
1387 }
1388
1389
1390 /* Not a good idea to start a job with the Scratch pool. It creates all kind
1391 * of recycling issues while the job is running. See Mantis #303
1392 */
check_pool(int32_t JobType,int32_t JobLevel,POOL * pool,POOL * next_pool,const char ** name)1393 bool check_pool(int32_t JobType, int32_t JobLevel, POOL *pool, POOL *next_pool,
1394 const char **name)
1395 {
1396 if (JobType == JT_BACKUP) {
1397 if (pool && strcmp(pool->name(), NT_("Scratch")) == 0) {
1398 *name = NT_("Pool");
1399 return false;
1400 }
1401 }
1402 /* The NextPool should also not be a Scratch pool */
1403 if (JobType == JT_MIGRATE || JobType == JT_COPY ||
1404 (JobType == JT_BACKUP && JobLevel == L_VIRTUAL_FULL)) {
1405 if (next_pool && strcmp(next_pool->name(), NT_("Scratch")) == 0) {
1406 *name = NT_("NextPool");
1407 return false;
1408 }
1409 }
1410 return true;
1411 }
1412
1413 /*
1414 * Put the run context that we have at this point into the JCR.
1415 * That allows us to re-ask for the run context.
1416 * This subroutine can be called multiple times, so it
1417 * must keep any prior settings.
1418 */
set_run_context_in_jcr(UAContext * ua,JCR * jcr,run_ctx & rc)1419 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc)
1420 {
1421 int i;
1422
1423 if (rc.job_user) {
1424 jcr->job_user = bstrdup(rc.job_user);
1425 }
1426 if (rc.job_group) {
1427 jcr->job_group = bstrdup(rc.job_group);
1428 }
1429
1430 jcr->verify_job = rc.verify_job;
1431 jcr->previous_job = rc.previous_job;
1432 jcr->pool = rc.pool;
1433 jcr->next_pool = rc.next_pool;
1434 if (rc.next_pool) {
1435 jcr->cmdline_next_pool_override = true;
1436 }
1437 if (rc.pool_name) {
1438 pm_strcpy(jcr->pool_source, _("Command input"));
1439 } else if (jcr->pool != jcr->job->pool) {
1440 pm_strcpy(jcr->pool_source, _("User input"));
1441 }
1442 if (rc.next_pool_name) {
1443 pm_strcpy(jcr->next_pool_source, _("Command input"));
1444 } else if (jcr->next_pool == jcr->job->next_pool) {
1445 pm_strcpy(jcr->next_pool_source, _("Job resource"));
1446 } else if (jcr->next_pool != jcr->pool->NextPool) {
1447 pm_strcpy(jcr->next_pool_source, _("User input"));
1448 }
1449
1450 set_rwstorage(jcr, rc.store);
1451 jcr->client = rc.client;
1452 if (jcr->client) {
1453 pm_strcpy(jcr->client_name, rc.client->name());
1454 } else {
1455 pm_strcpy(jcr->client_name, "**Dummy**");
1456 }
1457 if (rc.media_type) {
1458 if (!jcr->media_type) {
1459 jcr->media_type = get_pool_memory(PM_NAME);
1460 }
1461 pm_strcpy(jcr->media_type, rc.media_type);
1462 }
1463 jcr->fileset = rc.fileset;
1464 jcr->ExpectedFiles = rc.files;
1465 if (rc.catalog) {
1466 jcr->catalog = rc.catalog;
1467 pm_strcpy(jcr->catalog_source, _("User input"));
1468 }
1469
1470 pm_strcpy(jcr->comment, rc.comment);
1471
1472 if (rc.where) {
1473 if (jcr->where) {
1474 free(jcr->where);
1475 }
1476 jcr->where = bstrdup(rc.where);
1477 rc.where = NULL;
1478 }
1479
1480 if (rc.regexwhere) {
1481 if (jcr->RegexWhere) {
1482 free(jcr->RegexWhere);
1483 }
1484 jcr->RegexWhere = bstrdup(rc.regexwhere);
1485 rc.regexwhere = NULL;
1486 }
1487
1488 if (rc.when) {
1489 utime_t duration;
1490 jcr->sched_time = str_to_utime(rc.when);
1491 if (jcr->sched_time == 0) {
1492 if (duration_to_utime(rc.when, &duration)) {
1493 jcr->sched_time = time(NULL) + duration;
1494 } else {
1495 ua->send_msg(_("Invalid time, using current time.\n"));
1496 jcr->sched_time = time(NULL);
1497 }
1498 }
1499 rc.when = NULL;
1500 }
1501
1502 if (rc.bootstrap) {
1503 if (jcr->RestoreBootstrap) {
1504 free(jcr->RestoreBootstrap);
1505 }
1506 jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
1507 rc.bootstrap = NULL;
1508 }
1509
1510 if (rc.plugin_options) {
1511 if (jcr->plugin_options) {
1512 free(jcr->plugin_options);
1513 }
1514 jcr->plugin_options = bstrdup(rc.plugin_options);
1515 rc.plugin_options = NULL;
1516 }
1517
1518 if (rc.plugin_config) {
1519 if (jcr->plugin_config) {
1520 free_plugin_config_items(jcr->plugin_config);
1521 delete jcr->plugin_config;
1522 }
1523 jcr->plugin_config = rc.plugin_config;
1524 rc.plugin_config = NULL;
1525 }
1526
1527 if (rc.replace) {
1528 jcr->replace = 0;
1529 for (i=0; ReplaceOptions[i].name; i++) {
1530 if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
1531 jcr->replace = ReplaceOptions[i].token;
1532 }
1533 }
1534 if (!jcr->replace) {
1535 ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
1536 return false;
1537 }
1538 } else if (rc.job->replace) {
1539 jcr->replace = rc.job->replace;
1540 } else {
1541 jcr->replace = REPLACE_ALWAYS;
1542 }
1543 rc.replace = NULL;
1544
1545 /* Set Snapshot Retention (Job <- Client) */
1546 if (jcr->client) {
1547 jcr->snapshot_retention = jcr->client->SnapRetention;
1548 }
1549 if (jcr->job && jcr->job->SnapRetention > 0) {
1550 jcr->snapshot_retention = jcr->job->SnapRetention;
1551 }
1552
1553 if (rc.Priority) {
1554 jcr->JobPriority = rc.Priority;
1555 rc.Priority = 0;
1556 }
1557
1558 if (rc.since) {
1559 if (!jcr->stime) {
1560 jcr->stime = get_pool_memory(PM_MESSAGE);
1561 }
1562 pm_strcpy(jcr->stime, rc.since);
1563 rc.since = NULL;
1564 }
1565
1566 if (rc.cloned) {
1567 jcr->cloned = rc.cloned;
1568 rc.cloned = false;
1569 }
1570
1571 /* If pool changed, update migration write storage */
1572 if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
1573 (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) {
1574 if (!set_mac_wstorage(ua, jcr, rc.pool, rc.next_pool,
1575 jcr->next_pool_source)) {
1576 return false;
1577 }
1578 }
1579 rc.replace = ReplaceOptions[0].name;
1580 for (i=0; ReplaceOptions[i].name; i++) {
1581 if (ReplaceOptions[i].token == (int)jcr->replace) {
1582 rc.replace = ReplaceOptions[i].name;
1583 }
1584 }
1585 if (rc.level_name) {
1586 if (!get_level_from_name(jcr, rc.level_name)) {
1587 ua->send_msg(_("Level \"%s\" not valid.\n"), rc.level_name);
1588 return false;
1589 }
1590 rc.level_name = NULL;
1591 }
1592 if (rc.jid) {
1593 /* Note, this is also MigrateJobId and a VerifyJobId */
1594 jcr->RestoreJobId = str_to_int64(rc.jid);
1595
1596 /* Copy also this parameter for VirtualFull in jcr->JobIds */
1597 if (!jcr->JobIds) {
1598 jcr->JobIds = get_pool_memory(PM_FNAME);
1599 }
1600 pm_strcpy(jcr->JobIds, rc.jid);
1601 jcr->use_all_JobIds = rc.alljobid; /* if we found the "alljobid=" kw */
1602 rc.alljobid = false;
1603 rc.jid = 0;
1604 }
1605
1606 /* Some options are not available through the menu
1607 * TODO: Add an advanced menu?
1608 */
1609 if (rc.spool_data_set) {
1610 jcr->spool_data = rc.spool_data;
1611 }
1612
1613 if (rc.accurate_set) {
1614 jcr->accurate = rc.accurate;
1615 }
1616
1617 /* Used by migration jobs that can have the same name,
1618 * but can run at the same time
1619 */
1620 if (rc.ignoreduplicatecheck_set) {
1621 jcr->IgnoreDuplicateJobChecking = rc.ignoreduplicatecheck;
1622 }
1623
1624 /* Do not start a Backup job from the Scratch Pool */
1625 const char *name;
1626 if (!check_pool(jcr->getJobType(), jcr->getJobLevel(),
1627 rc.pool, rc.next_pool, &name)) {
1628 ua->send_msg(_("%s \"Scratch\" not valid in Job \"%s\".\n"),
1629 name, rc.job->name());
1630 return false;
1631 }
1632
1633 return true;
1634 }
1635
select_where_regexp(UAContext * ua,JCR * jcr)1636 static void select_where_regexp(UAContext *ua, JCR *jcr)
1637 {
1638 alist *regs;
1639 char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
1640 strip_prefix = add_suffix = rwhere = add_prefix = NULL;
1641
1642 try_again_reg:
1643 ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
1644 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
1645
1646 start_prompt(ua, _("This will replace your current Where value\n"));
1647 add_prompt(ua, _("Strip prefix")); /* 0 */
1648 add_prompt(ua, _("Add prefix")); /* 1 */
1649 add_prompt(ua, _("Add file suffix")); /* 2 */
1650 add_prompt(ua, _("Enter a regexp")); /* 3 */
1651 add_prompt(ua, _("Test filename manipulation")); /* 4 */
1652 add_prompt(ua, _("Use this ?")); /* 5 */
1653
1654 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1655 case 0:
1656 /* Strip prefix */
1657 if (get_cmd(ua, _("Please enter the path prefix to strip: "))) {
1658 if (strip_prefix) bfree(strip_prefix);
1659 strip_prefix = bstrdup(ua->cmd);
1660 }
1661
1662 goto try_again_reg;
1663 case 1:
1664 /* Add prefix */
1665 if (get_cmd(ua, _("Please enter the path prefix to add (/ for none): "))) {
1666 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1667 ua->cmd[0] = 0;
1668 }
1669
1670 if (add_prefix) bfree(add_prefix);
1671 add_prefix = bstrdup(ua->cmd);
1672 }
1673 goto try_again_reg;
1674 case 2:
1675 /* Add suffix */
1676 if (get_cmd(ua, _("Please enter the file suffix to add: "))) {
1677 if (add_suffix) bfree(add_suffix);
1678 add_suffix = bstrdup(ua->cmd);
1679 }
1680 goto try_again_reg;
1681 case 3:
1682 /* Add rwhere */
1683 if (get_cmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
1684 if (rwhere) bfree(rwhere);
1685 rwhere = bstrdup(ua->cmd);
1686 }
1687
1688 goto try_again_reg;
1689 case 4:
1690 /* Test regexp */
1691 char *result;
1692 char *regexp;
1693
1694 if (rwhere && rwhere[0] != '\0') {
1695 regs = get_bregexps(rwhere);
1696 ua->send_msg(_("regexwhere=%s\n"), NPRT(rwhere));
1697 } else {
1698 int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1699 regexp = (char *) bmalloc (len * sizeof(char));
1700 bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
1701 regs = get_bregexps(regexp);
1702 ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
1703 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp));
1704
1705 bfree(regexp);
1706 }
1707
1708 if (!regs) {
1709 ua->send_msg(_("Cannot use your regexp\n"));
1710 goto try_again_reg;
1711 }
1712 ua->send_msg(_("Enter a period (.) to stop this test\n"));
1713 while (get_cmd(ua, _("Please enter filename to test: "))) {
1714 apply_bregexps(ua->cmd, regs, &result);
1715 ua->send_msg(_("%s -> %s\n"), ua->cmd, result);
1716 }
1717 free_bregexps(regs);
1718 delete regs;
1719 goto try_again_reg;
1720
1721 case 5:
1722 /* OK */
1723 break;
1724 case -1: /* error or cancel */
1725 goto bail_out_reg;
1726 default:
1727 goto try_again_reg;
1728 }
1729
1730 /* replace the existing where */
1731 if (jcr->where) {
1732 bfree(jcr->where);
1733 jcr->where = NULL;
1734 }
1735
1736 /* replace the existing regexwhere */
1737 if (jcr->RegexWhere) {
1738 bfree(jcr->RegexWhere);
1739 jcr->RegexWhere = NULL;
1740 }
1741
1742 if (rwhere) {
1743 jcr->RegexWhere = bstrdup(rwhere);
1744 } else if (strip_prefix || add_prefix || add_suffix) {
1745 int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1746 jcr->RegexWhere = (char *) bmalloc(len*sizeof(char));
1747 bregexp_build_where(jcr->RegexWhere, len, strip_prefix, add_prefix, add_suffix);
1748 }
1749
1750 regs = get_bregexps(jcr->RegexWhere);
1751 if (regs) {
1752 free_bregexps(regs);
1753 delete regs;
1754 } else {
1755 if (jcr->RegexWhere) {
1756 bfree(jcr->RegexWhere);
1757 jcr->RegexWhere = NULL;
1758 }
1759 ua->send_msg(_("Cannot use your regexp.\n"));
1760 }
1761
1762 bail_out_reg:
1763 if (strip_prefix) bfree(strip_prefix);
1764 if (add_prefix) bfree(add_prefix);
1765 if (add_suffix) bfree(add_suffix);
1766 if (rwhere) bfree(rwhere);
1767 }
1768
select_job_level(UAContext * ua,JCR * jcr)1769 static void select_job_level(UAContext *ua, JCR *jcr)
1770 {
1771 if (jcr->getJobType() == JT_BACKUP) {
1772 start_prompt(ua, _("Levels:\n"));
1773 // add_prompt(ua, _("Base"));
1774 add_prompt(ua, _("Full"));
1775 add_prompt(ua, _("Incremental"));
1776 add_prompt(ua, _("Differential"));
1777 add_prompt(ua, _("Since"));
1778 add_prompt(ua, _("VirtualFull"));
1779 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
1780 // case 0:
1781 // jcr->JobLevel = L_BASE;
1782 // break;
1783 case 0:
1784 jcr->setJobLevel(L_FULL);
1785 break;
1786 case 1:
1787 jcr->setJobLevel(L_INCREMENTAL);
1788 break;
1789 case 2:
1790 jcr->setJobLevel(L_DIFFERENTIAL);
1791 break;
1792 case 3:
1793 jcr->setJobLevel(L_SINCE);
1794 break;
1795 case 4:
1796 jcr->setJobLevel(L_VIRTUAL_FULL);
1797 break;
1798 default:
1799 break;
1800 }
1801 } else if (jcr->getJobType() == JT_VERIFY) {
1802 start_prompt(ua, _("Levels:\n"));
1803 add_prompt(ua, _("Initialize Catalog"));
1804 add_prompt(ua, _("Verify Catalog"));
1805 add_prompt(ua, _("Verify Volume to Catalog"));
1806 add_prompt(ua, _("Verify Disk to Catalog"));
1807 add_prompt(ua, _("Verify Volume Data"));
1808 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
1809 case 0:
1810 jcr->setJobLevel(L_VERIFY_INIT);
1811 break;
1812 case 1:
1813 jcr->setJobLevel(L_VERIFY_CATALOG);
1814 break;
1815 case 2:
1816 jcr->setJobLevel(L_VERIFY_VOLUME_TO_CATALOG);
1817 break;
1818 case 3:
1819 jcr->setJobLevel(L_VERIFY_DISK_TO_CATALOG);
1820 break;
1821 case 4:
1822 jcr->setJobLevel(L_VERIFY_DATA);
1823 break;
1824 default:
1825 break;
1826 }
1827 } else {
1828 ua->warning_msg(_("Level not appropriate for this Job. Cannot be changed.\n"));
1829 }
1830 return;
1831 }
1832
display_job_parameters(UAContext * ua,JCR * jcr,JOB * job,const char * verify_list,char * jid,const char * replace,char * client_name)1833 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char *verify_list,
1834 char *jid, const char *replace, char *client_name)
1835 {
1836 char ec1[30], edl[50];
1837 char dt[MAX_TIME_LENGTH];
1838
1839 Dmsg1(800, "JobType=%c\n", jcr->getJobType());
1840 switch (jcr->getJobType()) {
1841 case JT_ADMIN:
1842 if (ua->api) {
1843 ua->signal(BNET_RUN_CMD);
1844 ua->send_msg("Type: Admin\n"
1845 "Title: Run Admin Job\n"
1846 "JobName: %s\n"
1847 "FileSet: %s\n"
1848 "Client: %s\n"
1849 "Storage: %s\n"
1850 "When: %s\n"
1851 "Priority: %d\n",
1852 job->name(),
1853 jcr->fileset->name(),
1854 NPRT(jcr->client->name()),
1855 jcr->wstore?jcr->wstore->name():"*None*",
1856 bstrutime(dt, sizeof(dt), jcr->sched_time),
1857 jcr->JobPriority);
1858 } else {
1859 ua->send_msg(_("Run Admin Job\n"
1860 "JobName: %s\n"
1861 "FileSet: %s\n"
1862 "Client: %s\n"
1863 "Storage: %s\n"
1864 "When: %s\n"
1865 "Priority: %d\n"),
1866 job->name(),
1867 jcr->fileset->name(),
1868 NPRT(jcr->client->name()),
1869 jcr->wstore?jcr->wstore->name():"*None*",
1870 bstrutime(dt, sizeof(dt), jcr->sched_time),
1871 jcr->JobPriority);
1872 }
1873 jcr->setJobLevel(L_FULL);
1874 break;
1875 case JT_BACKUP:
1876 case JT_VERIFY:
1877 char next_pool[MAX_NAME_LENGTH + 50];
1878 next_pool[0] = 0;
1879 if (jcr->getJobType() == JT_BACKUP) {
1880 if (ua->api) {
1881 ua->signal(BNET_RUN_CMD);
1882 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1883 bsnprintf(next_pool, sizeof(next_pool), "NextPool: %s\n",
1884 jcr->next_pool ? jcr->next_pool->name() : "*None*");
1885 }
1886 ua->send_msg("Type: Backup\n"
1887 "Title: Run Backup Job\n"
1888 "JobName: %s\n"
1889 "Level: %s\n"
1890 "Client: %s\n"
1891 "FileSet: %s\n"
1892 "Pool: %s\n"
1893 "%s"
1894 "Storage: %s\n"
1895 "When: %s\n"
1896 "Priority: %d\n"
1897 "%s%s%s",
1898 job->name(),
1899 level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
1900 jcr->client->name(),
1901 jcr->fileset->name(),
1902 NPRT(jcr->pool->name()),
1903 next_pool,
1904 jcr->wstore?jcr->wstore->name():"*None*",
1905 bstrutime(dt, sizeof(dt), jcr->sched_time),
1906 jcr->JobPriority,
1907 jcr->plugin_options?"Plugin Options: ":"",
1908 jcr->plugin_options?jcr->plugin_options:"",
1909 jcr->plugin_options?"\n":"");
1910 } else {
1911 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1912 bsnprintf(next_pool, sizeof(next_pool),
1913 "NextPool: %s (From %s)\n",
1914 jcr->next_pool ? jcr->next_pool->name() : "*None*",
1915 jcr->next_pool_source);
1916 }
1917 ua->send_msg(_("Run Backup job\n"
1918 "JobName: %s\n"
1919 "Level: %s\n"
1920 "Client: %s\n"
1921 "FileSet: %s\n"
1922 "Pool: %s (From %s)\n"
1923 "%s"
1924 "Storage: %s (From %s)\n"
1925 "When: %s\n"
1926 "Priority: %d\n"
1927 "%s%s%s"),
1928 job->name(),
1929 level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
1930 jcr->client->name(),
1931 jcr->fileset->name(),
1932 NPRT(jcr->pool->name()), jcr->pool_source,
1933 next_pool,
1934 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1935 bstrutime(dt, sizeof(dt), jcr->sched_time),
1936 jcr->JobPriority,
1937 jcr->plugin_options?"Plugin Options: ":"",
1938 jcr->plugin_options?jcr->plugin_options:"",
1939 jcr->plugin_options?"\n":"");
1940 }
1941 } else { /* JT_VERIFY */
1942 JOB_DBR jr;
1943 const char *Name;
1944 if (jcr->verify_job) {
1945 Name = jcr->verify_job->name();
1946 } else if (jcr->RestoreJobId) { /* Display job name if jobid requested */
1947 memset(&jr, 0, sizeof(jr));
1948 jr.JobId = jcr->RestoreJobId;
1949 if (!db_get_job_record(jcr, ua->db, &jr)) {
1950 ua->error_msg(_("Could not get job record for selected JobId. ERR=%s"),
1951 db_strerror(ua->db));
1952 return false;
1953 }
1954 Name = jr.Job;
1955 } else {
1956 Name = "";
1957 }
1958 if (!verify_list) {
1959 verify_list = job->WriteVerifyList;
1960 }
1961 if (!verify_list) {
1962 verify_list = "";
1963 }
1964 if (ua->api) {
1965 ua->signal(BNET_RUN_CMD);
1966 ua->send_msg("Type: Verify\n"
1967 "Title: Run Verify Job\n"
1968 "JobName: %s\n"
1969 "Level: %s\n"
1970 "Client: %s\n"
1971 "FileSet: %s\n"
1972 "Pool: %s (From %s)\n"
1973 "Storage: %s (From %s)\n"
1974 "Verify Job: %s\n"
1975 "Verify List: %s\n"
1976 "When: %s\n"
1977 "Priority: %d\n",
1978 job->name(),
1979 level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
1980 jcr->client->name(),
1981 jcr->fileset->name(),
1982 NPRT(jcr->pool->name()), jcr->pool_source,
1983 jcr->rstore->name(), jcr->rstore_source,
1984 Name,
1985 verify_list,
1986 bstrutime(dt, sizeof(dt), jcr->sched_time),
1987 jcr->JobPriority);
1988 } else {
1989 ua->send_msg(_("Run Verify Job\n"
1990 "JobName: %s\n"
1991 "Level: %s\n"
1992 "Client: %s\n"
1993 "FileSet: %s\n"
1994 "Pool: %s (From %s)\n"
1995 "Storage: %s (From %s)\n"
1996 "Verify Job: %s\n"
1997 "Verify List: %s\n"
1998 "When: %s\n"
1999 "Priority: %d\n"),
2000 job->name(),
2001 level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
2002 jcr->client->name(),
2003 jcr->fileset->name(),
2004 NPRT(jcr->pool->name()), jcr->pool_source,
2005 jcr->rstore->name(), jcr->rstore_source,
2006 Name,
2007 verify_list,
2008 bstrutime(dt, sizeof(dt), jcr->sched_time),
2009 jcr->JobPriority);
2010 }
2011 }
2012 break;
2013 case JT_RESTORE:
2014 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
2015 if (jid) {
2016 jcr->RestoreJobId = str_to_int64(jid);
2017 } else {
2018 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
2019 return false;
2020 }
2021 jcr->RestoreJobId = ua->int64_val;
2022 }
2023 }
2024 jcr->setJobLevel(L_FULL); /* default level */
2025 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
2026 if (jcr->RestoreJobId == 0) {
2027 /* RegexWhere is take before RestoreWhere */
2028 if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
2029 if (ua->api) {
2030 ua->signal(BNET_RUN_CMD);
2031 ua->send_msg("Type: Restore\n"
2032 "Title: Run Restore Job\n"
2033 "JobName: %s\n"
2034 "Bootstrap: %s\n"
2035 "RegexWhere: %s\n"
2036 "Replace: %s\n"
2037 "FileSet: %s\n"
2038 "Backup Client: %s\n"
2039 "Restore Client: %s\n"
2040 "Storage: %s\n"
2041 "When: %s\n"
2042 "Catalog: %s\n"
2043 "Priority: %d\n"
2044 "Plugin Options: %s\n",
2045 job->name(),
2046 NPRT(jcr->RestoreBootstrap),
2047 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
2048 replace,
2049 jcr->fileset->name(),
2050 client_name,
2051 jcr->client->name(),
2052 jcr->rstore->name(),
2053 bstrutime(dt, sizeof(dt), jcr->sched_time),
2054 jcr->catalog->name(),
2055 jcr->JobPriority,
2056 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2057 _("User specified") : _("*None*"));
2058 } else {
2059 ua->send_msg(_("Run Restore job\n"
2060 "JobName: %s\n"
2061 "Bootstrap: %s\n"
2062 "RegexWhere: %s\n"
2063 "Replace: %s\n"
2064 "FileSet: %s\n"
2065 "Backup Client: %s\n"
2066 "Restore Client: %s\n"
2067 "Storage: %s\n"
2068 "When: %s\n"
2069 "Catalog: %s\n"
2070 "Priority: %d\n"
2071 "Plugin Options: %s\n"),
2072 job->name(),
2073 NPRT(jcr->RestoreBootstrap),
2074 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
2075 replace,
2076 jcr->fileset->name(),
2077 client_name,
2078 jcr->client->name(),
2079 jcr->rstore->name(),
2080 bstrutime(dt, sizeof(dt), jcr->sched_time),
2081 jcr->catalog->name(),
2082 jcr->JobPriority,
2083 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2084 _("User specified") : _("*None*"));
2085 }
2086 } else {
2087 if (ua->api) {
2088 ua->signal(BNET_RUN_CMD);
2089 ua->send_msg("Type: Restore\n"
2090 "Title: Run Restore job\n"
2091 "JobName: %s\n"
2092 "Bootstrap: %s\n"
2093 "Where: %s\n"
2094 "Replace: %s\n"
2095 "FileSet: %s\n"
2096 "Backup Client: %s\n"
2097 "Restore Client: %s\n"
2098 "Storage: %s\n"
2099 "When: %s\n"
2100 "Catalog: %s\n"
2101 "Priority: %d\n"
2102 "Plugin Options: %s\n",
2103 job->name(),
2104 NPRT(jcr->RestoreBootstrap),
2105 jcr->where?jcr->where:NPRT(job->RestoreWhere),
2106 replace,
2107 jcr->fileset->name(),
2108 client_name,
2109 jcr->client->name(),
2110 jcr->rstore->name(),
2111 bstrutime(dt, sizeof(dt), jcr->sched_time),
2112 jcr->catalog->name(),
2113 jcr->JobPriority,
2114 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2115 _("User specified") : _("*None*"));
2116 } else {
2117 ua->send_msg(_("Run Restore job\n"
2118 "JobName: %s\n"
2119 "Bootstrap: %s\n"
2120 "Where: %s\n"
2121 "Replace: %s\n"
2122 "FileSet: %s\n"
2123 "Backup Client: %s\n"
2124 "Restore Client: %s\n"
2125 "Storage: %s\n"
2126 "When: %s\n"
2127 "Catalog: %s\n"
2128 "Priority: %d\n"
2129 "Plugin Options: %s\n"),
2130 job->name(),
2131 NPRT(jcr->RestoreBootstrap),
2132 jcr->where?jcr->where:NPRT(job->RestoreWhere),
2133 replace,
2134 jcr->fileset->name(),
2135 client_name,
2136 jcr->client->name(),
2137 jcr->rstore->name(),
2138 bstrutime(dt, sizeof(dt), jcr->sched_time),
2139 jcr->catalog->name(),
2140 jcr->JobPriority,
2141 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2142 _("User specified") : _("*None*"));
2143 }
2144 }
2145
2146 } else {
2147 /* ***FIXME*** This needs to be fixed for bat */
2148 if (ua->api) ua->signal(BNET_RUN_CMD);
2149 ua->send_msg(_("Run Restore job\n"
2150 "JobName: %s\n"
2151 "Bootstrap: %s\n"),
2152 job->name(),
2153 NPRT(jcr->RestoreBootstrap));
2154
2155 /* RegexWhere is take before RestoreWhere */
2156 if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
2157 ua->send_msg(_("RegexWhere: %s\n"),
2158 jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere);
2159 } else {
2160 ua->send_msg(_("Where: %s\n"),
2161 jcr->where?jcr->where:NPRT(job->RestoreWhere));
2162 }
2163
2164 ua->send_msg(_("Replace: %s\n"
2165 "Client: %s\n"
2166 "Storage: %s\n"
2167 "JobId: %s\n"
2168 "When: %s\n"
2169 "Catalog: %s\n"
2170 "Priority: %d\n"
2171 "Plugin Options: %s\n"),
2172 replace,
2173 jcr->client->name(),
2174 jcr->rstore->name(),
2175 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
2176 bstrutime(dt, sizeof(dt), jcr->sched_time),
2177 jcr->catalog->name(),
2178 jcr->JobPriority,
2179 (jcr->plugin_config && jcr->plugin_config->size() > 0) ?
2180 _("User specified") : _("*None*"));
2181 }
2182 break;
2183 case JT_COPY:
2184 case JT_MIGRATE:
2185 char *prt_type;
2186 jcr->setJobLevel(L_FULL); /* default level */
2187 if (ua->api) {
2188 ua->signal(BNET_RUN_CMD);
2189 if (jcr->getJobType() == JT_COPY) {
2190 prt_type = (char *)"Type: Copy\nTitle: Run Copy Job\n";
2191 } else {
2192 prt_type = (char *)"Type: Migration\nTitle: Run Migration Job\n";
2193 }
2194 ua->send_msg("%s"
2195 "JobName: %s\n"
2196 "Bootstrap: %s\n"
2197 "Client: %s\n"
2198 "FileSet: %s\n"
2199 "Pool: %s\n"
2200 "NextPool: %s\n"
2201 "Read Storage: %s\n"
2202 "Write Storage: %s\n"
2203 "JobId: %s\n"
2204 "When: %s\n"
2205 "Catalog: %s\n"
2206 "Priority: %d\n",
2207 prt_type,
2208 job->name(),
2209 NPRT(jcr->RestoreBootstrap),
2210 jcr->client->name(),
2211 jcr->fileset->name(),
2212 NPRT(jcr->pool->name()),
2213 jcr->next_pool?jcr->next_pool->name():"*None*",
2214 jcr->rstore->name(),
2215 jcr->wstore?jcr->wstore->name():"*None*",
2216 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
2217 bstrutime(dt, sizeof(dt), jcr->sched_time),
2218 jcr->catalog->name(),
2219 jcr->JobPriority);
2220 } else {
2221 if (jcr->getJobType() == JT_COPY) {
2222 prt_type = _("Run Copy job\n");
2223 } else {
2224 prt_type = _("Run Migration job\n");
2225 }
2226 ua->send_msg("%s"
2227 "JobName: %s\n"
2228 "Bootstrap: %s\n"
2229 "Client: %s\n"
2230 "FileSet: %s\n"
2231 "Pool: %s (From %s)\n"
2232 "NextPool: %s (From %s)\n"
2233 "Read Storage: %s (From %s)\n"
2234 "Write Storage: %s (From %s)\n"
2235 "JobId: %s\n"
2236 "When: %s\n"
2237 "Catalog: %s\n"
2238 "Priority: %d\n",
2239 prt_type,
2240 job->name(),
2241 NPRT(jcr->RestoreBootstrap),
2242 jcr->client->name(),
2243 jcr->fileset->name(),
2244 NPRT(jcr->pool->name()), jcr->pool_source,
2245 jcr->next_pool?jcr->next_pool->name():"*None*",
2246 NPRT(jcr->next_pool_source),
2247 jcr->rstore->name(), jcr->rstore_source,
2248 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
2249 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
2250 bstrutime(dt, sizeof(dt), jcr->sched_time),
2251 jcr->catalog->name(),
2252 jcr->JobPriority);
2253 }
2254 break;
2255 default:
2256 ua->error_msg(_("Unknown Job Type=%d\n"), jcr->getJobType());
2257 return false;
2258 }
2259 return true;
2260 }
2261
2262
scan_run_command_line_arguments(UAContext * ua,run_ctx & rc)2263 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc)
2264 {
2265 bool kw_ok;
2266 int i, j;
2267 static const char *kw[] = { /* command line arguments */
2268 "alljobid", /* 0 Used in a switch() */
2269 "jobid", /* 1 */
2270 "client", /* 2 */
2271 "fd",
2272 "fileset", /* 4 */
2273 "level", /* 5 */
2274 "storage", /* 6 */
2275 "sd", /* 7 */
2276 "regexwhere", /* 8 where string as a bregexp */
2277 "where", /* 9 */
2278 "bootstrap", /* 10 */
2279 "replace", /* 11 */
2280 "when", /* 12 */
2281 "priority", /* 13 */
2282 "yes", /* 14 -- if you change this change YES_POS too */
2283 "verifyjob", /* 15 */
2284 "files", /* 16 number of files to restore */
2285 "catalog", /* 17 override catalog */
2286 "since", /* 18 since */
2287 "cloned", /* 19 cloned */
2288 "verifylist", /* 20 verify output list */
2289 "migrationjob", /* 21 migration job name */
2290 "pool", /* 22 */
2291 "backupclient", /* 23 */
2292 "restoreclient", /* 24 */
2293 "pluginoptions", /* 25 */
2294 "spooldata", /* 26 */
2295 "comment", /* 27 */
2296 "ignoreduplicatecheck", /* 28 */
2297 "accurate", /* 29 */
2298 "job", /* 30 */
2299 "mediatype", /* 31 */
2300 "nextpool", /* 32 override next pool name */
2301 "fdcalled", /* 33 */
2302
2303 "jobuser", /* 34 */
2304 "jobgroup", /* 35 */
2305 NULL};
2306
2307 #define YES_POS 14
2308
2309 rc.catalog_name = NULL;
2310 rc.job_name = NULL;
2311 rc.pool_name = NULL;
2312 rc.next_pool_name = NULL;
2313 rc.store_name = NULL;
2314 rc.client_name = NULL;
2315 rc.media_type = NULL;
2316 rc.restore_client_name = NULL;
2317 rc.fileset_name = NULL;
2318 rc.verify_job_name = NULL;
2319 rc.previous_job_name = NULL;
2320 rc.accurate_set = false;
2321 rc.spool_data_set = false;
2322 rc.ignoreduplicatecheck = false;
2323 rc.comment = NULL;
2324 rc.job_group = NULL;
2325 rc.job_user = NULL;
2326 free_plugin_config_items(rc.plugin_config);
2327
2328 for (i=1; i<ua->argc; i++) {
2329 Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
2330 kw_ok = false;
2331 /* Keep looking until we find a good keyword */
2332 for (j=0; !kw_ok && kw[j]; j++) {
2333 if (strcasecmp(ua->argk[i], kw[j]) == 0) {
2334 /* Note, yes and run have no value, so do not fail */
2335 if (!ua->argv[i] && j != YES_POS /*yes*/) {
2336 ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
2337 return false;
2338 }
2339 Dmsg2(800, "Got j=%d keyword=%s\n", j, NPRT(kw[j]));
2340 switch (j) {
2341 case 0: /* alljobid */
2342 rc.alljobid = true;
2343 /* Fall through wanted */
2344 case 1: /* JobId */
2345 if (rc.jid && !rc.mod) {
2346 ua->send_msg(_("JobId specified twice.\n"));
2347 return false;
2348 }
2349 rc.jid = ua->argv[i];
2350 kw_ok = true;
2351 break;
2352 case 2: /* client */
2353 case 3: /* fd */
2354 if (rc.client_name) {
2355 ua->send_msg(_("Client specified twice.\n"));
2356 return false;
2357 }
2358 rc.client_name = ua->argv[i];
2359 kw_ok = true;
2360 break;
2361 case 4: /* fileset */
2362 if (rc.fileset_name) {
2363 ua->send_msg(_("FileSet specified twice.\n"));
2364 return false;
2365 }
2366 rc.fileset_name = ua->argv[i];
2367 kw_ok = true;
2368 break;
2369 case 5: /* level */
2370 if (rc.level_name) {
2371 ua->send_msg(_("Level specified twice.\n"));
2372 return false;
2373 }
2374 rc.level_name = ua->argv[i];
2375 kw_ok = true;
2376 break;
2377 case 6: /* storage */
2378 case 7: /* sd */
2379 if (rc.store_name) {
2380 ua->send_msg(_("Storage specified twice.\n"));
2381 return false;
2382 }
2383 rc.store_name = ua->argv[i];
2384 kw_ok = true;
2385 break;
2386 case 8: /* regexwhere */
2387 if ((rc.regexwhere || rc.where) && !rc.mod) {
2388 ua->send_msg(_("RegexWhere or Where specified twice.\n"));
2389 return false;
2390 }
2391 rc.regexwhere = ua->argv[i];
2392 if (!acl_access_ok(ua, Where_ACL, rc.regexwhere)) {
2393 ua->send_msg(_("No authorization for \"regexwhere\" specification.\n"));
2394 return false;
2395 }
2396 kw_ok = true;
2397 break;
2398 case 9: /* where */
2399 if ((rc.where || rc.regexwhere) && !rc.mod) {
2400 ua->send_msg(_("Where or RegexWhere specified twice.\n"));
2401 return false;
2402 }
2403 rc.where = ua->argv[i];
2404 if (!acl_access_ok(ua, Where_ACL, rc.where)) {
2405 ua->send_msg(_("No authoriztion for \"where\" specification.\n"));
2406 return false;
2407 }
2408 kw_ok = true;
2409 break;
2410 case 10: /* bootstrap */
2411 if (rc.bootstrap && !rc.mod) {
2412 ua->send_msg(_("Bootstrap specified twice.\n"));
2413 return false;
2414 }
2415 rc.bootstrap = ua->argv[i];
2416 kw_ok = true;
2417 break;
2418 case 11: /* replace */
2419 if (rc.replace && !rc.mod) {
2420 ua->send_msg(_("Replace specified twice.\n"));
2421 return false;
2422 }
2423 rc.replace = ua->argv[i];
2424 kw_ok = true;
2425 break;
2426 case 12: /* When */
2427 if (rc.when && !rc.mod) {
2428 ua->send_msg(_("When specified twice.\n"));
2429 return false;
2430 }
2431 rc.when = ua->argv[i];
2432 kw_ok = true;
2433 break;
2434 case 13: /* Priority */
2435 if (rc.Priority && !rc.mod) {
2436 ua->send_msg(_("Priority specified twice.\n"));
2437 return false;
2438 }
2439 rc.Priority = atoi(ua->argv[i]);
2440 if (rc.Priority <= 0) {
2441 ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
2442 rc.Priority = 10;
2443 }
2444 kw_ok = true;
2445 break;
2446 case 14: /* yes */
2447 kw_ok = true;
2448 break;
2449 case 15: /* Verify Job */
2450 if (rc.verify_job_name) {
2451 ua->send_msg(_("Verify Job specified twice.\n"));
2452 return false;
2453 }
2454 rc.verify_job_name = ua->argv[i];
2455 kw_ok = true;
2456 break;
2457 case 16: /* files */
2458 rc.files = atoi(ua->argv[i]);
2459 kw_ok = true;
2460 break;
2461 case 17: /* catalog */
2462 rc.catalog_name = ua->argv[i];
2463 kw_ok = true;
2464 break;
2465 case 18: /* since */
2466 rc.since = ua->argv[i];
2467 kw_ok = true;
2468 break;
2469 case 19: /* cloned */
2470 rc. cloned = true;
2471 kw_ok = true;
2472 break;
2473 case 20: /* write verify list output */
2474 rc.verify_list = ua->argv[i];
2475 kw_ok = true;
2476 break;
2477 case 21: /* Migration Job */
2478 if (rc.previous_job_name) {
2479 ua->send_msg(_("Migration Job specified twice.\n"));
2480 return false;
2481 }
2482 rc.previous_job_name = ua->argv[i];
2483 kw_ok = true;
2484 break;
2485 case 22: /* pool */
2486 if (rc.pool_name) {
2487 ua->send_msg(_("Pool specified twice.\n"));
2488 return false;
2489 }
2490 rc.pool_name = ua->argv[i];
2491 kw_ok = true;
2492 break;
2493 case 23: /* backupclient */
2494 if (rc.client_name) {
2495 ua->send_msg(_("Client specified twice.\n"));
2496 return 0;
2497 }
2498 rc.client_name = ua->argv[i];
2499 kw_ok = true;
2500 break;
2501 case 24: /* restoreclient */
2502 if (rc.restore_client_name && !rc.mod) {
2503 ua->send_msg(_("Restore Client specified twice.\n"));
2504 return false;
2505 }
2506 rc.restore_client_name = ua->argv[i];
2507 kw_ok = true;
2508 break;
2509 case 25: /* pluginoptions */
2510 ua->send_msg(_("Plugin Options not yet implemented.\n"));
2511 return false;
2512 if (rc.plugin_options) {
2513 ua->send_msg(_("Plugin Options specified twice.\n"));
2514 return false;
2515 }
2516 rc.plugin_options = ua->argv[i];
2517 if (!acl_access_ok(ua, PluginOptions_ACL, rc.plugin_options)) {
2518 ua->send_msg(_("No authoriztion for \"PluginOptions\" specification.\n"));
2519 return false;
2520 }
2521 kw_ok = true;
2522 break;
2523 case 26: /* spooldata */
2524 if (rc.spool_data_set) {
2525 ua->send_msg(_("Spool flag specified twice.\n"));
2526 return false;
2527 }
2528 if (is_yesno(ua->argv[i], &rc.spool_data)) {
2529 rc.spool_data_set = true;
2530 kw_ok = true;
2531 } else {
2532 ua->send_msg(_("Invalid spooldata flag.\n"));
2533 }
2534 break;
2535 case 27: /* comment */
2536 rc.comment = ua->argv[i];
2537 kw_ok = true;
2538 break;
2539 case 28: /* ignoreduplicatecheck */
2540 if (rc.ignoreduplicatecheck_set) {
2541 ua->send_msg(_("IgnoreDuplicateCheck flag specified twice.\n"));
2542 return false;
2543 }
2544 if (is_yesno(ua->argv[i], &rc.ignoreduplicatecheck)) {
2545 rc.ignoreduplicatecheck_set = true;
2546 kw_ok = true;
2547 } else {
2548 ua->send_msg(_("Invalid ignoreduplicatecheck flag.\n"));
2549 }
2550 break;
2551 case 29: /* accurate */
2552 if (rc.accurate_set) {
2553 ua->send_msg(_("Accurate flag specified twice.\n"));
2554 return false;
2555 }
2556 if (is_yesno(ua->argv[i], &rc.accurate)) {
2557 rc.accurate_set = true;
2558 kw_ok = true;
2559 } else {
2560 ua->send_msg(_("Invalid accurate flag.\n"));
2561 }
2562 break;
2563 case 30: /* job */
2564 if (rc.job_name) {
2565 ua->send_msg(_("Job name specified twice.\n"));
2566 return false;
2567 }
2568 rc.job_name = ua->argv[i];
2569 kw_ok = true;
2570 break;
2571 case 31: /* mediatype */
2572 if (rc.media_type) {
2573 ua->send_msg(_("Media Type specified twice.\n"));
2574 return false;
2575 }
2576 rc.media_type = ua->argv[i];
2577 kw_ok = true;
2578 break;
2579 case 32: /* Next Pool */
2580 if (rc.next_pool_name) {
2581 ua->send_msg(_("NextPool specified twice.\n"));
2582 return false;
2583 }
2584 rc.next_pool_name = ua->argv[i];
2585 kw_ok = true;
2586 break;
2587 case 33: /* fdcalled */
2588 kw_ok = true;
2589 break;
2590 case 34: /* job user */
2591 if (rc.job_user) {
2592 ua->send_msg(_("JobUser specified twice.\n"));
2593 return false;
2594 }
2595 rc.job_user = ua->argv[i];
2596 kw_ok = true;
2597 break;
2598 case 35: /* job group */
2599 if (rc.job_group) {
2600 ua->send_msg(_("JobGroup specified twice.\n"));
2601 return false;
2602 }
2603 rc.job_group = ua->argv[i];
2604 kw_ok = true;
2605 break;
2606 default:
2607 break;
2608 }
2609 } /* end strcase compare */
2610 } /* end keyword loop */
2611
2612 /*
2613 * End of keyword for loop -- if not found, we got a bogus keyword
2614 */
2615 if (!kw_ok) {
2616 Dmsg1(800, "%s not found\n", ua->argk[i]);
2617 /*
2618 * Special case for Job Name, it can be the first
2619 * keyword that has no value.
2620 */
2621 if (!rc.job_name && !ua->argv[i]) {
2622 rc.job_name = ua->argk[i]; /* use keyword as job name */
2623 Dmsg1(800, "Set jobname=%s\n", rc.job_name);
2624 } else {
2625 ua->send_msg(_("Invalid keyword: %s\n"), ua->argk[i]);
2626 return false;
2627 }
2628 }
2629 } /* end argc loop */
2630
2631 Dmsg0(800, "Done scan.\n");
2632 if (rc.comment) {
2633 if (!is_comment_legal(ua, rc.comment)) {
2634 return false;
2635 }
2636 }
2637 if (rc.catalog_name) {
2638 rc.catalog = GetCatalogResWithName(rc.catalog_name);
2639 if (rc.catalog == NULL) {
2640 ua->error_msg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
2641 return false;
2642 }
2643 if (!acl_access_ok(ua, Catalog_ACL, rc.catalog->name())) {
2644 ua->error_msg(_("No authorization. Catalog \"%s\".\n"), rc.catalog->name());
2645 return false;
2646 }
2647 }
2648 Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
2649
2650 if (!get_job(ua, rc)) {
2651 return false;
2652 }
2653
2654 if (!get_pool(ua, rc)) {
2655 return false;
2656 }
2657
2658 if (!get_next_pool(ua, rc)) {
2659 return false;
2660 }
2661
2662 if (!get_storage(ua, rc)) {
2663 return false;
2664 }
2665
2666
2667 if (!get_client(ua, rc)) {
2668 return false;
2669 }
2670
2671 if (!get_fileset(ua, rc)) {
2672 return false;
2673 }
2674
2675 if (rc.verify_job_name) {
2676 rc.verify_job = GetJobResWithName(rc.verify_job_name);
2677 if (!rc.verify_job) {
2678 ua->send_msg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
2679 rc.verify_job = select_job_resource(ua);
2680 }
2681 } else if (!rc.verify_job) {
2682 rc.verify_job = rc.job->verify_job;
2683 }
2684
2685 if (rc.previous_job_name) {
2686 rc.previous_job = GetJobResWithName(rc.previous_job_name);
2687 if (!rc.previous_job) {
2688 ua->send_msg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
2689 rc.previous_job = select_job_resource(ua);
2690 }
2691 } else {
2692 rc.previous_job = rc.job->verify_job;
2693 }
2694 return true;
2695 }
2696