]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_run.c
0aeb67696bb9e953b28f03dababc4c92d5f4fe34
[bacula/bacula] / bacula / src / dird / ua_run.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *   Bacula Director -- Run Command
23  *
24  *     Kern Sibbald, December MMI
25  *
26  */
27
28 #include "bacula.h"
29 #include "dird.h"
30
31 const char *get_command(int index);
32
33 class run_ctx {
34 public:
35    char *job_name, *level_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    const char *replace;
39    char *when, *verify_job_name, *catalog_name;
40    char *previous_job_name;
41    char *since;
42    char *plugin_options;
43    const char *verify_list;
44    JOB *job;
45    JOB *verify_job;
46    JOB *previous_job;
47    JOB_DBR jr;
48    USTORE *store;
49    CLIENT *client;
50    FILESET *fileset;
51    POOL *pool;
52    POOL *next_pool;
53    CAT *catalog;
54    JobId_t JobId;
55    alist *JobIds;
56    int Priority;
57    int files;
58    bool cloned;
59    bool mod;
60    bool restart;
61    bool done;
62    bool alljobid;
63    int spool_data;
64    bool spool_data_set;
65    int accurate;
66    bool accurate_set;
67    int ignoreduplicatecheck;
68    bool ignoreduplicatecheck_set;
69    /* Methods */
70    run_ctx() { memset(this, 0, sizeof(run_ctx));
71                store = new USTORE; };
72    ~run_ctx() {
73       delete store;
74       if (JobIds) {
75          delete JobIds;
76       }
77    };
78 };
79
80 /* Forward referenced subroutines */
81 static void select_job_level(UAContext *ua, JCR *jcr);
82 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job,
83                 const char *verify_list, char *jid, const char *replace,
84                 char *client_name);
85 static void select_where_regexp(UAContext *ua, JCR *jcr);
86 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc);
87 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc);
88 static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc);
89 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc);
90
91 /* Imported variables */
92 extern struct s_kw ReplaceOptions[];
93
94 /*
95  * For Backup and Verify Jobs
96  *     run [job=]<job-name> level=<level-name>
97  *
98  * For Restore Jobs
99  *     run <job-name>
100  *
101  *  Returns: 0 on error
102  *           JobId if OK
103  *
104  */
105 int run_cmd(UAContext *ua, const char *cmd)
106 {
107    JCR *jcr = NULL;
108    run_ctx rc;
109    int status;
110
111    if (!open_client_db(ua)) {
112       goto bail_out;
113    }
114
115    if (!scan_run_command_line_arguments(ua, rc)) {
116       goto bail_out;
117    }
118
119    if (find_arg(ua, NT_("fdcalled")) > 0) {
120       jcr->file_bsock = dup_bsock(ua->UA_sock);
121       ua->quit = true;
122    }
123
124    for ( ;; ) {
125       /*
126        * Create JCR to run job.  NOTE!!! after this point, free_jcr()
127        *  before returning.
128        */
129       if (!jcr) {
130          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
131          set_jcr_defaults(jcr, rc.job);
132          jcr->unlink_bsr = ua->jcr->unlink_bsr;    /* copy unlink flag from caller */
133          ua->jcr->unlink_bsr = false;
134       }
135       /* Transfer JobIds to new restore Job */
136       if (ua->jcr->JobIds) {
137          jcr->JobIds = ua->jcr->JobIds;
138          ua->jcr->JobIds = NULL;
139       }
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;
146       }
147
148       if (!set_run_context_in_jcr(ua, jcr, rc)) {
149          break; /* error get out of while loop */
150       }
151
152
153       /* Run without prompting? */
154       if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
155          return start_job(ua, jcr, rc);
156       }
157
158       /*
159        * Prompt User to see if all run job parameters are correct, and
160        *   allow him to modify them.
161        */
162       if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace,
163            rc.client_name)) {
164          break; /* error get out of while loop */
165       }
166
167       if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
168          break; /* error get out of while loop */
169       }
170
171       if (strncasecmp(ua->cmd, ".mod ", 5) == 0 ||
172           (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) {
173          parse_ua_args(ua);
174          rc.mod = true;
175          if (!scan_run_command_line_arguments(ua, rc)) {
176             break; /* error get out of while loop */
177          }
178          continue;   /* another round with while loop */
179       }
180
181       /* Allow the user to modify the settings */
182       status = modify_job_parameters(ua, jcr, rc);
183       if (status == 0) {
184          continue;   /* another round with while loop */
185       }
186       if (status == -1) { /* error */
187          break; /* error get out of while loop */
188       }
189
190       if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
191          return start_job(ua, jcr, rc);
192       }
193       if (strncasecmp(ua->cmd, _("no"), strlen(ua->cmd)) == 0) {
194          break; /* get out of while loop */
195       }
196       ua->send_msg(_("\nBad response: %s. You must answer yes, mod, or no.\n\n"), ua->cmd);
197    }
198
199 bail_out:
200    ua->send_msg(_("Job not run.\n"));
201    if (ua->jcr->component_fd) {
202       fclose(ua->jcr->component_fd);
203       ua->jcr->component_fd = NULL;
204    }
205    if (ua->jcr->component_fname) {
206       unlink(ua->jcr->component_fname);
207       free_and_null_pool_memory(ua->jcr->component_fname);
208    }
209    if (jcr) {
210       if (jcr->component_fd) {
211          fclose(jcr->component_fd);
212          jcr->component_fd = NULL;
213       }
214       if (jcr->component_fname) {
215          unlink(jcr->component_fname);
216          free_and_null_pool_memory(jcr->component_fname);
217       }
218       free_jcr(jcr);
219    }
220    return 0;                       /* do not run */
221 }
222
223 static JobId_t start_job(UAContext *ua, JCR *jcr, run_ctx &rc)
224 {
225    JobId_t JobId;
226
227    if (rc.jr.JobStatus == JS_Incomplete) {
228       Dmsg1(100, "Ressuming JobId=%d\n", rc.jr.JobId);
229       JobId = resume_job(jcr, &rc.jr);
230    } else {
231       Dmsg1(100, "Starting JobId=%d\n", rc.jr.JobId);
232       JobId = run_job(jcr);
233    }
234    Dmsg4(100, "JobId=%u NewJobId=%d pool=%s priority=%d\n", (int)jcr->JobId,
235          JobId, jcr->pool->name(), jcr->JobPriority);
236    free_jcr(jcr);                  /* release jcr */
237    if (JobId == 0) {
238       ua->error_msg(_("Job failed.\n"));
239    } else {
240       char ed1[50];
241       ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
242    }
243    return JobId;
244 }
245
246 /*
247  * If no job_name defined in the run context, ask
248  *  the user for it.
249  * Then put the job resource in the run context and
250  *  check the access rights.
251  */
252 static bool get_job(UAContext *ua, run_ctx &rc)
253 {
254    if (rc.job_name) {
255       /* Find Job */
256       rc.job = GetJobResWithName(rc.job_name);
257       if (!rc.job) {
258          if (*rc.job_name != 0) {
259             ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name);
260          }
261          rc.job = select_job_resource(ua);
262       } else {
263          Dmsg1(100, "Found job=%s\n", rc.job_name);
264       }
265    } else if (!rc.job) {
266       ua->send_msg(_("A job name must be specified.\n"));
267       rc.job = select_job_resource(ua);
268    }
269    if (!rc.job) {
270       return false;
271    } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) {
272       ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name());
273       return false;
274    }
275    return true;
276 }
277
278 /*
279  * If no pool_name defined in the run context, ask
280  *  the user for it.
281  * Then put the pool resource in the run context and
282  *  check the access rights.
283  */
284 static bool get_pool(UAContext *ua, run_ctx &rc)
285 {
286    if (rc.pool_name) {
287       rc.pool = GetPoolResWithName(rc.pool_name);
288       if (!rc.pool) {
289          if (*rc.pool_name != 0) {
290             ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name);
291          }
292          rc.pool = select_pool_resource(ua);
293       }
294    } else if (!rc.pool) {
295       rc.pool = rc.job->pool;             /* use default */
296    }
297    if (!rc.pool) {
298       return false;
299    } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) {
300       ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name());
301       return false;
302    }
303    Dmsg1(100, "Using Pool=%s\n", rc.pool->name());
304    return true;
305 }
306
307 static bool get_next_pool(UAContext *ua, run_ctx &rc)
308 {
309    if (rc.next_pool_name) {
310       Dmsg1(100, "Have next pool=%s\n", rc.next_pool_name);
311       rc.next_pool = GetPoolResWithName(rc.next_pool_name);
312       if (!rc.next_pool) {
313          if (*rc.next_pool_name != 0) {
314             ua->warning_msg(_("NextPool \"%s\" not found.\n"), rc.next_pool_name);
315          }
316          rc.next_pool = select_pool_resource(ua);
317       }
318    }
319    /* NextPool can come from Job resource NextPool or Pool resource NextPool */
320    if (!rc.next_pool) {
321       if (rc.job->next_pool) {
322          rc.next_pool = rc.job->next_pool;
323       } else {
324          rc.next_pool = rc.pool->NextPool;      /* use default */
325       }
326    }
327    if (rc.next_pool && !acl_access_ok(ua, Pool_ACL, rc.next_pool->name())) {
328       ua->error_msg(_("No authorization. NextPool \"%s\".\n"), rc.next_pool->name());
329       return false;
330    }
331    if (rc.next_pool) {
332       Dmsg1(100, "Using NextPool=%s\n", NPRT(rc.next_pool->name()));
333    }
334    return true;
335 }
336
337
338 /*
339  * Fill in client data according to what is setup
340  *  in the run context, and make sure the user
341  *  has authorized access to it.
342  */
343 static bool get_client(UAContext *ua, run_ctx &rc)
344 {
345    if (rc.client_name) {
346       rc.client = GetClientResWithName(rc.client_name);
347       if (!rc.client) {
348          if (*rc.client_name != 0) {
349             ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name);
350          }
351          rc.client = select_client_resource(ua);
352       }
353    } else if (!rc.client) {
354       rc.client = rc.job->client;           /* use default */
355    }
356    if (!rc.client) {
357       return false;
358    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
359       ua->error_msg(_("No authorization. Client \"%s\".\n"),
360                rc.client->name());
361       return false;
362    }
363    Dmsg1(800, "Using client=%s\n", rc.client->name());
364
365    if (rc.restore_client_name) {
366       rc.client = GetClientResWithName(rc.restore_client_name);
367       if (!rc.client) {
368          if (*rc.restore_client_name != 0) {
369             ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name);
370          }
371          rc.client = select_client_resource(ua);
372       }
373    } else if (!rc.client) {
374       rc.client = rc.job->client;           /* use default */
375    }
376    if (!rc.client) {
377       return false;
378    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
379       ua->error_msg(_("No authorization. Client \"%s\".\n"),
380                rc.client->name());
381       return false;
382    }
383    Dmsg1(800, "Using restore client=%s\n", rc.client->name());
384    return true;
385 }
386
387
388 /*
389  * Fill in fileset data according to what is setup
390  *  in the run context, and make sure the user
391  *  has authorized access to it.
392  */
393 static bool get_fileset(UAContext *ua, run_ctx &rc)
394 {
395    if (rc.fileset_name) {
396       rc.fileset = GetFileSetResWithName(rc.fileset_name);
397       if (!rc.fileset) {
398          ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
399          rc.fileset = select_fileset_resource(ua);
400       }
401    } else if (!rc.fileset) {
402       rc.fileset = rc.job->fileset;           /* use default */
403    }
404    if (!rc.fileset) {
405       return false;
406    } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) {
407       ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
408                rc.fileset->name());
409       return false;
410    }
411    return true;
412 }
413
414 /*
415  * Fill in storage data according to what is setup
416  *  in the run context, and make sure the user
417  *  has authorized access to it.
418  */
419 static bool get_storage(UAContext *ua, run_ctx &rc)
420 {
421    if (rc.store_name) {
422       rc.store->store = GetStoreResWithName(rc.store_name);
423       pm_strcpy(rc.store->store_source, _("command line"));
424       if (!rc.store->store) {
425          if (*rc.store_name != 0) {
426             ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
427          }
428          rc.store->store = select_storage_resource(ua);
429          pm_strcpy(rc.store->store_source, _("user selection"));
430       }
431    } else if (!rc.store->store) {
432       get_job_storage(rc.store, rc.job, NULL);      /* use default */
433    }
434    if (!rc.store->store) {
435       ua->error_msg(_("No storage specified.\n"));
436       return false;
437    } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
438       ua->error_msg(_("No authorization. Storage \"%s\".\n"),
439                rc.store->store->name());
440       return false;
441    }
442    Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
443    return true;
444 }
445
446 /*
447  * Get and pass back a list of Jobids in rc.jid
448  */
449 static bool get_jobid_list(UAContext *ua, sellist &sl, run_ctx &rc)
450 {
451    int i, JobId;
452    JOB_DBR jr;
453    char *pJobId;
454    bool found = false;
455
456    memset(&jr, 0, sizeof(jr));
457    rc.jid = NULL;
458    /* See if any JobId is specified */
459    if ((i=find_arg(ua, "jobid")) >= 0) {
460       rc.jid = ua->argv[i];
461       if (!rc.jid) {
462          ua->send_msg(_("No JobId specified.\n"));
463          return false;
464       }
465       if (!sl.set_string(ua->argv[i], true)) {
466          ua->send_msg("%s", sl.get_errmsg());
467          return false;
468       }
469       return true;
470    }
471
472    /* No JobId list give, so see if he specified a Job */
473    if ((i=find_arg(ua, "job")) >= 0) {
474       rc.job_name = ua->argv[i];
475       if (!get_job(ua, rc)) {
476          ua->send_msg(_("Invalid or no Job name specified.\n"));
477          return false;
478       }
479    }
480    jr.limit = 100;  /* max 100 records */
481    if (rc.job_name) {
482       bstrncpy(jr.Name, rc.job_name, sizeof(jr.Name));
483    } else {
484       jr.Name[0] = 0;
485    }
486    jr.JobStatus = rc.jr.JobStatus;
487    Dmsg2(100, "JobStatus=%d JobName=%s\n", jr.JobStatus, jr.Name);
488    /* rc.JobIds is alist of all records found and printed */
489    rc.JobIds = db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, INCOMPLETE_JOBS);
490    if (!rc.JobIds || rc.JobIds->size()==0 ||
491        !get_selection_list(ua, sl, _("Enter the JobId list to select: "), false)) {
492       return false;
493    }
494    Dmsg1(100, "list=%s\n", sl.get_list());
495    /*
496     * Make sure each item entered is in the JobIds list
497     */
498    while ( (JobId = sl.next()) > 0) {
499       foreach_alist(pJobId, rc.JobIds) {
500          if (JobId == str_to_int64(pJobId)) {
501             pJobId[0] = 0;
502             found = true;
503             break;
504          }
505       }
506       if (!found) {
507          ua->error_msg(_("JobId=%d entered is not in the list.\n"), JobId);
508          return false;
509       }
510    }
511    sl.begin();         /* reset to walk list again */
512    rc.done = false;
513    return true;
514 }
515
516 static bool get_jobid_from_list(UAContext *ua, sellist &sl, run_ctx &rc)
517 {
518    int JobId;
519
520    if (rc.done) {
521       return false;
522    }
523    if ((JobId = sl.next()) < 0) {
524       Dmsg1(100, "sl.next()=%d\n", JobId);
525       rc.done = true;
526       return false;
527    }
528    rc.jr.JobId = rc.JobId = JobId;
529    Dmsg1(100, "Next JobId=%d\n", rc.JobId);
530    if (!db_get_job_record(ua->jcr, ua->db, &rc.jr)) {
531       ua->error_msg(_("Could not get job record for selected JobId=%d. ERR=%s"),
532                     rc.JobId, db_strerror(ua->db));
533       return false;
534    }
535    Dmsg3(100, "Job=%s JobId=%d JobStatus=%c\n", rc.jr.Name, rc.jr.JobId,
536          rc.jr.JobStatus);
537    rc.job_name = rc.jr.Name;
538    if (!get_job(ua, rc)) {
539       return false;
540    }
541    if (!get_pool(ua, rc)) {
542       return false;
543    }
544    get_job_storage(rc.store, rc.job, NULL);
545    rc.client_name = rc.job->client->hdr.name;
546    if (!get_client(ua, rc)) {
547       return false;
548    }
549    if (!get_fileset(ua, rc)) {
550       return false;
551    }
552    if (!get_storage(ua, rc)) {
553       return false;
554    }
555    return true;
556 }
557
558 /*
559  * Restart Canceled, Failed, or Incomplete Jobs
560  *
561  *  Returns: 0 on error
562  *           JobId if OK
563  *
564  */
565 int restart_cmd(UAContext *ua, const char *cmd)
566 {
567    JCR *jcr = NULL;
568    run_ctx rc;
569    sellist sl;
570    int i, j;
571    bool got_kw = false;
572    struct s_js {
573       const char *status_name;
574       int32_t job_status;
575    };
576    struct s_js kw[] = {
577       {"Incomplete", JS_Incomplete},
578       {"Canceled",   JS_Canceled},
579       {"Failed",     JS_FatalError},
580       {"All",        0},
581       {NULL,         0}
582    };
583
584    if (!open_client_db(ua)) {
585       return 0;
586    }
587
588    rc.jr.JobStatus = 0;
589    for (i=1; i<ua->argc; i++) {
590       for (j=0; kw[j].status_name; j++) {
591          if (strcasecmp(ua->argk[i], kw[j].status_name) == 0) {
592             rc.jr.JobStatus = kw[j].job_status;
593             got_kw = true;
594             break;
595          }
596       }
597    }
598    if (!got_kw) {  /* Must prompt user */
599       start_prompt(ua, _("You have the following choices:\n"));
600       for (i=0; kw[i].status_name; i++) {
601          add_prompt(ua, kw[i].status_name);
602       }
603       i = do_prompt(ua, NULL, _("Select termination code: "), NULL, 0);
604       if (i < 0) {
605          return 0;
606       }
607       rc.jr.JobStatus = kw[i].job_status;
608    }
609
610    /* type now has what job termination code we want to look at */
611    Dmsg1(100, "Termination code=%c\n", rc.jr.JobStatus);
612
613    /* Get a list of JobIds to restore */
614    if (!get_jobid_list(ua, sl, rc)) {
615       if (rc.JobIds) {
616          rc.JobIds->destroy();
617       }
618       return false;
619    }
620    Dmsg1(100, "list=%s\n", sl.get_list());
621
622    while (get_jobid_from_list(ua, sl, rc)) {
623       /*
624        * Create JCR to run job.  NOTE!!! after this point, free_jcr()
625        *  before returning.
626        */
627       if (!jcr) {
628          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
629          set_jcr_defaults(jcr, rc.job);
630          jcr->unlink_bsr = ua->jcr->unlink_bsr;    /* copy unlink flag from caller */
631          ua->jcr->unlink_bsr = false;
632       }
633
634       if (!set_run_context_in_jcr(ua, jcr, rc)) {
635          break;
636       }
637       start_job(ua, jcr, rc);
638       jcr = NULL;
639    }
640
641    if (jcr) {
642       free_jcr(jcr);
643    }
644    if (rc.JobIds) {
645       rc.JobIds->destroy();
646    }
647    return 0;                       /* do not run */
648 }
649
650 int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc)
651 {
652    int i, opt;
653
654    /*
655     * At user request modify parameters of job to be run.
656     */
657    if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0){
658       FILE *fd;
659
660       start_prompt(ua, _("Parameters to modify:\n"));
661       add_prompt(ua, _("Level"));            /* 0 */
662       add_prompt(ua, _("Storage"));          /* 1 */
663       add_prompt(ua, _("Job"));              /* 2 */
664       add_prompt(ua, _("FileSet"));          /* 3 */
665       if (jcr->getJobType() == JT_RESTORE) {
666          add_prompt(ua, _("Restore Client"));   /* 4 */
667       } else {
668          add_prompt(ua, _("Client"));        /* 4 */
669       }
670       add_prompt(ua, _("When"));             /* 5 */
671       add_prompt(ua, _("Priority"));         /* 6 */
672       if (jcr->getJobType() == JT_BACKUP ||
673           jcr->getJobType() == JT_COPY ||
674           jcr->getJobType() == JT_MIGRATE ||
675           jcr->getJobType() == JT_VERIFY) {
676          add_prompt(ua, _("Pool"));          /* 7 */
677          if ((jcr->getJobType() == JT_BACKUP &&   /* Virtual full */
678               jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
679              jcr->getJobType() == JT_COPY ||
680              jcr->getJobType() == JT_MIGRATE) {
681             add_prompt(ua, _("NextPool"));          /* 8 */
682          } else if (jcr->getJobType() == JT_VERIFY) {
683             add_prompt(ua, _("Verify Job"));  /* 8 */
684          }
685       } else if (jcr->getJobType() == JT_RESTORE) {
686          add_prompt(ua, _("Bootstrap"));     /* 7 */
687          add_prompt(ua, _("Where"));         /* 8 */
688          add_prompt(ua, _("File Relocation"));/* 9 */
689          add_prompt(ua, _("Replace"));       /* 10 */
690          add_prompt(ua, _("JobId"));         /* 11 */
691       }
692       if (jcr->getJobType() == JT_BACKUP || jcr->getJobType() == JT_RESTORE) {
693          add_prompt(ua, _("Plugin Options")); /* 12 */
694       }
695       switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
696       case 0:
697          /* Level */
698          select_job_level(ua, jcr);
699          goto try_again;
700       case 1:
701          /* Storage */
702          rc.store->store = select_storage_resource(ua);
703          if (rc.store->store) {
704             pm_strcpy(rc.store->store_source, _("user selection"));
705             set_rwstorage(jcr, rc.store);
706             goto try_again;
707          }
708          break;
709       case 2:
710          /* Job */
711          rc.job = select_job_resource(ua);
712          if (rc.job) {
713             jcr->job = rc.job;
714             set_jcr_defaults(jcr, rc.job);
715             goto try_again;
716          }
717          break;
718       case 3:
719          /* FileSet */
720          rc.fileset = select_fileset_resource(ua);
721          if (rc.fileset) {
722             jcr->fileset = rc.fileset;
723             goto try_again;
724          }
725          break;
726       case 4:
727          /* Client */
728          rc.client = select_client_resource(ua);
729          if (rc.client) {
730             jcr->client = rc.client;
731             goto try_again;
732          }
733          break;
734       case 5:
735          /* When */
736          if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
737             break;
738          }
739          if (ua->cmd[0] == 0) {
740             jcr->sched_time = time(NULL);
741          } else {
742             jcr->sched_time = str_to_utime(ua->cmd);
743             if (jcr->sched_time == 0) {
744                ua->send_msg(_("Invalid time, using current time.\n"));
745                jcr->sched_time = time(NULL);
746             }
747          }
748          goto try_again;
749       case 6:
750          /* Priority */
751          if (!get_pint(ua, _("Enter new Priority: "))) {
752             break;
753          }
754          if (ua->pint32_val == 0) {
755             ua->send_msg(_("Priority must be a positive integer.\n"));
756          } else {
757             jcr->JobPriority = ua->pint32_val;
758          }
759          goto try_again;
760       case 7:
761          /* Pool or Bootstrap depending on JobType */
762          if (jcr->getJobType() == JT_BACKUP ||
763              jcr->getJobType() == JT_COPY ||
764              jcr->getJobType() == JT_MIGRATE ||
765              jcr->getJobType() == JT_VERIFY) {      /* Pool */
766             rc.pool = select_pool_resource(ua);
767             if (rc.pool) {
768                jcr->pool = rc.pool;
769                Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
770                goto try_again;
771             }
772             break;
773          }
774
775          /* Bootstrap */
776          if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
777             break;
778          }
779          if (jcr->RestoreBootstrap) {
780             free(jcr->RestoreBootstrap);
781             jcr->RestoreBootstrap = NULL;
782          }
783          if (ua->cmd[0] != 0) {
784             jcr->RestoreBootstrap = bstrdup(ua->cmd);
785             fd = fopen(jcr->RestoreBootstrap, "rb");
786             if (!fd) {
787                berrno be;
788                ua->send_msg(_("Warning cannot open %s: ERR=%s\n"),
789                   jcr->RestoreBootstrap, be.bstrerror());
790                free(jcr->RestoreBootstrap);
791                jcr->RestoreBootstrap = NULL;
792             } else {
793                fclose(fd);
794             }
795          }
796          goto try_again;
797       case 8:
798          /* Specify Next Pool */
799          if ((jcr->getJobType() == JT_BACKUP &&   /* Virtual full */
800               jcr->is_JobLevel(L_VIRTUAL_FULL)) ||
801              jcr->getJobType() == JT_COPY ||
802              jcr->getJobType() == JT_MIGRATE) {
803             rc.next_pool = select_pool_resource(ua);
804             if (rc.next_pool) {
805                jcr->next_pool = rc.next_pool;
806                goto try_again;
807             }
808          }
809          /* Verify Job */
810          if (jcr->getJobType() == JT_VERIFY) {
811             rc.verify_job = select_job_resource(ua);
812             if (rc.verify_job) {
813               jcr->verify_job = rc.verify_job;
814             }
815             goto try_again;
816          }
817          /* Where */
818          if (!get_cmd(ua, _("Please enter the full path prefix for restore (/ for none): "))) {
819             break;
820          }
821          if (jcr->RegexWhere) { /* cannot use regexwhere and where */
822             free(jcr->RegexWhere);
823             jcr->RegexWhere = NULL;
824          }
825          if (jcr->where) {
826             free(jcr->where);
827             jcr->where = NULL;
828          }
829          if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
830             ua->cmd[0] = 0;
831          }
832          jcr->where = bstrdup(ua->cmd);
833          goto try_again;
834       case 9:
835          /* File relocation */
836          select_where_regexp(ua, jcr);
837          goto try_again;
838       case 10:
839          /* Replace */
840          start_prompt(ua, _("Replace:\n"));
841          for (i=0; ReplaceOptions[i].name; i++) {
842             add_prompt(ua, ReplaceOptions[i].name);
843          }
844          opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
845          if (opt >=  0) {
846             rc.replace = ReplaceOptions[opt].name;
847             jcr->replace = ReplaceOptions[opt].token;
848          }
849          goto try_again;
850       case 11:
851          /* JobId */
852          rc.jid = NULL;                  /* force reprompt */
853          jcr->RestoreJobId = 0;
854          if (jcr->RestoreBootstrap) {
855             ua->send_msg(_("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
856          }
857          goto try_again;
858       case 12:
859          /* Plugin Options */
860          //generate_plugin_event(jcr, bEventJobConfig, &rc);
861          if (!get_cmd(ua, _("Please Plugin Options string: "))) {
862             break;
863          }
864          if (jcr->plugin_options) {
865             free(jcr->plugin_options);
866             jcr->plugin_options = NULL;
867          }
868          jcr->plugin_options = bstrdup(ua->cmd);
869          goto try_again;
870       case -1:                        /* error or cancel */
871          goto bail_out;
872       default:
873          goto try_again;
874       }
875       goto bail_out;
876    }
877    return 1;
878
879 bail_out:
880    return -1;
881 try_again:
882    return 0;
883 }
884
885 /*
886  * Put the run context that we have at this point into the JCR.
887  * That allows us to re-ask for the run context.
888  * This subroutine can be called multiple times, so it
889  *  must keep any prior settings.
890  */
891 static bool set_run_context_in_jcr(UAContext *ua, JCR *jcr, run_ctx &rc)
892 {
893    int i;
894
895    jcr->verify_job = rc.verify_job;
896    jcr->previous_job = rc.previous_job;
897    jcr->pool = rc.pool;
898    jcr->next_pool = rc.next_pool;
899    if (rc.next_pool) {
900       jcr->cmdline_next_pool_override = true;
901    }
902    if (rc.pool_name) {
903       pm_strcpy(jcr->pool_source, _("Command input"));
904    } else if (jcr->pool != jcr->job->pool) {
905       pm_strcpy(jcr->pool_source, _("User input"));
906    }
907    if (rc.next_pool_name) {
908       pm_strcpy(jcr->next_pool_source, _("Command input"));
909    } else if (jcr->next_pool == jcr->job->next_pool) {
910       pm_strcpy(jcr->next_pool_source, _("Job resource"));
911    } else if (jcr->next_pool != jcr->pool->NextPool) {
912       pm_strcpy(jcr->next_pool_source, _("User input"));
913    }
914
915    set_rwstorage(jcr, rc.store);
916    jcr->client = rc.client;
917    if (jcr->client) {
918       pm_strcpy(jcr->client_name, rc.client->name());
919    } else {
920       pm_strcpy(jcr->client_name, "**Dummy**");
921    }
922    if (rc.media_type) {
923       if (!jcr->media_type) {
924          jcr->media_type = get_pool_memory(PM_NAME);
925       }
926       pm_strcpy(jcr->media_type, rc.media_type);
927    }
928    jcr->fileset = rc.fileset;
929    jcr->ExpectedFiles = rc.files;
930    if (rc.catalog) {
931       jcr->catalog = rc.catalog;
932       pm_strcpy(jcr->catalog_source, _("User input"));
933    }
934
935    pm_strcpy(jcr->comment, rc.comment);
936
937    if (rc.where) {
938       if (jcr->where) {
939          free(jcr->where);
940       }
941       jcr->where = bstrdup(rc.where);
942       rc.where = NULL;
943    }
944
945    if (rc.regexwhere) {
946       if (jcr->RegexWhere) {
947          free(jcr->RegexWhere);
948       }
949       jcr->RegexWhere = bstrdup(rc.regexwhere);
950       rc.regexwhere = NULL;
951    }
952
953    if (rc.when) {
954       jcr->sched_time = str_to_utime(rc.when);
955       if (jcr->sched_time == 0) {
956          ua->send_msg(_("Invalid time, using current time.\n"));
957          jcr->sched_time = time(NULL);
958       }
959       rc.when = NULL;
960    }
961
962    if (rc.bootstrap) {
963       if (jcr->RestoreBootstrap) {
964          free(jcr->RestoreBootstrap);
965       }
966       jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
967       rc.bootstrap = NULL;
968    }
969
970    if (rc.plugin_options) {
971       if (jcr->plugin_options) {
972          free(jcr->plugin_options);
973       }
974       jcr->plugin_options = bstrdup(rc.plugin_options);
975       rc.plugin_options = NULL;
976    }
977
978    if (rc.replace) {
979       jcr->replace = 0;
980       for (i=0; ReplaceOptions[i].name; i++) {
981          if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
982             jcr->replace = ReplaceOptions[i].token;
983          }
984       }
985       if (!jcr->replace) {
986          ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
987          return false;
988       }
989    } else if (rc.job->replace) {
990       jcr->replace = rc.job->replace;
991    } else {
992       jcr->replace = REPLACE_ALWAYS;
993    }
994    rc.replace = NULL;
995
996    /* Set Snapshot Retention (Job <- Client) */
997    if (jcr->client) {
998       jcr->snapshot_retention = jcr->client->SnapRetention;
999    }
1000    if (jcr->job && jcr->job->SnapRetention > 0) {
1001       jcr->snapshot_retention = jcr->job->SnapRetention;
1002    }
1003
1004    if (rc.Priority) {
1005       jcr->JobPriority = rc.Priority;
1006       rc.Priority = 0;
1007    }
1008
1009    if (rc.since) {
1010       if (!jcr->stime) {
1011          jcr->stime = get_pool_memory(PM_MESSAGE);
1012       }
1013       pm_strcpy(jcr->stime, rc.since);
1014       rc.since = NULL;
1015    }
1016
1017    if (rc.cloned) {
1018       jcr->cloned = rc.cloned;
1019       rc.cloned = false;
1020    }
1021
1022    /* If pool changed, update migration write storage */
1023    if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
1024       (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) {
1025       if (!set_mac_wstorage(ua, jcr, rc.pool, rc.next_pool,
1026             jcr->next_pool_source)) {
1027          return false;
1028       }
1029    }
1030    rc.replace = ReplaceOptions[0].name;
1031    for (i=0; ReplaceOptions[i].name; i++) {
1032       if ((int)ReplaceOptions[i].token == jcr->replace) {
1033          rc.replace = ReplaceOptions[i].name;
1034       }
1035    }
1036    if (rc.level_name) {
1037       if (!get_level_from_name(jcr, rc.level_name)) {
1038          ua->send_msg(_("Level \"%s\" not valid.\n"), rc.level_name);
1039          return false;
1040       }
1041       rc.level_name = NULL;
1042    }
1043    if (rc.jid) {
1044       /* Note, this is also MigrateJobId and a VerifyJobId */
1045       jcr->RestoreJobId = str_to_int64(rc.jid);
1046
1047       /* Copy also this parameter for VirtualFull in jcr->JobIds */
1048       if (!jcr->JobIds) {
1049          jcr->JobIds = get_pool_memory(PM_FNAME);
1050       }
1051       pm_strcpy(jcr->JobIds, rc.jid);
1052       jcr->use_all_JobIds = rc.alljobid; /* if we found the "alljobid=" kw */
1053       rc.alljobid = false;
1054       rc.jid = 0;
1055    }
1056
1057    /* Some options are not available through the menu
1058     * TODO: Add an advanced menu?
1059     */
1060    if (rc.spool_data_set) {
1061       jcr->spool_data = rc.spool_data;
1062    }
1063
1064    if (rc.accurate_set) {
1065       jcr->accurate = rc.accurate;
1066    }
1067
1068    /* Used by migration jobs that can have the same name,
1069     * but can run at the same time
1070     */
1071    if (rc.ignoreduplicatecheck_set) {
1072       jcr->IgnoreDuplicateJobChecking = rc.ignoreduplicatecheck;
1073    }
1074
1075    return true;
1076 }
1077
1078 static void select_where_regexp(UAContext *ua, JCR *jcr)
1079 {
1080    alist *regs;
1081    char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
1082    strip_prefix = add_suffix = rwhere = add_prefix = NULL;
1083
1084 try_again_reg:
1085    ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
1086                 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
1087
1088    start_prompt(ua, _("This will replace your current Where value\n"));
1089    add_prompt(ua, _("Strip prefix"));                /* 0 */
1090    add_prompt(ua, _("Add prefix"));                  /* 1 */
1091    add_prompt(ua, _("Add file suffix"));             /* 2 */
1092    add_prompt(ua, _("Enter a regexp"));              /* 3 */
1093    add_prompt(ua, _("Test filename manipulation"));  /* 4 */
1094    add_prompt(ua, _("Use this ?"));                  /* 5 */
1095
1096    switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1097    case 0:
1098       /* Strip prefix */
1099       if (get_cmd(ua, _("Please enter the path prefix to strip: "))) {
1100          if (strip_prefix) bfree(strip_prefix);
1101          strip_prefix = bstrdup(ua->cmd);
1102       }
1103
1104       goto try_again_reg;
1105    case 1:
1106       /* Add prefix */
1107       if (get_cmd(ua, _("Please enter the path prefix to add (/ for none): "))) {
1108          if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1109             ua->cmd[0] = 0;
1110          }
1111
1112          if (add_prefix) bfree(add_prefix);
1113          add_prefix = bstrdup(ua->cmd);
1114       }
1115       goto try_again_reg;
1116    case 2:
1117       /* Add suffix */
1118       if (get_cmd(ua, _("Please enter the file suffix to add: "))) {
1119          if (add_suffix) bfree(add_suffix);
1120          add_suffix = bstrdup(ua->cmd);
1121       }
1122       goto try_again_reg;
1123    case 3:
1124       /* Add rwhere */
1125       if (get_cmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
1126          if (rwhere) bfree(rwhere);
1127          rwhere = bstrdup(ua->cmd);
1128       }
1129
1130       goto try_again_reg;
1131    case 4:
1132       /* Test regexp */
1133       char *result;
1134       char *regexp;
1135
1136       if (rwhere && rwhere[0] != '\0') {
1137          regs = get_bregexps(rwhere);
1138          ua->send_msg(_("regexwhere=%s\n"), NPRT(rwhere));
1139       } else {
1140          int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1141          regexp = (char *) bmalloc (len * sizeof(char));
1142          bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
1143          regs = get_bregexps(regexp);
1144          ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
1145                       NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp));
1146
1147          bfree(regexp);
1148       }
1149
1150       if (!regs) {
1151          ua->send_msg(_("Cannot use your regexp\n"));
1152          goto try_again_reg;
1153       }
1154       ua->send_msg(_("Enter a period (.) to stop this test\n"));
1155       while (get_cmd(ua, _("Please enter filename to test: "))) {
1156          apply_bregexps(ua->cmd, regs, &result);
1157          ua->send_msg(_("%s -> %s\n"), ua->cmd, result);
1158       }
1159       free_bregexps(regs);
1160       delete regs;
1161       goto try_again_reg;
1162
1163    case 5:
1164       /* OK */
1165       break;
1166    case -1:                        /* error or cancel */
1167       goto bail_out_reg;
1168    default:
1169       goto try_again_reg;
1170    }
1171
1172    /* replace the existing where */
1173    if (jcr->where) {
1174       bfree(jcr->where);
1175       jcr->where = NULL;
1176    }
1177
1178    /* replace the existing regexwhere */
1179    if (jcr->RegexWhere) {
1180       bfree(jcr->RegexWhere);
1181       jcr->RegexWhere = NULL;
1182    }
1183
1184    if (rwhere) {
1185       jcr->RegexWhere = bstrdup(rwhere);
1186    } else if (strip_prefix || add_prefix || add_suffix) {
1187       int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
1188       jcr->RegexWhere = (char *) bmalloc(len*sizeof(char));
1189       bregexp_build_where(jcr->RegexWhere, len, strip_prefix, add_prefix, add_suffix);
1190    }
1191
1192    regs = get_bregexps(jcr->RegexWhere);
1193    if (regs) {
1194       free_bregexps(regs);
1195       delete regs;
1196    } else {
1197       if (jcr->RegexWhere) {
1198          bfree(jcr->RegexWhere);
1199          jcr->RegexWhere = NULL;
1200       }
1201       ua->send_msg(_("Cannot use your regexp.\n"));
1202    }
1203
1204 bail_out_reg:
1205    if (strip_prefix) bfree(strip_prefix);
1206    if (add_prefix)   bfree(add_prefix);
1207    if (add_suffix)   bfree(add_suffix);
1208    if (rwhere)       bfree(rwhere);
1209 }
1210
1211 static void select_job_level(UAContext *ua, JCR *jcr)
1212 {
1213    if (jcr->getJobType() == JT_BACKUP) {
1214       start_prompt(ua, _("Levels:\n"));
1215 //    add_prompt(ua, _("Base"));
1216       add_prompt(ua, _("Full"));
1217       add_prompt(ua, _("Incremental"));
1218       add_prompt(ua, _("Differential"));
1219       add_prompt(ua, _("Since"));
1220       add_prompt(ua, _("VirtualFull"));
1221       switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
1222 //    case 0:
1223 //       jcr->JobLevel = L_BASE;
1224 //       break;
1225       case 0:
1226          jcr->setJobLevel(L_FULL);
1227          break;
1228       case 1:
1229          jcr->setJobLevel(L_INCREMENTAL);
1230          break;
1231       case 2:
1232          jcr->setJobLevel(L_DIFFERENTIAL);
1233          break;
1234       case 3:
1235          jcr->setJobLevel(L_SINCE);
1236          break;
1237       case 4:
1238          jcr->setJobLevel(L_VIRTUAL_FULL);
1239          break;
1240       default:
1241          break;
1242       }
1243    } else if (jcr->getJobType() == JT_VERIFY) {
1244       start_prompt(ua, _("Levels:\n"));
1245       add_prompt(ua, _("Initialize Catalog"));
1246       add_prompt(ua, _("Verify Catalog"));
1247       add_prompt(ua, _("Verify Volume to Catalog"));
1248       add_prompt(ua, _("Verify Disk to Catalog"));
1249       add_prompt(ua, _("Verify Volume Data"));
1250       switch (do_prompt(ua, "",  _("Select level"), NULL, 0)) {
1251       case 0:
1252          jcr->setJobLevel(L_VERIFY_INIT);
1253          break;
1254       case 1:
1255          jcr->setJobLevel(L_VERIFY_CATALOG);
1256          break;
1257       case 2:
1258          jcr->setJobLevel(L_VERIFY_VOLUME_TO_CATALOG);
1259          break;
1260       case 3:
1261          jcr->setJobLevel(L_VERIFY_DISK_TO_CATALOG);
1262          break;
1263       case 4:
1264          jcr->setJobLevel(L_VERIFY_DATA);
1265          break;
1266       default:
1267          break;
1268       }
1269    } else {
1270       ua->warning_msg(_("Level not appropriate for this Job. Cannot be changed.\n"));
1271    }
1272    return;
1273 }
1274
1275 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char *verify_list,
1276    char *jid, const char *replace, char *client_name)
1277 {
1278    char ec1[30];
1279    char dt[MAX_TIME_LENGTH];
1280
1281    Dmsg1(800, "JobType=%c\n", jcr->getJobType());
1282    switch (jcr->getJobType()) {
1283    case JT_ADMIN:
1284       if (ua->api) {
1285          ua->signal(BNET_RUN_CMD);
1286          ua->send_msg("Type: Admin\n"
1287                      "Title: Run Admin Job\n"
1288                      "JobName:  %s\n"
1289                      "FileSet:  %s\n"
1290                      "Client:   %s\n"
1291                      "Storage:  %s\n"
1292                      "When:     %s\n"
1293                      "Priority: %d\n",
1294                  job->name(),
1295                  jcr->fileset->name(),
1296                  NPRT(jcr->client->name()),
1297                  jcr->wstore?jcr->wstore->name():"*None*",
1298                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1299                  jcr->JobPriority);
1300       } else {
1301          ua->send_msg(_("Run Admin Job\n"
1302                      "JobName:  %s\n"
1303                      "FileSet:  %s\n"
1304                      "Client:   %s\n"
1305                      "Storage:  %s\n"
1306                      "When:     %s\n"
1307                      "Priority: %d\n"),
1308                  job->name(),
1309                  jcr->fileset->name(),
1310                  NPRT(jcr->client->name()),
1311                  jcr->wstore?jcr->wstore->name():"*None*",
1312                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1313                  jcr->JobPriority);
1314       }
1315       jcr->setJobLevel(L_FULL);
1316       break;
1317    case JT_BACKUP:
1318    case JT_VERIFY:
1319       char next_pool[MAX_NAME_LENGTH + 50];
1320       next_pool[0] = 0;
1321       if (jcr->getJobType() == JT_BACKUP) {
1322          if (ua->api) {
1323             ua->signal(BNET_RUN_CMD);
1324             if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1325                bsnprintf(next_pool, sizeof(next_pool), "NextPool: %s\n",
1326                   jcr->next_pool ? jcr->next_pool->name() : "*None*");
1327             }
1328             ua->send_msg("Type: Backup\n"
1329                         "Title: Run Backup Job\n"
1330                         "JobName:  %s\n"
1331                         "Level:    %s\n"
1332                         "Client:   %s\n"
1333                         "FileSet:  %s\n"
1334                         "Pool:     %s\n"
1335                         "%s"
1336                         "Storage:  %s\n"
1337                         "When:     %s\n"
1338                         "Priority: %d\n"
1339                         "%s%s%s",
1340                  job->name(),
1341                  level_to_str(jcr->getJobLevel()),
1342                  jcr->client->name(),
1343                  jcr->fileset->name(),
1344                  NPRT(jcr->pool->name()),
1345                  next_pool,
1346                  jcr->wstore?jcr->wstore->name():"*None*",
1347                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1348                  jcr->JobPriority,
1349                  jcr->plugin_options?"Plugin Options: ":"",
1350                  jcr->plugin_options?jcr->plugin_options:"",
1351                  jcr->plugin_options?"\n":"");
1352          } else {
1353             if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
1354                bsnprintf(next_pool, sizeof(next_pool),
1355                   "NextPool: %s (From %s)\n",
1356                   jcr->next_pool ? jcr->next_pool->name() : "*None*",
1357                   jcr->next_pool_source);
1358             }
1359             ua->send_msg(_("Run Backup job\n"
1360                         "JobName:  %s\n"
1361                         "Level:    %s\n"
1362                         "Client:   %s\n"
1363                         "FileSet:  %s\n"
1364                         "Pool:     %s (From %s)\n"
1365                         "%s"
1366                         "Storage:  %s (From %s)\n"
1367                         "When:     %s\n"
1368                         "Priority: %d\n"
1369                         "%s%s%s"),
1370                  job->name(),
1371                  level_to_str(jcr->getJobLevel()),
1372                  jcr->client->name(),
1373                  jcr->fileset->name(),
1374                  NPRT(jcr->pool->name()), jcr->pool_source,
1375                  next_pool,
1376                  jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1377                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1378                  jcr->JobPriority,
1379                  jcr->plugin_options?"Plugin Options: ":"",
1380                  jcr->plugin_options?jcr->plugin_options:"",
1381                  jcr->plugin_options?"\n":"");
1382          }
1383       } else {  /* JT_VERIFY */
1384          JOB_DBR jr;
1385          const char *Name;
1386          if (jcr->verify_job) {
1387             Name = jcr->verify_job->name();
1388          } else if (jcr->RestoreJobId) { /* Display job name if jobid requested */
1389             memset(&jr, 0, sizeof(jr));
1390             jr.JobId = jcr->RestoreJobId;
1391             if (!db_get_job_record(jcr, ua->db, &jr)) {
1392                ua->error_msg(_("Could not get job record for selected JobId. ERR=%s"),
1393                     db_strerror(ua->db));
1394                return false;
1395             }
1396             Name = jr.Job;
1397          } else {
1398             Name = "";
1399          }
1400          if (!verify_list) {
1401             verify_list = job->WriteVerifyList;
1402          }
1403          if (!verify_list) {
1404             verify_list = "";
1405          }
1406          if (ua->api) {
1407             ua->signal(BNET_RUN_CMD);
1408             ua->send_msg("Type: Verify\n"
1409                         "Title: Run Verify Job\n"
1410                         "JobName:     %s\n"
1411                         "Level:       %s\n"
1412                         "Client:      %s\n"
1413                         "FileSet:     %s\n"
1414                         "Pool:        %s (From %s)\n"
1415                         "Storage:     %s (From %s)\n"
1416                         "Verify Job:  %s\n"
1417                         "Verify List: %s\n"
1418                         "When:        %s\n"
1419                         "Priority:    %d\n",
1420               job->name(),
1421               level_to_str(jcr->getJobLevel()),
1422               jcr->client->name(),
1423               jcr->fileset->name(),
1424               NPRT(jcr->pool->name()), jcr->pool_source,
1425               jcr->rstore->name(), jcr->rstore_source,
1426               Name,
1427               verify_list,
1428               bstrutime(dt, sizeof(dt), jcr->sched_time),
1429               jcr->JobPriority);
1430          } else {
1431             ua->send_msg(_("Run Verify Job\n"
1432                         "JobName:     %s\n"
1433                         "Level:       %s\n"
1434                         "Client:      %s\n"
1435                         "FileSet:     %s\n"
1436                         "Pool:        %s (From %s)\n"
1437                         "Storage:     %s (From %s)\n"
1438                         "Verify Job:  %s\n"
1439                         "Verify List: %s\n"
1440                         "When:        %s\n"
1441                         "Priority:    %d\n"),
1442               job->name(),
1443               level_to_str(jcr->getJobLevel()),
1444               jcr->client->name(),
1445               jcr->fileset->name(),
1446               NPRT(jcr->pool->name()), jcr->pool_source,
1447               jcr->rstore->name(), jcr->rstore_source,
1448               Name,
1449               verify_list,
1450               bstrutime(dt, sizeof(dt), jcr->sched_time),
1451               jcr->JobPriority);
1452          }
1453       }
1454       break;
1455    case JT_RESTORE:
1456       if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
1457          if (jid) {
1458             jcr->RestoreJobId = str_to_int64(jid);
1459          } else {
1460             if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
1461                return false;
1462             }
1463             jcr->RestoreJobId = ua->int64_val;
1464          }
1465       }
1466       jcr->setJobLevel(L_FULL);      /* default level */
1467       Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
1468       if (jcr->RestoreJobId == 0) {
1469          /* RegexWhere is take before RestoreWhere */
1470          if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
1471             if (ua->api) {
1472                ua->signal(BNET_RUN_CMD);
1473                ua->send_msg("Type: Restore\n"
1474                         "Title: Run Restore Job\n"
1475                         "JobName:         %s\n"
1476                         "Bootstrap:       %s\n"
1477                         "RegexWhere:      %s\n"
1478                         "Replace:         %s\n"
1479                         "FileSet:         %s\n"
1480                         "Backup Client:   %s\n"
1481                         "Restore Client:  %s\n"
1482                         "Storage:         %s\n"
1483                         "When:            %s\n"
1484                         "Catalog:         %s\n"
1485                         "Priority:        %d\n"
1486                         "Plugin Options:  %s\n",
1487                  job->name(),
1488                  NPRT(jcr->RestoreBootstrap),
1489                  jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
1490                  replace,
1491                  jcr->fileset->name(),
1492                  client_name,
1493                  jcr->client->name(),
1494                  jcr->rstore->name(),
1495                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1496                  jcr->catalog->name(),
1497                  jcr->JobPriority,
1498                  NPRTB(jcr->plugin_options));
1499
1500             } else {
1501                ua->send_msg(_("Run Restore job\n"
1502                         "JobName:         %s\n"
1503                         "Bootstrap:       %s\n"
1504                         "RegexWhere:      %s\n"
1505                         "Replace:         %s\n"
1506                         "FileSet:         %s\n"
1507                         "Backup Client:   %s\n"
1508                         "Restore Client:  %s\n"
1509                         "Storage:         %s\n"
1510                         "When:            %s\n"
1511                         "Catalog:         %s\n"
1512                         "Priority:        %d\n"
1513                         "Plugin Options:  %s\n"),
1514                  job->name(),
1515                  NPRT(jcr->RestoreBootstrap),
1516                  jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
1517                  replace,
1518                  jcr->fileset->name(),
1519                  client_name,
1520                  jcr->client->name(),
1521                  jcr->rstore->name(),
1522                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1523                  jcr->catalog->name(),
1524                  jcr->JobPriority,
1525                  NPRTB(jcr->plugin_options));
1526             }
1527          } else {
1528             if (ua->api) {
1529                ua->signal(BNET_RUN_CMD);
1530                ua->send_msg("Type: Restore\n"
1531                         "Title: Run Restore job\n"
1532                         "JobName:         %s\n"
1533                         "Bootstrap:       %s\n"
1534                         "Where:           %s\n"
1535                         "Replace:         %s\n"
1536                         "FileSet:         %s\n"
1537                         "Backup Client:   %s\n"
1538                         "Restore Client:  %s\n"
1539                         "Storage:         %s\n"
1540                         "When:            %s\n"
1541                         "Catalog:         %s\n"
1542                         "Priority:        %d\n"
1543                         "Plugin Options:  %s\n",
1544                  job->name(),
1545                  NPRT(jcr->RestoreBootstrap),
1546                  jcr->where?jcr->where:NPRT(job->RestoreWhere),
1547                  replace,
1548                  jcr->fileset->name(),
1549                  client_name,
1550                  jcr->client->name(),
1551                  jcr->rstore->name(),
1552                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1553                  jcr->catalog->name(),
1554                  jcr->JobPriority,
1555                  NPRTB(jcr->plugin_options));
1556
1557             } else {
1558                ua->send_msg(_("Run Restore job\n"
1559                         "JobName:         %s\n"
1560                         "Bootstrap:       %s\n"
1561                         "Where:           %s\n"
1562                         "Replace:         %s\n"
1563                         "FileSet:         %s\n"
1564                         "Backup Client:   %s\n"
1565                         "Restore Client:  %s\n"
1566                         "Storage:         %s\n"
1567                         "When:            %s\n"
1568                         "Catalog:         %s\n"
1569                         "Priority:        %d\n"
1570                         "Plugin Options:  %s\n"),
1571                  job->name(),
1572                  NPRT(jcr->RestoreBootstrap),
1573                  jcr->where?jcr->where:NPRT(job->RestoreWhere),
1574                  replace,
1575                  jcr->fileset->name(),
1576                  client_name,
1577                  jcr->client->name(),
1578                  jcr->rstore->name(),
1579                  bstrutime(dt, sizeof(dt), jcr->sched_time),
1580                  jcr->catalog->name(),
1581                  jcr->JobPriority,
1582                  NPRTB(jcr->plugin_options));
1583             }
1584          }
1585
1586       } else {
1587          /* ***FIXME*** This needs to be fixed for bat */
1588          if (ua->api) ua->signal(BNET_RUN_CMD);
1589          ua->send_msg(_("Run Restore job\n"
1590                         "JobName:    %s\n"
1591                         "Bootstrap:  %s\n"),
1592                       job->name(),
1593                       NPRT(jcr->RestoreBootstrap));
1594
1595          /* RegexWhere is take before RestoreWhere */
1596          if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
1597             ua->send_msg(_("RegexWhere: %s\n"),
1598                          jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere);
1599          } else {
1600             ua->send_msg(_("Where:      %s\n"),
1601                          jcr->where?jcr->where:NPRT(job->RestoreWhere));
1602          }
1603
1604          ua->send_msg(_("Replace:         %s\n"
1605                         "Client:          %s\n"
1606                         "Storage:         %s\n"
1607                         "JobId:           %s\n"
1608                         "When:            %s\n"
1609                         "Catalog:         %s\n"
1610                         "Priority:        %d\n"
1611                         "Plugin Options:  %s\n"),
1612               replace,
1613               jcr->client->name(),
1614               jcr->rstore->name(),
1615               jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
1616               bstrutime(dt, sizeof(dt), jcr->sched_time),
1617               jcr->catalog->name(),
1618               jcr->JobPriority,
1619               NPRTB(jcr->plugin_options));
1620        }
1621       break;
1622    case JT_COPY:
1623    case JT_MIGRATE:
1624       char *prt_type;
1625       jcr->setJobLevel(L_FULL);      /* default level */
1626       if (ua->api) {
1627          ua->signal(BNET_RUN_CMD);
1628          if (jcr->getJobType() == JT_COPY) {
1629             prt_type = (char *)"Type: Copy\nTitle: Run Copy Job\n";
1630          } else {
1631             prt_type = (char *)"Type: Migration\nTitle: Run Migration Job\n";
1632          }
1633          ua->send_msg("%s"
1634                      "JobName:       %s\n"
1635                      "Bootstrap:     %s\n"
1636                      "Client:        %s\n"
1637                      "FileSet:       %s\n"
1638                      "Pool:          %s\n"
1639                      "NextPool:      %s\n"
1640                      "Read Storage:  %s\n"
1641                      "Write Storage: %s\n"
1642                      "JobId:         %s\n"
1643                      "When:          %s\n"
1644                      "Catalog:       %s\n"
1645                      "Priority:      %d\n",
1646            prt_type,
1647            job->name(),
1648            NPRT(jcr->RestoreBootstrap),
1649            jcr->client->name(),
1650            jcr->fileset->name(),
1651            NPRT(jcr->pool->name()),
1652            jcr->next_pool?jcr->next_pool->name():"*None*",
1653            jcr->rstore->name(),
1654            jcr->wstore?jcr->wstore->name():"*None*",
1655            jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
1656            bstrutime(dt, sizeof(dt), jcr->sched_time),
1657            jcr->catalog->name(),
1658            jcr->JobPriority);
1659       } else {
1660          if (jcr->getJobType() == JT_COPY) {
1661             prt_type = _("Run Copy job\n");
1662          } else {
1663             prt_type = _("Run Migration job\n");
1664          }
1665          ua->send_msg("%s"
1666                      "JobName:       %s\n"
1667                      "Bootstrap:     %s\n"
1668                      "Client:        %s\n"
1669                      "FileSet:       %s\n"
1670                      "Pool:          %s (From %s)\n"
1671                      "NextPool:      %s (From %s)\n"
1672                      "Read Storage:  %s (From %s)\n"
1673                      "Write Storage: %s (From %s)\n"
1674                      "JobId:         %s\n"
1675                      "When:          %s\n"
1676                      "Catalog:       %s\n"
1677                      "Priority:      %d\n",
1678            prt_type,
1679            job->name(),
1680            NPRT(jcr->RestoreBootstrap),
1681            jcr->client->name(),
1682            jcr->fileset->name(),
1683            NPRT(jcr->pool->name()), jcr->pool_source,
1684            jcr->next_pool?jcr->next_pool->name():"*None*",
1685                NPRT(jcr->next_pool_source),
1686            jcr->rstore->name(), jcr->rstore_source,
1687            jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1688            jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
1689            bstrutime(dt, sizeof(dt), jcr->sched_time),
1690            jcr->catalog->name(),
1691            jcr->JobPriority);
1692       }
1693       break;
1694    default:
1695       ua->error_msg(_("Unknown Job Type=%d\n"), jcr->getJobType());
1696       return false;
1697    }
1698    return true;
1699 }
1700
1701
1702 static bool scan_run_command_line_arguments(UAContext *ua, run_ctx &rc)
1703 {
1704    bool kw_ok;
1705    int i, j;
1706    static const char *kw[] = {        /* command line arguments */
1707       "alljobid",                     /* 0 Used in a switch() */
1708       "jobid",                        /* 1 */
1709       "client",                       /* 2 */
1710       "fd",
1711       "fileset",                      /* 4 */
1712       "level",                        /* 5 */
1713       "storage",                      /* 6 */
1714       "sd",                           /* 7 */
1715       "regexwhere",                   /* 8 where string as a bregexp */
1716       "where",                        /* 9 */
1717       "bootstrap",                    /* 10 */
1718       "replace",                      /* 11 */
1719       "when",                         /* 12 */
1720       "priority",                     /* 13 */
1721       "yes",        /* 14  -- if you change this change YES_POS too */
1722       "verifyjob",                    /* 15 */
1723       "files",                        /* 16 number of files to restore */
1724       "catalog",                      /* 17 override catalog */
1725       "since",                        /* 18 since */
1726       "cloned",                       /* 19 cloned */
1727       "verifylist",                   /* 20 verify output list */
1728       "migrationjob",                 /* 21 migration job name */
1729       "pool",                         /* 22 */
1730       "backupclient",                 /* 23 */
1731       "restoreclient",                /* 24 */
1732       "pluginoptions",                /* 25 */
1733       "spooldata",                    /* 26 */
1734       "comment",                      /* 27 */
1735       "ignoreduplicatecheck",         /* 28 */
1736       "accurate",                     /* 29 */
1737       "job",                          /* 30 */
1738       "mediatype",                    /* 31 */
1739       "nextpool",                     /* 32 override next pool name */
1740       NULL};
1741
1742 #define YES_POS 14
1743
1744    rc.catalog_name = NULL;
1745    rc.job_name = NULL;
1746    rc.pool_name = NULL;
1747    rc.next_pool_name = NULL;
1748    rc.store_name = NULL;
1749    rc.client_name = NULL;
1750    rc.media_type = NULL;
1751    rc.restore_client_name = NULL;
1752    rc.fileset_name = NULL;
1753    rc.verify_job_name = NULL;
1754    rc.previous_job_name = NULL;
1755    rc.accurate_set = false;
1756    rc.spool_data_set = false;
1757    rc.ignoreduplicatecheck = false;
1758    rc.comment = NULL;
1759    rc.plugin_options = NULL;
1760
1761    for (i=1; i<ua->argc; i++) {
1762       Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
1763       kw_ok = false;
1764       /* Keep looking until we find a good keyword */
1765       for (j=0; !kw_ok && kw[j]; j++) {
1766          if (strcasecmp(ua->argk[i], kw[j]) == 0) {
1767             /* Note, yes and run have no value, so do not fail */
1768             if (!ua->argv[i] && j != YES_POS /*yes*/) {
1769                ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
1770                return false;
1771             }
1772             Dmsg2(800, "Got j=%d keyword=%s\n", j, NPRT(kw[j]));
1773             switch (j) {
1774             case 0: /* alljobid */
1775                rc.alljobid = true;
1776                /* Fall through wanted */
1777             case 1:  /* JobId */
1778                if (rc.jid && !rc.mod) {
1779                   ua->send_msg(_("JobId specified twice.\n"));
1780                   return false;
1781                }
1782                rc.jid = ua->argv[i];
1783                kw_ok = true;
1784                break;
1785             case 2: /* client */
1786             case 3: /* fd */
1787                if (rc.client_name) {
1788                   ua->send_msg(_("Client specified twice.\n"));
1789                   return false;
1790                }
1791                rc.client_name = ua->argv[i];
1792                kw_ok = true;
1793                break;
1794             case 4: /* fileset */
1795                if (rc.fileset_name) {
1796                   ua->send_msg(_("FileSet specified twice.\n"));
1797                   return false;
1798                }
1799                rc.fileset_name = ua->argv[i];
1800                kw_ok = true;
1801                break;
1802             case 5: /* level */
1803                if (rc.level_name) {
1804                   ua->send_msg(_("Level specified twice.\n"));
1805                   return false;
1806                }
1807                rc.level_name = ua->argv[i];
1808                kw_ok = true;
1809                break;
1810             case 6: /* storage */
1811             case 7: /* sd */
1812                if (rc.store_name) {
1813                   ua->send_msg(_("Storage specified twice.\n"));
1814                   return false;
1815                }
1816                rc.store_name = ua->argv[i];
1817                kw_ok = true;
1818                break;
1819             case 8: /* regexwhere */
1820                 if ((rc.regexwhere || rc.where) && !rc.mod) {
1821                   ua->send_msg(_("RegexWhere or Where specified twice.\n"));
1822                   return false;
1823                }
1824                rc.regexwhere = ua->argv[i];
1825                if (!acl_access_ok(ua, Where_ACL, rc.regexwhere)) {
1826                   ua->send_msg(_("No authorization for \"regexwhere\" specification.\n"));
1827                   return false;
1828                }
1829                kw_ok = true;
1830                break;
1831            case 9: /* where */
1832                if ((rc.where || rc.regexwhere) && !rc.mod) {
1833                   ua->send_msg(_("Where or RegexWhere specified twice.\n"));
1834                   return false;
1835                }
1836                rc.where = ua->argv[i];
1837                if (!acl_access_ok(ua, Where_ACL, rc.where)) {
1838                   ua->send_msg(_("No authoriztion for \"where\" specification.\n"));
1839                   return false;
1840                }
1841                kw_ok = true;
1842                break;
1843             case 10: /* bootstrap */
1844                if (rc.bootstrap && !rc.mod) {
1845                   ua->send_msg(_("Bootstrap specified twice.\n"));
1846                   return false;
1847                }
1848                rc.bootstrap = ua->argv[i];
1849                kw_ok = true;
1850                break;
1851             case 11: /* replace */
1852                if (rc.replace && !rc.mod) {
1853                   ua->send_msg(_("Replace specified twice.\n"));
1854                   return false;
1855                }
1856                rc.replace = ua->argv[i];
1857                kw_ok = true;
1858                break;
1859             case 12: /* When */
1860                if (rc.when && !rc.mod) {
1861                   ua->send_msg(_("When specified twice.\n"));
1862                   return false;
1863                }
1864                rc.when = ua->argv[i];
1865                kw_ok = true;
1866                break;
1867             case 13:  /* Priority */
1868                if (rc.Priority && !rc.mod) {
1869                   ua->send_msg(_("Priority specified twice.\n"));
1870                   return false;
1871                }
1872                rc.Priority = atoi(ua->argv[i]);
1873                if (rc.Priority <= 0) {
1874                   ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
1875                   rc.Priority = 10;
1876                }
1877                kw_ok = true;
1878                break;
1879             case 14: /* yes */
1880                kw_ok = true;
1881                break;
1882             case 15: /* Verify Job */
1883                if (rc.verify_job_name) {
1884                   ua->send_msg(_("Verify Job specified twice.\n"));
1885                   return false;
1886                }
1887                rc.verify_job_name = ua->argv[i];
1888                kw_ok = true;
1889                break;
1890             case 16: /* files */
1891                rc.files = atoi(ua->argv[i]);
1892                kw_ok = true;
1893                break;
1894             case 17: /* catalog */
1895                rc.catalog_name = ua->argv[i];
1896                kw_ok = true;
1897                break;
1898             case 18: /* since */
1899                rc.since = ua->argv[i];
1900                kw_ok = true;
1901                break;
1902             case 19: /* cloned */
1903                rc. cloned = true;
1904                kw_ok = true;
1905                break;
1906             case 20: /* write verify list output */
1907                rc.verify_list = ua->argv[i];
1908                kw_ok = true;
1909                break;
1910             case 21: /* Migration Job */
1911                if (rc.previous_job_name) {
1912                   ua->send_msg(_("Migration Job specified twice.\n"));
1913                   return false;
1914                }
1915                rc.previous_job_name = ua->argv[i];
1916                kw_ok = true;
1917                break;
1918             case 22: /* pool */
1919                if (rc.pool_name) {
1920                   ua->send_msg(_("Pool specified twice.\n"));
1921                   return false;
1922                }
1923                rc.pool_name = ua->argv[i];
1924                kw_ok = true;
1925                break;
1926             case 23: /* backupclient */
1927                if (rc.client_name) {
1928                   ua->send_msg(_("Client specified twice.\n"));
1929                   return 0;
1930                }
1931                rc.client_name = ua->argv[i];
1932                kw_ok = true;
1933                break;
1934             case 24: /* restoreclient */
1935                if (rc.restore_client_name && !rc.mod) {
1936                   ua->send_msg(_("Restore Client specified twice.\n"));
1937                   return false;
1938                }
1939                rc.restore_client_name = ua->argv[i];
1940                kw_ok = true;
1941                break;
1942             case 25: /* pluginoptions */
1943                ua->send_msg(_("Plugin Options not yet implemented.\n"));
1944                return false;
1945                if (rc.plugin_options) {
1946                   ua->send_msg(_("Plugin Options specified twice.\n"));
1947                   return false;
1948                }
1949                rc.plugin_options = ua->argv[i];
1950                if (!acl_access_ok(ua, PluginOptions_ACL, rc.plugin_options)) {
1951                   ua->send_msg(_("No authoriztion for \"PluginOptions\" specification.\n"));
1952                   return false;
1953                }
1954                kw_ok = true;
1955                break;
1956             case 26: /* spooldata */
1957                if (rc.spool_data_set) {
1958                   ua->send_msg(_("Spool flag specified twice.\n"));
1959                   return false;
1960                }
1961                if (is_yesno(ua->argv[i], &rc.spool_data)) {
1962                   rc.spool_data_set = true;
1963                   kw_ok = true;
1964                } else {
1965                   ua->send_msg(_("Invalid spooldata flag.\n"));
1966                }
1967                break;
1968             case 27: /* comment */
1969                rc.comment = ua->argv[i];
1970                kw_ok = true;
1971                break;
1972             case 28: /* ignoreduplicatecheck */
1973                if (rc.ignoreduplicatecheck_set) {
1974                   ua->send_msg(_("IgnoreDuplicateCheck flag specified twice.\n"));
1975                   return false;
1976                }
1977                if (is_yesno(ua->argv[i], &rc.ignoreduplicatecheck)) {
1978                   rc.ignoreduplicatecheck_set = true;
1979                   kw_ok = true;
1980                } else {
1981                   ua->send_msg(_("Invalid ignoreduplicatecheck flag.\n"));
1982                }
1983                break;
1984             case 29: /* accurate */
1985                if (rc.accurate_set) {
1986                   ua->send_msg(_("Accurate flag specified twice.\n"));
1987                   return false;
1988                }
1989                if (is_yesno(ua->argv[i], &rc.accurate)) {
1990                   rc.accurate_set = true;
1991                   kw_ok = true;
1992                } else {
1993                   ua->send_msg(_("Invalid accurate flag.\n"));
1994                }
1995                break;
1996             case 30: /* job */
1997                if (rc.job_name) {
1998                   ua->send_msg(_("Job name specified twice.\n"));
1999                   return false;
2000                }
2001                rc.job_name = ua->argv[i];
2002                kw_ok = true;
2003                break;
2004             case 31: /* mediatype */
2005                if (rc.media_type) {
2006                   ua->send_msg(_("Media Type specified twice.\n"));
2007                   return false;
2008                }
2009                rc.media_type = ua->argv[i];
2010                kw_ok = true;
2011                break;
2012             case 32: /* Next Pool */
2013                if (rc.next_pool_name) {
2014                   ua->send_msg(_("NextPool specified twice.\n"));
2015                   return false;
2016                }
2017                rc.next_pool_name = ua->argv[i];
2018                kw_ok = true;
2019                break;
2020             default:
2021                break;
2022             }
2023          } /* end strcase compare */
2024       } /* end keyword loop */
2025
2026       /*
2027        * End of keyword for loop -- if not found, we got a bogus keyword
2028        */
2029       if (!kw_ok) {
2030          Dmsg1(800, "%s not found\n", ua->argk[i]);
2031          /*
2032           * Special case for Job Name, it can be the first
2033           * keyword that has no value.
2034           */
2035          if (!rc.job_name && !ua->argv[i]) {
2036             rc.job_name = ua->argk[i];   /* use keyword as job name */
2037             Dmsg1(800, "Set jobname=%s\n", rc.job_name);
2038          } else {
2039             ua->send_msg(_("Invalid keyword: %s\n"), ua->argk[i]);
2040             return false;
2041          }
2042       }
2043    } /* end argc loop */
2044
2045    Dmsg0(800, "Done scan.\n");
2046    if (rc.comment) {
2047       if (!is_comment_legal(ua, rc.comment)) {
2048          return false;
2049       }
2050    }
2051    if (rc.catalog_name) {
2052        rc.catalog = GetCatalogResWithName(rc.catalog_name);
2053        if (rc.catalog == NULL) {
2054             ua->error_msg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
2055            return false;
2056        }
2057        if (!acl_access_ok(ua, Catalog_ACL, rc.catalog->name())) {
2058           ua->error_msg(_("No authorization. Catalog \"%s\".\n"), rc.catalog->name());
2059           return false;
2060        }
2061    }
2062    Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
2063
2064    if (!get_job(ua, rc)) {
2065       return false;
2066    }
2067
2068    if (!get_pool(ua, rc)) {
2069       return false;
2070    }
2071
2072    if (!get_next_pool(ua, rc)) {
2073       return false;
2074    }
2075
2076    if (!get_storage(ua, rc)) {
2077       return false;
2078    }
2079
2080
2081    if (!get_client(ua, rc)) {
2082       return false;
2083    }
2084
2085    if (!get_fileset(ua, rc)) {
2086       return false;
2087    }
2088
2089    if (rc.verify_job_name) {
2090       rc.verify_job = GetJobResWithName(rc.verify_job_name);
2091       if (!rc.verify_job) {
2092          ua->send_msg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
2093          rc.verify_job = select_job_resource(ua);
2094       }
2095    } else if (!rc.verify_job) {
2096       rc.verify_job = rc.job->verify_job;
2097    }
2098
2099    if (rc.previous_job_name) {
2100       rc.previous_job = GetJobResWithName(rc.previous_job_name);
2101       if (!rc.previous_job) {
2102          ua->send_msg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
2103          rc.previous_job = select_job_resource(ua);
2104       }
2105    } else {
2106       rc.previous_job = rc.job->verify_job;
2107    }
2108    return true;
2109 }