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