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