]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_run.c
First cut of adding .mod to the run command.
[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    for (i=1; i<ua->argc; i++) {
901       Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
902       kw_ok = false;
903       /* Keep looking until we find a good keyword */
904       for (j=0; !kw_ok && kw[j]; j++) {
905          if (strcasecmp(ua->argk[i], kw[j]) == 0) {
906             /* Note, yes and run have no value, so do not fail */
907             if (!ua->argv[i] && j != YES_POS /*yes*/) {
908                ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
909                return 1;
910             }
911             Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j]));
912             switch (j) {
913             case 0: /* job */
914                if (rc.job_name && !rc.mod) {
915                   ua->send_msg(_("Job name specified twice.\n"));
916                   return false;
917                }
918                rc.job_name = ua->argv[i];
919                kw_ok = true;
920                break;
921             case 1: /* JobId */
922                if (rc.jid && !rc.mod) {
923                   ua->send_msg(_("JobId specified twice.\n"));
924                   return false;
925                }
926                rc.jid = ua->argv[i];
927                kw_ok = true;
928                break;
929             case 2: /* client */
930             case 3: /* fd */
931                if (rc.client_name && !rc.mod) {
932                   ua->send_msg(_("Client specified twice.\n"));
933                   return false;
934                }
935                rc.client_name = ua->argv[i];
936                kw_ok = true;
937                break;
938             case 4: /* fileset */
939                if (rc.fileset_name && !rc.mod) {
940                   ua->send_msg(_("FileSet specified twice.\n"));
941                   return false;
942                }
943                rc.fileset_name = ua->argv[i];
944                kw_ok = true;
945                break;
946             case 5: /* level */
947                if (rc.level_name && !rc.mod) {
948                   ua->send_msg(_("Level specified twice.\n"));
949                   return false;
950                }
951                rc.level_name = ua->argv[i];
952                kw_ok = true;
953                break;
954             case 6: /* storage */
955             case 7: /* sd */
956                if (rc.store_name && !rc.mod) {
957                   ua->send_msg(_("Storage specified twice.\n"));
958                   return false;
959                }
960                rc.store_name = ua->argv[i];
961                kw_ok = true;
962                break;
963             case 8: /* regexwhere */
964                 if ((rc.regexwhere || rc.where) && !rc.mod) {
965                   ua->send_msg(_("RegexWhere or Where specified twice.\n"));
966                   return false;
967                }
968                rc.regexwhere = ua->argv[i];
969                if (!acl_access_ok(ua, Where_ACL, rc.regexwhere)) {
970                   ua->send_msg(_("No authorization for \"regexwhere\" specification.\n"));
971                   return false;
972                }
973                kw_ok = true;
974                break;
975            case 9: /* where */
976                if ((rc.where || rc.regexwhere) && !rc.mod) {
977                   ua->send_msg(_("Where or RegexWhere specified twice.\n"));
978                   return false;
979                }
980                rc.where = ua->argv[i];
981                if (!acl_access_ok(ua, Where_ACL, rc.where)) {
982                   ua->send_msg(_("No authoriztion for \"where\" specification.\n"));
983                   return false;
984                }
985                kw_ok = true;
986                break;
987             case 10: /* bootstrap */
988                if (rc.bootstrap && !rc.mod) {
989                   ua->send_msg(_("Bootstrap specified twice.\n"));
990                   return false;
991                }
992                rc.bootstrap = ua->argv[i];
993                kw_ok = true;
994                break;
995             case 11: /* replace */
996                if (rc.replace && !rc.mod) {
997                   ua->send_msg(_("Replace specified twice.\n"));
998                   return false;
999                }
1000                rc.replace = ua->argv[i];
1001                kw_ok = true;
1002                break;
1003             case 12: /* When */
1004                if (rc.when && !rc.mod) {
1005                   ua->send_msg(_("When specified twice.\n"));
1006                   return false;
1007                }
1008                rc.when = ua->argv[i];
1009                kw_ok = true;
1010                break;
1011             case 13:  /* Priority */
1012                if (rc.Priority && !rc.mod) {
1013                   ua->send_msg(_("Priority specified twice.\n"));
1014                   return false;
1015                }
1016                rc.Priority = atoi(ua->argv[i]);
1017                if (rc.Priority <= 0) {
1018                   ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
1019                   rc.Priority = 10;
1020                }
1021                kw_ok = true;
1022                break;
1023             case 14: /* yes */
1024                kw_ok = true;
1025                break;
1026             case 15: /* Verify Job */
1027                if (rc.verify_job_name && !rc.mod) {
1028                   ua->send_msg(_("Verify Job specified twice.\n"));
1029                   return false;
1030                }
1031                rc.verify_job_name = ua->argv[i];
1032                kw_ok = true;
1033                break;
1034             case 16: /* files */
1035                rc.files = atoi(ua->argv[i]);
1036                kw_ok = true;
1037                break;
1038
1039             case 17: /* catalog */
1040                rc.catalog_name = ua->argv[i];
1041                kw_ok = true;
1042                break;
1043
1044             case 18: /* since */
1045                rc.since = ua->argv[i];
1046                kw_ok = true; 
1047                break;
1048
1049             case 19: /* cloned */
1050                rc. cloned = true;
1051                kw_ok = true;
1052                break;
1053
1054             case 20: /* write verify list output */
1055                rc.verify_list = ua->argv[i];
1056                kw_ok = true;
1057                break;
1058             case 21: /* Migration Job */
1059                if (rc.previous_job_name && !rc.mod) {
1060                   ua->send_msg(_("Migration Job specified twice.\n"));
1061                   return false;
1062                }
1063                rc.previous_job_name = ua->argv[i];
1064                kw_ok = true;
1065                break;
1066             case 22: /* pool */
1067                if (rc.pool_name && !rc.mod) {
1068                   ua->send_msg(_("Pool specified twice.\n"));
1069                   return false;
1070                }
1071                rc.pool_name = ua->argv[i];
1072                kw_ok = true;
1073                break;
1074             case 23: /* backupclient */
1075                if (rc.client_name && !rc.mod) {
1076                   ua->send_msg(_("Client specified twice.\n"));
1077                   return 0;
1078                }
1079                rc.client_name = ua->argv[i];
1080                kw_ok = true;
1081                break;
1082             case 24: /* restoreclient */
1083                if (rc.restore_client_name && !rc.mod) {
1084                   ua->send_msg(_("Restore Client specified twice.\n"));
1085                   return false;
1086                }
1087                rc.restore_client_name = ua->argv[i];
1088                kw_ok = true;
1089                break;
1090             default:
1091                break;
1092             }
1093          } /* end strcase compare */
1094       } /* end keyword loop */
1095       /*
1096        * End of keyword for loop -- if not found, we got a bogus keyword
1097        */
1098       if (!kw_ok) {
1099          Dmsg1(800, "%s not found\n", ua->argk[i]);
1100          /*
1101           * Special case for Job Name, it can be the first
1102           * keyword that has no value.
1103           */
1104          if (!rc.job_name && !ua->argv[i]) {
1105             rc.job_name = ua->argk[i];   /* use keyword as job name */
1106             Dmsg1(800, "Set jobname=%s\n", rc.job_name);
1107          } else {
1108             ua->send_msg(_("Invalid keyword: %s\n"), ua->argk[i]);
1109             return false;
1110          }
1111       }
1112    } /* end argc loop */
1113              
1114    Dmsg0(800, "Done scan.\n");
1115
1116    if (rc.catalog_name != NULL) {
1117        rc.catalog = GetCatalogResWithName(rc.catalog_name);
1118        if (rc.catalog == NULL) {
1119             ua->error_msg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
1120            return false;
1121        }
1122        if (!acl_access_ok(ua, Catalog_ACL, rc.catalog->name())) {
1123           ua->error_msg(_("No authorization. Catalog \"%s\".\n"), rc.catalog->name());
1124           return false;
1125        }
1126    }
1127    Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
1128
1129    if (rc.job_name) {
1130       /* Find Job */
1131       rc.job = GetJobResWithName(rc.job_name);
1132       if (!rc.job) {
1133          if (*rc.job_name != 0) {
1134             ua->send_msg(_("Job \"%s\" not found\n"), rc.job_name);
1135          }
1136          rc.job = select_job_resource(ua);
1137       } else {
1138          Dmsg1(800, "Found job=%s\n", rc.job_name);
1139       }
1140    } else {
1141       ua->send_msg(_("A job name must be specified.\n"));
1142       rc.job = select_job_resource(ua);
1143    }
1144    if (!rc.job) {
1145       return false;
1146    } else if (!acl_access_ok(ua, Job_ACL, rc.job->name())) {
1147       ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name());
1148       return false;
1149    }
1150
1151    if (rc.pool_name) {
1152       rc.pool = GetPoolResWithName(rc.pool_name);
1153       if (!rc.pool) {
1154          if (*rc.pool_name != 0) {
1155             ua->warning_msg(_("Pool \"%s\" not found.\n"), rc.pool_name);
1156          }
1157          rc.pool = select_pool_resource(ua);
1158       }
1159    } else {
1160       rc.pool = rc.job->pool;             /* use default */
1161    }
1162    if (!rc.pool) {
1163       return false;
1164    } else if (!acl_access_ok(ua, Pool_ACL, rc.pool->name())) {
1165       ua->error_msg(_("No authorization. Pool \"%s\".\n"), rc.pool->name());
1166       return false;
1167    }
1168    Dmsg1(100, "Using pool %s\n", rc.pool->name());
1169
1170    if (rc.store_name) {
1171       rc.store->store = GetStoreResWithName(rc.store_name);
1172       pm_strcpy(rc.store->store_source, _("command line"));
1173       if (!rc.store->store) {
1174          if (*rc.store_name != 0) {
1175             ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
1176          }
1177          rc.store->store = select_storage_resource(ua);
1178          pm_strcpy(rc.store->store_source, _("user selection"));
1179       }
1180    } else {
1181       get_job_storage(rc.store, rc.job, NULL);      /* use default */
1182    }
1183    if (!rc.store->store) {
1184       ua->error_msg(_("No storage specified.\n"));
1185       return true;
1186    } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
1187       ua->error_msg(_("No authorization. Storage \"%s\".\n"),
1188                rc.store->store->name());
1189       return false;
1190    }
1191    Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
1192
1193    if (rc.client_name) {
1194       rc.client = GetClientResWithName(rc.client_name);
1195       if (!rc.client) {
1196          if (*rc.client_name != 0) {
1197             ua->warning_msg(_("Client \"%s\" not found.\n"), rc.client_name);
1198          }
1199          rc.client = select_client_resource(ua);
1200       }
1201    } else {
1202       rc.client = rc.job->client;           /* use default */
1203    }
1204    if (!rc.client) {
1205       return false;
1206    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
1207       ua->error_msg(_("No authorization. Client \"%s\".\n"),
1208                rc.client->name());
1209       return false;
1210    }
1211    Dmsg1(800, "Using client=%s\n", rc.client->name());
1212
1213    if (rc.restore_client_name) {
1214       rc.client = GetClientResWithName(rc.restore_client_name);
1215       if (!rc.client) {
1216          if (*rc.restore_client_name != 0) {
1217             ua->warning_msg(_("Restore Client \"%s\" not found.\n"), rc.restore_client_name);
1218          }
1219          rc.client = select_client_resource(ua);
1220       }
1221    } else {
1222       rc.client = rc.job->client;           /* use default */
1223    }
1224    if (!rc.client) {
1225       return false;
1226    } else if (!acl_access_ok(ua, Client_ACL, rc.client->name())) {
1227       ua->error_msg(_("No authorization. Client \"%s\".\n"),
1228                rc.client->name());
1229       return false;
1230    }
1231    Dmsg1(800, "Using restore client=%s\n", rc.client->name());
1232
1233
1234    if (rc.fileset_name) {
1235       rc.fileset = GetFileSetResWithName(rc.fileset_name);
1236       if (!rc.fileset) {
1237          ua->send_msg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
1238          rc.fileset = select_fileset_resource(ua);
1239       }
1240    } else {
1241       rc.fileset = rc.job->fileset;           /* use default */
1242    }
1243    if (!rc.fileset) {
1244       return false;
1245    } else if (!acl_access_ok(ua, FileSet_ACL, rc.fileset->name())) {
1246       ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
1247                rc.fileset->name());
1248       return false;
1249    }
1250
1251    if (rc.verify_job_name) {
1252       rc.verify_job = GetJobResWithName(rc.verify_job_name);
1253       if (!rc.verify_job) {
1254          ua->send_msg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
1255          rc.verify_job = select_job_resource(ua);
1256       }
1257    } else {
1258       rc.verify_job = rc.job->verify_job;
1259    }
1260
1261    if (rc.previous_job_name) {
1262       rc.previous_job = GetJobResWithName(rc.previous_job_name);
1263       if (!rc.previous_job) {
1264          ua->send_msg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
1265          rc.previous_job = select_job_resource(ua);
1266       }
1267    } else {
1268       rc.previous_job = rc.job->verify_job;
1269    }
1270    return true;
1271 }