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