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