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