]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_run.c
Implement first cut running console commands in a RunScript.
[bacula/bacula] / bacula / src / dird / ua_run.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- Run Command
31  *
32  *     Kern Sibbald, December MMI
33  *
34  *   Version $Id$
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39
40 class run_ctx {
41 public:
42    char *job_name, *level_name, *jid, *store_name, *pool_name;
43    char *where, *fileset_name, *client_name, *bootstrap, *regexwhere;
44    char *restore_client_name;
45    const char *replace;
46    char *when, *verify_job_name, *catalog_name;
47    char *previous_job_name;
48    char *since;
49    const char *verify_list;
50    JOB *job;
51    JOB *verify_job;
52    JOB *previous_job;
53    USTORE *store;
54    CLIENT *client;
55    FILESET *fileset;
56    POOL *pool;
57    CAT *catalog;
58    int Priority;
59    int files;
60    bool cloned;
61    bool mod;
62
63    /* Methods */
64    run_ctx() { memset(this, 0, sizeof(run_ctx)); 
65                store = new USTORE; };
66    ~run_ctx() { delete store; };
67 };
68
69 /* Forward referenced subroutines */
70 static void select_job_level(UAContext *ua, JCR *jcr);
71 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, 
72                 const char *verify_list, char *jid, const char *replace,
73                 char *client_name);
74 static void select_where_regexp(UAContext *ua, JCR *jcr);
75 static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc);
76
77 /* Imported variables */
78 extern struct s_kw ReplaceOptions[];
79
80 /*
81  * For Backup and Verify Jobs
82  *     run [job=]<job-name> level=<level-name>
83  *
84  * For Restore Jobs
85  *     run <job-name> 
86  *
87  *  Returns: 0 on error
88  *           JobId if OK
89  *
90  */
91 int run_cmd(UAContext *ua, const char *cmd)
92 {
93    JCR *jcr = NULL;
94    run_ctx rc;
95    int i, opt;
96
97    if (!open_client_db(ua)) {
98       return 1;
99    }
100
101    if (!scan_command_line_arguments(ua, rc)) {
102       return 0;
103    }
104
105    if (find_arg(ua, NT_("fdcalled")) > 0) {
106       jcr->file_bsock = dup_bsock(ua->UA_sock);
107       ua->quit = true;
108    }
109
110 try_again:
111    /*
112     * Create JCR to run job.  NOTE!!! after this point, free_jcr()
113     *  before returning.
114     */
115    if (!jcr) {
116       jcr = new_jcr(sizeof(JCR), dird_free_jcr);
117       set_jcr_defaults(jcr, rc.job);
118       jcr->unlink_bsr = ua->jcr->unlink_bsr;    /* copy unlink flag from caller */
119       ua->jcr->unlink_bsr = false;
120    }
121
122    jcr->verify_job = rc.verify_job;
123    jcr->previous_job = rc.previous_job;
124    jcr->pool = rc.pool;
125    set_rwstorage(jcr, rc.store);
126    jcr->client = rc.client;
127    pm_strcpy(jcr->client_name, rc.client->name());
128    jcr->fileset = rc.fileset;
129    jcr->ExpectedFiles = rc.files;
130    if (rc.catalog) {
131       jcr->catalog = rc.catalog;
132    }
133    if (rc.where) {
134       if (jcr->where) {
135          free(jcr->where);
136       }
137       jcr->where = bstrdup(rc.where);
138       rc.where = NULL;
139    }
140
141    if (rc.regexwhere) {
142       if (jcr->RegexWhere) {
143          free(jcr->RegexWhere);
144       }
145       jcr->RegexWhere = bstrdup(rc.regexwhere);       
146       rc.regexwhere = NULL;
147    }
148
149    if (rc.when) {
150       jcr->sched_time = str_to_utime(rc.when);
151       if (jcr->sched_time == 0) {
152          ua->send_msg(_("Invalid time, using current time.\n"));
153          jcr->sched_time = time(NULL);
154       }
155       rc.when = NULL;
156    }
157
158    if (rc.bootstrap) {
159       if (jcr->RestoreBootstrap) {
160          free(jcr->RestoreBootstrap);
161       }
162       jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
163       rc.bootstrap = NULL;
164    }
165
166    if (rc.replace) {
167       jcr->replace = 0;
168       for (i=0; ReplaceOptions[i].name; i++) {
169          if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
170             jcr->replace = ReplaceOptions[i].token;
171          }
172       }
173       if (!jcr->replace) {
174          ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
175          goto bail_out;
176       }
177    } else if (rc.job->replace) {
178       jcr->replace = rc.job->replace;
179    } else {
180       jcr->replace = REPLACE_ALWAYS;
181    }
182    rc.replace = NULL;
183
184    if (rc.Priority) {
185       jcr->JobPriority = rc.Priority;
186       rc.Priority = 0;
187    }
188
189    if (rc.since) {
190       if (!jcr->stime) {
191          jcr->stime = get_pool_memory(PM_MESSAGE);
192       }
193       pm_strcpy(jcr->stime, rc.since);
194       rc.since = NULL;
195    }
196
197    if (rc.cloned) {
198       jcr->cloned = rc.cloned;
199       rc.cloned = false;
200    }
201
202
203    /* If pool changed, update migration write storage */
204    if (jcr->JobType == JT_MIGRATE) {
205       if (!set_migration_wstorage(jcr, rc.pool)) {
206          goto bail_out;
207       }
208    }
209    rc.replace = ReplaceOptions[0].name;
210    for (i=0; ReplaceOptions[i].name; i++) {
211       if (ReplaceOptions[i].token == jcr->replace) {
212          rc.replace = ReplaceOptions[i].name;
213       }
214    }
215    if (rc.level_name) {
216       if (!get_level_from_name(jcr, rc.level_name)) {
217          ua->send_msg(_("Level %s not valid.\n"), rc.level_name);
218          goto bail_out;
219       }
220       rc.level_name = NULL;
221    }
222    if (rc.jid) {
223       /* Note, this is also MigrateJobId */
224       jcr->RestoreJobId = str_to_int64(rc.jid);
225       rc.jid = 0;
226    }
227
228    /* Run without prompting? */
229    if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
230       goto start_job;
231    }
232
233    /*
234     * Prompt User to see if all run job parameters are correct, and
235     *   allow him to modify them.
236     */
237    if (!display_job_parameters(ua, jcr, rc.job, rc.verify_list, rc.jid, rc.replace,
238         rc.client_name)) {
239       goto bail_out;
240    }
241
242    if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
243       goto bail_out;
244    }
245
246    if (strncasecmp(ua->cmd, ".mod ", 5) == 0 || 
247        (strncasecmp(ua->cmd, "mod ", 4) == 0 && strlen(ua->cmd) > 6)) {
248       parse_ua_args(ua);
249       rc.mod = true;
250       if (!scan_command_line_arguments(ua, rc)) {
251          return 0;
252       }
253       goto try_again;
254    }
255
256    /*
257     * At user request modify parameters of job to be run.
258     */
259    if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
260       FILE *fd;
261
262       start_prompt(ua, _("Parameters to modify:\n"));
263       add_prompt(ua, _("Level"));            /* 0 */
264       add_prompt(ua, _("Storage"));          /* 1 */
265       add_prompt(ua, _("Job"));              /* 2 */
266       add_prompt(ua, _("FileSet"));          /* 3 */
267       if (jcr->JobType == JT_RESTORE) {
268          add_prompt(ua, _("Restore Client"));   /* 4 */
269       } else {
270          add_prompt(ua, _("Client"));        /* 4 */
271       }
272       add_prompt(ua, _("When"));             /* 5 */
273       add_prompt(ua, _("Priority"));         /* 6 */
274       if (jcr->JobType == JT_BACKUP ||
275           jcr->JobType == JT_MIGRATE ||
276           jcr->JobType == JT_VERIFY) {
277          add_prompt(ua, _("Pool"));          /* 7 */
278          if (jcr->JobType == JT_VERIFY) {
279             add_prompt(ua, _("Verify Job"));  /* 8 */
280          }
281       } else if (jcr->JobType == JT_RESTORE) {
282          add_prompt(ua, _("Bootstrap"));     /* 7 */
283          add_prompt(ua, _("Where"));         /* 8 */
284          add_prompt(ua, _("File Relocation"));/* 9 */    
285          add_prompt(ua, _("Replace"));       /* 10 */
286          add_prompt(ua, _("JobId"));         /* 11 */
287       }
288       switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
289       case 0:
290          /* Level */
291          select_job_level(ua, jcr);
292          goto try_again;
293       case 1:
294          /* Storage */
295          rc.store->store = select_storage_resource(ua);
296          if (rc.store->store) {
297             pm_strcpy(rc.store->store_source, _("user selection"));
298             set_rwstorage(jcr, rc.store);
299             goto try_again;
300          }
301          break;
302       case 2:
303          /* Job */
304          rc.job = select_job_resource(ua);
305          if (rc.job) {
306             jcr->job = rc.job;
307             set_jcr_defaults(jcr, rc.job);
308             goto try_again;
309          }
310          break;
311       case 3:
312          /* FileSet */
313          rc.fileset = select_fileset_resource(ua);
314          if (rc.fileset) {
315             jcr->fileset = rc.fileset;
316             goto try_again;
317          }
318          break;
319       case 4:
320          /* Client */
321          rc.client = select_client_resource(ua);
322          if (rc.client) {
323             jcr->client = rc.client;
324             goto try_again;
325          }
326          break;
327       case 5:
328          /* When */
329          if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
330             break;
331          }
332          if (ua->cmd[0] == 0) {
333             jcr->sched_time = time(NULL);
334          } else {
335             jcr->sched_time = str_to_utime(ua->cmd);
336             if (jcr->sched_time == 0) {
337                ua->send_msg(_("Invalid time, using current time.\n"));
338                jcr->sched_time = time(NULL);
339             }
340          }
341          goto try_again;
342       case 6:
343          /* Priority */
344          if (!get_pint(ua, _("Enter new Priority: "))) {
345             break;
346          }
347          if (ua->pint32_val == 0) {
348             ua->send_msg(_("Priority must be a positive integer.\n"));
349          } else {
350             jcr->JobPriority = ua->pint32_val;
351          }
352          goto try_again;
353       case 7:
354          /* Pool or Bootstrap depending on JobType */
355          if (jcr->JobType == JT_BACKUP ||
356              jcr->JobType == JT_MIGRATE ||
357              jcr->JobType == JT_VERIFY) {      /* Pool */
358             rc.pool = select_pool_resource(ua);
359             if (rc.pool) {
360                jcr->pool = rc.pool;
361                Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
362                goto try_again;
363             }
364             break;
365          }
366
367          /* Bootstrap */
368          if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
369             break;
370          }
371          if (jcr->RestoreBootstrap) {
372             free(jcr->RestoreBootstrap);
373             jcr->RestoreBootstrap = NULL;
374          }
375          if (ua->cmd[0] != 0) {
376             jcr->RestoreBootstrap = bstrdup(ua->cmd);
377             fd = fopen(jcr->RestoreBootstrap, "rb");
378             if (!fd) {
379                berrno be;
380                ua->send_msg(_("Warning cannot open %s: ERR=%s\n"),
381                   jcr->RestoreBootstrap, be.bstrerror());
382                free(jcr->RestoreBootstrap);
383                jcr->RestoreBootstrap = NULL;
384             } else {
385                fclose(fd);
386             }
387          }
388          goto try_again;
389       case 8:
390          /* Verify Job */
391          if (jcr->JobType == JT_VERIFY) {
392             rc.verify_job = select_job_resource(ua);
393             if (rc.verify_job) {
394               jcr->verify_job = rc.verify_job;
395             }
396             goto try_again;
397          }
398          /* Where */
399          if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) {
400             break;
401          }
402          if (jcr->RegexWhere) { /* cannot use regexwhere and where */
403             free(jcr->RegexWhere);
404             jcr->RegexWhere = NULL;
405          }
406          if (jcr->where) {
407             free(jcr->where);
408             jcr->where = NULL;
409          }
410          if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
411             ua->cmd[0] = 0;
412          }
413          jcr->where = bstrdup(ua->cmd);
414          goto try_again;
415       case 9: 
416          /* File relocation */
417          select_where_regexp(ua, jcr);
418          goto try_again;
419       case 10:
420          /* Replace */
421          start_prompt(ua, _("Replace:\n"));
422          for (i=0; ReplaceOptions[i].name; i++) {
423             add_prompt(ua, ReplaceOptions[i].name);
424          }
425          opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
426          if (opt >=  0) {
427             rc.replace = ReplaceOptions[opt].name;
428             jcr->replace = ReplaceOptions[opt].token;
429          }
430          goto try_again;
431       case 11:
432          /* JobId */
433          rc.jid = NULL;                  /* force reprompt */
434          jcr->RestoreJobId = 0;
435          if (jcr->RestoreBootstrap) {
436             ua->send_msg(_("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
437          }
438          goto try_again;
439       case -1:                        /* error or cancel */
440          goto bail_out;
441       default:
442          goto try_again;
443       }
444       goto bail_out;
445    }
446
447    if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
448       JobId_t JobId;
449       Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
450
451 start_job:
452       Dmsg3(100, "JobId=%u using pool %s priority=%d\n", (int)jcr->JobId, 
453             jcr->pool->name(), jcr->JobPriority);
454       JobId = run_job(jcr);
455       Dmsg4(100, "JobId=%u NewJobId=%d using pool %s priority=%d\n", (int)jcr->JobId, 
456             JobId, jcr->pool->name(), jcr->JobPriority);
457       free_jcr(jcr);                  /* release jcr */
458       if (JobId == 0) {
459          ua->error_msg(_("Job failed.\n"));
460       } else {
461          char ed1[50];
462          ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
463       }
464       return JobId;
465    }
466
467 bail_out:
468    ua->send_msg(_("Job not run.\n"));
469    free_jcr(jcr);
470    return 0;                       /* do not run */
471 }
472
473 static void select_where_regexp(UAContext *ua, JCR *jcr)
474 {
475    alist *regs;
476    char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
477    strip_prefix = add_suffix = rwhere = add_prefix = NULL;
478
479 try_again_reg:
480    ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
481                 NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
482
483    start_prompt(ua, _("This will replace your current Where value\n"));
484    add_prompt(ua, _("Strip prefix"));                /* 0 */
485    add_prompt(ua, _("Add prefix"));                  /* 1 */
486    add_prompt(ua, _("Add file suffix"));             /* 2 */
487    add_prompt(ua, _("Enter a regexp"));              /* 3 */
488    add_prompt(ua, _("Test filename manipulation"));  /* 4 */
489    add_prompt(ua, _("Use this ?"));                  /* 5 */
490    
491    switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
492    case 0:
493       /* Strip prefix */
494       if (get_cmd(ua, _("Please enter path prefix to strip: "))) {
495          if (strip_prefix) bfree(strip_prefix);
496          strip_prefix = bstrdup(ua->cmd);
497       }
498       
499       goto try_again_reg;
500    case 1:
501       /* Add prefix */
502       if (get_cmd(ua, _("Please enter path prefix to add (/ for none): "))) {
503          if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
504             ua->cmd[0] = 0;
505          }
506
507          if (add_prefix) bfree(add_prefix);
508          add_prefix = bstrdup(ua->cmd);
509       }
510       goto try_again_reg;
511    case 2:
512       /* Add suffix */
513       if (get_cmd(ua, _("Please enter file suffix to add: "))) {
514          if (add_suffix) bfree(add_suffix);
515          add_suffix = bstrdup(ua->cmd);
516       }      
517       goto try_again_reg;
518    case 3:
519       /* Add rwhere */
520       if (get_cmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
521          if (rwhere) bfree(rwhere);
522          rwhere = bstrdup(ua->cmd);
523       }
524       
525       goto try_again_reg;      
526    case 4:
527       /* Test regexp */ 
528       char *result;
529       char *regexp;
530       
531       if (rwhere && rwhere[0] != '\0') {
532          regs = get_bregexps(rwhere);
533          ua->send_msg(_("regexwhere=%s\n"), NPRT(rwhere));
534       } else {
535          int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
536          regexp = (char *) bmalloc (len * sizeof(char));
537          bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
538          regs = get_bregexps(regexp);
539          ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
540                       NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp));
541          
542          bfree(regexp);
543       }
544
545       if (!regs) {
546          ua->send_msg(_("Cannot use your regexp\n"));
547          goto try_again_reg;
548       }
549
550       while (get_cmd(ua, _("Please enter filename to test: "))) {
551          apply_bregexps(ua->cmd, regs, &result);
552          ua->send_msg(_("%s -> %s\n"), ua->cmd, result);
553       }
554       free_bregexps(regs);
555       delete regs;
556       goto try_again_reg;
557
558    case 5:
559       /* OK */
560       break;
561    case -1:                        /* error or cancel */
562       goto bail_out_reg;
563    default:
564       goto try_again_reg;
565    }
566
567    /* replace the existing where */
568    if (jcr->where) {
569       bfree(jcr->where);
570       jcr->where = NULL;
571    }
572
573    /* replace the existing regexwhere */
574    if (jcr->RegexWhere) {
575       bfree(jcr->RegexWhere);
576       jcr->RegexWhere = NULL;
577    }
578
579    if (rwhere) {
580       jcr->RegexWhere = bstrdup(rwhere);
581    } else if (strip_prefix || add_prefix || add_suffix) {
582       int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
583       jcr->RegexWhere = (char *) bmalloc(len*sizeof(char));
584       bregexp_build_where(jcr->RegexWhere, len, strip_prefix, add_prefix, add_suffix);
585    }
586
587    regs = get_bregexps(jcr->RegexWhere);
588    if (regs) {
589       free_bregexps(regs);
590       delete regs;
591    } else {
592       if (jcr->RegexWhere) {
593          bfree(jcr->RegexWhere);
594          jcr->RegexWhere = NULL;
595       }
596       ua->send_msg(_("Cannot use your regexp.\n"));
597    }
598
599 bail_out_reg:
600    if (strip_prefix) bfree(strip_prefix);
601    if (add_prefix)   bfree(add_prefix);
602    if (add_suffix)   bfree(add_suffix);
603    if (rwhere)       bfree(rwhere);
604 }
605
606 static void select_job_level(UAContext *ua, JCR *jcr)
607 {
608    if (jcr->JobType == JT_BACKUP) {
609       start_prompt(ua, _("Levels:\n"));
610       add_prompt(ua, _("Base"));
611       add_prompt(ua, _("Full"));
612       add_prompt(ua, _("Incremental"));
613       add_prompt(ua, _("Differential"));
614       add_prompt(ua, _("Since"));
615       switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
616       case 0:
617          jcr->JobLevel = L_BASE;
618          break;
619       case 1:
620          jcr->JobLevel = L_FULL;
621          break;
622       case 2:
623          jcr->JobLevel = L_INCREMENTAL;
624          break;
625       case 3:
626          jcr->JobLevel = L_DIFFERENTIAL;
627          break;
628       case 4:
629          jcr->JobLevel = L_SINCE;
630          break;
631       default:
632          break;
633       }
634    } else if (jcr->JobType == JT_VERIFY) {
635       start_prompt(ua, _("Levels:\n"));
636       add_prompt(ua, _("Initialize Catalog"));
637       add_prompt(ua, _("Verify Catalog"));
638       add_prompt(ua, _("Verify Volume to Catalog"));
639       add_prompt(ua, _("Verify Disk to Catalog"));
640       add_prompt(ua, _("Verify Volume Data (not yet implemented)"));
641       switch (do_prompt(ua, "",  _("Select level"), NULL, 0)) {
642       case 0:
643          jcr->JobLevel = L_VERIFY_INIT;
644          break;
645       case 1:
646          jcr->JobLevel = L_VERIFY_CATALOG;
647          break;
648       case 2:
649          jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
650          break;
651       case 3:
652          jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
653          break;
654       case 4:
655          jcr->JobLevel = L_VERIFY_DATA;
656          break;
657       default:
658          break;
659       }
660    } else {
661       ua->warning_msg(_("Level not appropriate for this Job. Cannot be changed.\n"));
662    }
663    return;
664 }
665
666 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char *verify_list,
667    char *jid, const char *replace, char *client_name) 
668 {
669    Dmsg1(800, "JobType=%c\n", jcr->JobType);
670    switch (jcr->JobType) {
671       char ec1[30];
672       char dt[MAX_TIME_LENGTH];
673    case JT_ADMIN:
674       if (ua->api) ua->signal(BNET_RUN_CMD);   
675       ua->send_msg(_("Run %s job\n"
676                      "JobName:  %s\n"
677                      "FileSet:  %s\n"
678                      "Client:   %s\n"
679                      "Storage:  %s\n"
680                      "When:     %s\n"
681                      "Priority: %d\n"),
682                  _("Admin"),
683                  job->name(),
684                  jcr->fileset->name(),
685                  NPRT(jcr->client->name()),
686                  jcr->wstore?jcr->wstore->name():"*None*",
687                  bstrutime(dt, sizeof(dt), jcr->sched_time),
688                  jcr->JobPriority);
689       jcr->JobLevel = L_FULL;
690       break;
691    case JT_BACKUP:
692    case JT_VERIFY:
693       if (jcr->JobType == JT_BACKUP) {
694          if (ua->api) ua->signal(BNET_RUN_CMD);   
695          ua->send_msg(_("Run %s job\n"
696                         "JobName:  %s\n"
697                         "Level:    %s\n"
698                         "Client:   %s\n"
699                         "FileSet:  %s\n"
700                         "Pool:     %s (From %s)\n"
701                         "Storage:  %s (From %s)\n"
702                         "When:     %s\n"
703                         "Priority: %d\n"),
704                  _("Backup"),
705                  job->name(),
706                  level_to_str(jcr->JobLevel),
707                  jcr->client->name(),
708                  jcr->fileset->name(),
709                  NPRT(jcr->pool->name()), jcr->pool_source,
710                  jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
711                  bstrutime(dt, sizeof(dt), jcr->sched_time),
712                  jcr->JobPriority);
713       } else {  /* JT_VERIFY */
714          const char *Name;
715          if (jcr->verify_job) {
716             Name = jcr->verify_job->name();
717          } else {
718             Name = "";
719          }
720          if (!verify_list) {
721             verify_list = job->WriteVerifyList;
722          }
723          if (!verify_list) {
724             verify_list = "";
725          }
726          if (ua->api) ua->signal(BNET_RUN_CMD);   
727          ua->send_msg(_("Run %s job\n"
728                         "JobName:     %s\n"
729                         "Level:       %s\n"
730                         "Client:      %s\n"
731                         "FileSet:     %s\n"
732                         "Pool:        %s (From %s)\n"
733                         "Storage:     %s (From %s)\n"
734                         "Verify Job:  %s\n"
735                         "Verify List: %s\n"
736                         "When:        %s\n"
737                         "Priority:    %d\n"),
738               _("Verify"),
739               job->name(),
740               level_to_str(jcr->JobLevel),
741               jcr->client->name(),
742               jcr->fileset->name(),
743               NPRT(jcr->pool->name()), jcr->pool_source,
744               jcr->rstore->name(), jcr->rstore_source,
745               Name,
746               verify_list,
747               bstrutime(dt, sizeof(dt), jcr->sched_time),
748               jcr->JobPriority);
749       }
750       break;
751    case JT_RESTORE:
752       if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
753          if (jid) {
754             jcr->RestoreJobId = str_to_int64(jid);
755          } else {
756             if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
757                return false;
758             }
759             jcr->RestoreJobId = ua->int64_val;
760          }
761       }
762       jcr->JobLevel = L_FULL;      /* default level */
763       Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
764       if (jcr->RestoreJobId == 0) {
765          if (ua->api) ua->signal(BNET_RUN_CMD);   
766          /* RegexWhere is take before RestoreWhere */
767          if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
768             ua->send_msg(_("Run Restore job\n"
769                         "JobName:        f%s\n"
770                         "Bootstrap:       %s\n"
771                         "RegexWhere:      %s\n"
772                         "Replace:         %s\n"
773                         "FileSet:         %s\n"
774                         "Backup Client:   %s\n"
775                         "Restore Client:  %s\n"
776                         "Storage:         %s\n"
777                         "When:            %s\n"
778                         "Catalog:         %s\n"
779                         "Priority:        %d\n"),
780                  job->name(),
781                  NPRT(jcr->RestoreBootstrap), 
782                  jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere,
783                  replace,
784                  jcr->fileset->name(),
785                  client_name,
786                  jcr->client->name(),
787                  jcr->rstore->name(),
788                  bstrutime(dt, sizeof(dt), jcr->sched_time),
789                  jcr->catalog->name(),
790                  jcr->JobPriority);
791
792          } else {
793             ua->send_msg(_("Run Restore job\n"
794                         "JobName:         %s\n"
795                         "Bootstrap:       %s\n"
796                         "Where:           %s\n"
797                         "Replace:         %s\n"
798                         "FileSet:         %s\n"
799                         "Backup Client:   %s\n"
800                         "Restore Client:  %s\n"
801                         "Storage:         %s\n"
802                         "When:            %s\n"
803                         "Catalog:         %s\n"
804                         "Priority:        %d\n"),
805                  job->name(),
806                  NPRT(jcr->RestoreBootstrap), 
807                  jcr->where?jcr->where:NPRT(job->RestoreWhere), 
808                  replace,
809                  jcr->fileset->name(),
810                  client_name,
811                  jcr->client->name(),
812                  jcr->rstore->name(),
813                  bstrutime(dt, sizeof(dt), jcr->sched_time),
814                  jcr->catalog->name(),
815                  jcr->JobPriority);
816          }
817
818       } else {
819          if (ua->api) ua->signal(BNET_RUN_CMD);   
820          ua->send_msg(_("Run Restore job\n"
821                         "JobName:    %s\n"
822                         "Bootstrap:  %s\n"),
823                       job->name(),
824                       NPRT(jcr->RestoreBootstrap));
825                       
826          /* RegexWhere is take before RestoreWhere */
827          if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
828             ua->send_msg(_("RegexWhere: %s\n"),
829                          jcr->RegexWhere?jcr->RegexWhere:job->RegexWhere);
830          } else {
831             ua->send_msg(_("Where:      %s\n"),
832                          jcr->where?jcr->where:NPRT(job->RestoreWhere));
833          }
834
835          ua->send_msg(_("Replace:    %s\n"
836                         "Client:     %s\n"
837                         "Storage:    %s\n"
838                         "JobId:      %s\n"
839                         "When:       %s\n"
840                         "Catalog:    %s\n"
841                         "Priority:   %d\n"),
842               replace,
843               jcr->client->name(),
844               jcr->rstore->name(),
845               jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
846               bstrutime(dt, sizeof(dt), jcr->sched_time),
847               jcr->catalog->name(),
848               jcr->JobPriority);
849       }
850       break;
851    case JT_MIGRATE:
852       jcr->JobLevel = L_FULL;      /* default level */
853       if (ua->api) ua->signal(BNET_RUN_CMD);   
854       ua->send_msg(_("Run Migration job\n"
855                      "JobName:       %s\n"
856                      "Bootstrap:     %s\n"
857                      "Client:        %s\n"
858                      "FileSet:       %s\n"
859                      "Pool:          %s (From %s)\n"
860                      "Read Storage:  %s (From %s)\n"
861                      "Write Storage: %s (From %s)\n"
862                      "JobId:         %s\n"
863                      "When:          %s\n"
864                      "Catalog:       %s\n"
865                      "Priority:      %d\n"),
866            job->name(),
867            NPRT(jcr->RestoreBootstrap),
868            jcr->client->name(),
869            jcr->fileset->name(),
870            NPRT(jcr->pool->name()), jcr->pool_source,
871            jcr->rstore->name(), jcr->rstore_source,
872            jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
873            jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
874            bstrutime(dt, sizeof(dt), jcr->sched_time),
875            jcr->catalog->name(),
876            jcr->JobPriority);
877       break;
878    default:
879       ua->error_msg(_("Unknown Job Type=%d\n"), jcr->JobType);
880       return false;
881    }
882    return true;
883 }
884
885
886 static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc)
887 {
888    bool kw_ok;
889    int i, j;
890    static const char *kw[] = {        /* command line arguments */
891       "job",                          /*  Used in a switch() */
892       "jobid",                        /* 1 */
893       "client",                       /* 2 */
894       "fd",
895       "fileset",                      /* 4 */
896       "level",                        /* 5 */
897       "storage",                      /* 6 */
898       "sd",                           /* 7 */
899       "regexwhere",                   /* 8 where string as a bregexp */
900       "where",                        /* 9 */
901       "bootstrap",                    /* 10 */
902       "replace",                      /* 11 */
903       "when",                         /* 12 */
904       "priority",                     /* 13 */
905       "yes",          /* 14  -- if you change this change YES_POS too */
906       "verifyjob",                    /* 15 */
907       "files",                        /* 16 number of files to restore */
908       "catalog",                      /* 17 override catalog */
909       "since",                        /* 18 since */
910       "cloned",                       /* 19 cloned */
911       "verifylist",                   /* 20 verify output list */
912       "migrationjob",                 /* 21 migration job name */
913       "pool",                         /* 22 */
914       "backupclient",                 /* 23 */
915       "restoreclient",                /* 24 */
916       NULL};
917
918 #define YES_POS 14
919
920    rc.catalog_name = NULL;
921    rc.job_name = NULL;
922    rc.pool_name = NULL;
923    rc.store_name = NULL;
924    rc.client_name = NULL;
925    rc.restore_client_name = NULL;
926    rc.fileset_name = NULL;
927    rc.verify_job_name = NULL;
928    rc.previous_job_name = NULL;
929
930
931    for (i=1; i<ua->argc; i++) {
932       Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
933       kw_ok = false;
934       /* Keep looking until we find a good keyword */
935       for (j=0; !kw_ok && kw[j]; j++) {
936          if (strcasecmp(ua->argk[i], kw[j]) == 0) {
937             /* Note, yes and run have no value, so do not fail */
938             if (!ua->argv[i] && j != YES_POS /*yes*/) {
939                ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
940                return false;
941             }
942             Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j]));
943             switch (j) {
944             case 0: /* job */
945                if (rc.job_name) {
946                   ua->send_msg(_("Job name specified twice.\n"));
947                   return false;
948                }
949                rc.job_name = ua->argv[i];
950                kw_ok = true;
951                break;
952             case 1: /* JobId */
953                if (rc.jid && !rc.mod) {
954                   ua->send_msg(_("JobId specified twice.\n"));
955                   return false;
956                }
957                rc.jid = ua->argv[i];
958                kw_ok = true;
959                break;
960             case 2: /* client */
961             case 3: /* fd */
962                if (rc.client_name) {
963                   ua->send_msg(_("Client specified twice.\n"));
964                   return false;
965                }
966                rc.client_name = ua->argv[i];
967                kw_ok = true;
968                break;
969             case 4: /* fileset */
970                if (rc.fileset_name) {
971                   ua->send_msg(_("FileSet specified twice.\n"));
972                   return false;
973                }
974                rc.fileset_name = ua->argv[i];
975                kw_ok = true;
976                break;
977             case 5: /* level */
978                if (rc.level_name) {
979                   ua->send_msg(_("Level specified twice.\n"));
980                   return false;
981                }
982                rc.level_name = ua->argv[i];
983                kw_ok = true;
984                break;
985             case 6: /* storage */
986             case 7: /* sd */
987                if (rc.store_name) {
988                   ua->send_msg(_("Storage specified twice.\n"));
989                   return false;
990                }
991                rc.store_name = ua->argv[i];
992                kw_ok = true;
993                break;
994             case 8: /* regexwhere */
995                 if ((rc.regexwhere || rc.where) && !rc.mod) {
996                   ua->send_msg(_("RegexWhere or Where specified twice.\n"));
997                   return false;
998                }
999                rc.regexwhere = ua->argv[i];
1000                if (!acl_access_ok(ua, Where_ACL, rc.regexwhere)) {
1001                   ua->send_msg(_("No authorization for \"regexwhere\" specification.\n"));
1002                   return false;
1003                }
1004                kw_ok = true;
1005                break;
1006            case 9: /* where */
1007                if ((rc.where || rc.regexwhere) && !rc.mod) {
1008                   ua->send_msg(_("Where or RegexWhere specified twice.\n"));
1009                   return false;
1010                }
1011                rc.where = ua->argv[i];
1012                if (!acl_access_ok(ua, Where_ACL, rc.where)) {
1013                   ua->send_msg(_("No authoriztion for \"where\" specification.\n"));
1014                   return false;
1015                }
1016                kw_ok = true;
1017                break;
1018             case 10: /* bootstrap */
1019                if (rc.bootstrap && !rc.mod) {
1020                   ua->send_msg(_("Bootstrap specified twice.\n"));
1021                   return false;
1022                }
1023                rc.bootstrap = ua->argv[i];
1024                kw_ok = true;
1025                break;
1026             case 11: /* replace */
1027                if (rc.replace && !rc.mod) {
1028                   ua->send_msg(_("Replace specified twice.\n"));
1029                   return false;
1030                }
1031                rc.replace = ua->argv[i];
1032                kw_ok = true;
1033                break;
1034             case 12: /* When */
1035                if (rc.when && !rc.mod) {
1036                   ua->send_msg(_("When specified twice.\n"));
1037                   return false;
1038                }
1039                rc.when = ua->argv[i];
1040                kw_ok = true;
1041                break;
1042             case 13:  /* Priority */
1043                if (rc.Priority && !rc.mod) {
1044                   ua->send_msg(_("Priority specified twice.\n"));
1045                   return false;
1046                }
1047                rc.Priority = atoi(ua->argv[i]);
1048                if (rc.Priority <= 0) {
1049                   ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
1050                   rc.Priority = 10;
1051                }
1052                kw_ok = true;
1053                break;
1054             case 14: /* yes */
1055                kw_ok = true;
1056                break;
1057             case 15: /* Verify Job */
1058                if (rc.verify_job_name) {
1059                   ua->send_msg(_("Verify Job specified twice.\n"));
1060                   return false;
1061                }
1062                rc.verify_job_name = ua->argv[i];
1063                kw_ok = true;
1064                break;
1065             case 16: /* files */
1066                rc.files = atoi(ua->argv[i]);
1067                kw_ok = true;
1068                break;
1069
1070             case 17: /* catalog */
1071                rc.catalog_name = ua->argv[i];
1072                kw_ok = true;
1073                break;
1074
1075             case 18: /* since */
1076                rc.since = ua->argv[i];
1077                kw_ok = true; 
1078                break;
1079
1080             case 19: /* cloned */
1081                rc. cloned = true;
1082                kw_ok = true;
1083                break;
1084
1085             case 20: /* write verify list output */
1086                rc.verify_list = ua->argv[i];
1087                kw_ok = true;
1088                break;
1089             case 21: /* Migration Job */
1090                if (rc.previous_job_name) {
1091                   ua->send_msg(_("Migration Job specified twice.\n"));
1092                   return false;
1093                }
1094                rc.previous_job_name = ua->argv[i];
1095                kw_ok = true;
1096                break;
1097             case 22: /* pool */
1098                if (rc.pool_name) {
1099                   ua->send_msg(_("Pool specified twice.\n"));
1100                   return false;
1101                }
1102                rc.pool_name = ua->argv[i];
1103                kw_ok = true;
1104                break;
1105             case 23: /* backupclient */
1106                if (rc.client_name) {
1107                   ua->send_msg(_("Client specified twice.\n"));
1108                   return 0;
1109                }
1110                rc.client_name = ua->argv[i];
1111                kw_ok = true;
1112                break;
1113             case 24: /* restoreclient */
1114                if (rc.restore_client_name && !rc.mod) {
1115                   ua->send_msg(_("Restore Client specified twice.\n"));
1116                   return false;
1117                }
1118                rc.restore_client_name = ua->argv[i];
1119                kw_ok = true;
1120                break;
1121             default:
1122                break;
1123             }
1124          } /* end strcase compare */
1125       } /* end keyword loop */
1126       /*
1127        * End of keyword for loop -- if not found, we got a bogus keyword
1128        */
1129       if (!kw_ok) {
1130          Dmsg1(800, "%s not found\n", ua->argk[i]);
1131          /*
1132           * Special case for Job Name, it can be the first
1133           * keyword that has no value.
1134           */
1135          if (!rc.job_name && !ua->argv[i]) {
1136             rc.job_name = ua->argk[i];   /* use keyword as job name */
1137             Dmsg1(800, "Set jobname=%s\n", rc.job_name);
1138          } else {
1139             ua->send_msg(_("Invalid keyword: %s\n"), ua->argk[i]);
1140             return false;
1141          }
1142       }
1143    } /* end argc loop */
1144              
1145    Dmsg0(800, "Done scan.\n");
1146
1147    if (rc.catalog_name) {
1148        rc.catalog = GetCatalogResWithName(rc.catalog_name);
1149        if (rc.catalog == NULL) {
1150             ua->error_msg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
1151            return false;
1152        }
1153        if (!acl_access_ok(ua, Catalog_ACL, rc.catalog->name())) {
1154           ua->error_msg(_("No authorization. Catalog \"%s\".\n"), rc.catalog->name());
1155           return false;
1156        }
1157    }
1158    Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
1159
1160    if (rc.job_name) {
1161       /* Find Job */
1162       rc.job = GetJobResWithName(rc.job_name);
1163       if (!rc.job) {
1164          if (*rc.job_name != 0) {
1165             ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name);
1166          }
1167          rc.job = select_job_resource(ua);
1168       } else {
1169          Dmsg1(800, "Found job=%s\n", rc.job_name);
1170       }
1171    } else if (!rc.job) {
1172       ua->send_msg(_("A job name must be specified.\n"));
1173       rc.job = select_job_resource(ua);
1174    }
1175    if (!rc.job) {
1176       return false;
1177    } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) {
1178       ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name());
1179       return false;
1180    }
1181
1182    if (rc.pool_name) {
1183       rc.pool = GetPoolResWithName(rc.pool_name);
1184       if (!rc.pool) {
1185          if (*rc.pool_name != 0) {
1186             ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name);
1187          }
1188          rc.pool = select_pool_resource(ua);
1189       }
1190    } else if (!rc.pool) {
1191       rc.pool = rc.job->pool;             /* use default */
1192    }
1193    if (!rc.pool) {
1194       return false;
1195    } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) {
1196       ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name());
1197       return false;
1198    }
1199    Dmsg1(100, "Using pool %s\n", rc.pool->name());
1200
1201    if (rc.store_name) {
1202       rc.store->store = GetStoreResWithName(rc.store_name);
1203       pm_strcpy(rc.store->store_source, _("command line"));
1204       if (!rc.store->store) {
1205          if (*rc.store_name != 0) {
1206             ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
1207          }
1208          rc.store->store = select_storage_resource(ua);
1209          pm_strcpy(rc.store->store_source, _("user selection"));
1210       }
1211    } else if (!rc.store->store) {
1212       get_job_storage(rc.store, rc.job, NULL);      /* use default */
1213    }
1214    if (!rc.store->store) {
1215       ua->error_msg(_("No storage specified.\n"));
1216       return false;
1217    } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
1218       ua->error_msg(_("No authorization. Storage \"%s\".\n"),
1219                rc.store->store->name());
1220       return false;
1221    }
1222    Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
1223
1224    if (rc.client_name) {
1225       rc.client = GetClientResWithName(rc.client_name);
1226       if (!rc.client) {
1227          if (*rc.client_name != 0) {
1228             ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name);
1229          }
1230          rc.client = select_client_resource(ua);
1231       }
1232    } else if (!rc.client) {
1233       rc.client = rc.job->client;           /* use default */
1234    }
1235    if (!rc.client) {
1236       return false;
1237    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
1238       ua->error_msg(_("No authorization. Client \"%s\".\n"),
1239                rc.client->name());
1240       return false;
1241    }
1242    Dmsg1(800, "Using client=%s\n", rc.client->name());
1243
1244    if (rc.restore_client_name) {
1245       rc.client = GetClientResWithName(rc.restore_client_name);
1246       if (!rc.client) {
1247          if (*rc.restore_client_name != 0) {
1248             ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name);
1249          }
1250          rc.client = select_client_resource(ua);
1251       }
1252    } else if (!rc.client) {
1253       rc.client = rc.job->client;           /* use default */
1254    }
1255    if (!rc.client) {
1256       return false;
1257    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
1258       ua->error_msg(_("No authorization. Client \"%s\".\n"),
1259                rc.client->name());
1260       return false;
1261    }
1262    Dmsg1(800, "Using restore client=%s\n", rc.client->name());
1263
1264
1265    if (rc.fileset_name) {
1266       rc.fileset = GetFileSetResWithName(rc.fileset_name);
1267       if (!rc.fileset) {
1268          ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
1269          rc.fileset = select_fileset_resource(ua);
1270       }
1271    } else if (!rc.fileset) {
1272       rc.fileset = rc.job->fileset;           /* use default */
1273    }
1274    if (!rc.fileset) {
1275       return false;
1276    } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) {
1277       ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
1278                rc.fileset->name());
1279       return false;
1280    }
1281
1282    if (rc.verify_job_name) {
1283       rc.verify_job = GetJobResWithName(rc.verify_job_name);
1284       if (!rc.verify_job) {
1285          ua->send_msg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
1286          rc.verify_job = select_job_resource(ua);
1287       }
1288    } else if (!rc.verify_job) {
1289       rc.verify_job = rc.job->verify_job;
1290    }
1291
1292    if (rc.previous_job_name) {
1293       rc.previous_job = GetJobResWithName(rc.previous_job_name);
1294       if (!rc.previous_job) {
1295          ua->send_msg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
1296          rc.previous_job = select_job_resource(ua);
1297       }
1298    } else {
1299       rc.previous_job = rc.job->verify_job;
1300    }
1301    return true;
1302 }