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