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