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