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