1 /* BAREOS® - Backup Archiving REcovery Open Sourced
2 
3    Copyright (C) 2001-2012 Free Software Foundation Europe e.V.
4    Copyright (C) 2011-2016 Planets Communications B.V.
5    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Kern Sibbald, December MMI
24  */
25 /**
26  * @file
27  * BAREOS Director -- Run Command
28  */
29 #include "include/bareos.h"
30 #include "dird.h"
31 #include "dird/jcr_private.h"
32 #include "dird/job.h"
33 #include "dird/migration.h"
34 #include "dird/storage.h"
35 #include "dird/ua_db.h"
36 #include "dird/ua_input.h"
37 #include "dird/ua_select.h"
38 #include "dird/ua_run.h"
39 #include "lib/breg.h"
40 #include "lib/berrno.h"
41 #include "lib/edit.h"
42 #include "lib/keyword_table_s.h"
43 
44 namespace directordaemon {
45 
46 /* Forward referenced subroutines */
47 static void SelectJobLevel(UaContext* ua, JobControlRecord* jcr);
48 static bool DisplayJobParameters(UaContext* ua,
49                                  JobControlRecord* jcr,
50                                  RunContext& rc);
51 static void SelectWhereRegexp(UaContext* ua, JobControlRecord* jcr);
52 static bool ScanCommandLineArguments(UaContext* ua, RunContext& rc);
53 static bool ResetRestoreContext(UaContext* ua,
54                                 JobControlRecord* jcr,
55                                 RunContext& rc);
56 static int ModifyJobParameters(UaContext* ua,
57                                JobControlRecord* jcr,
58                                RunContext& rc);
59 
60 /* Imported variables */
61 extern struct s_kw ReplaceOptions[];
62 
63 /**
64  * Rerun a job by jobid. Lookup the job data and rerun the job with that data.
65  *
66  * Returns: false on error
67  *          true if OK
68  */
reRunJob(UaContext * ua,JobId_t JobId,bool yes,utime_t now)69 static inline bool reRunJob(UaContext* ua, JobId_t JobId, bool yes, utime_t now)
70 {
71   JobDbRecord jr;
72   char dt[MAX_TIME_LENGTH];
73   PoolMem cmdline(PM_MESSAGE);
74 
75   jr.JobId = JobId;
76   ua->SendMsg("rerunning jobid %d\n", jr.JobId);
77   if (!ua->db->GetJobRecord(ua->jcr, &jr)) {
78     Jmsg(ua->jcr, M_WARNING, 0,
79          _("Error getting Job record for Job rerun: ERR=%s"),
80          ua->db->strerror());
81     goto bail_out;
82   }
83 
84   /*
85    * Only perform rerun on JobTypes where it makes sense.
86    */
87   switch (jr.JobType) {
88     case JT_BACKUP:
89     case JT_COPY:
90     case JT_MIGRATE:
91       break;
92     default:
93       return true;
94   }
95 
96   if (jr.JobLevel == L_NONE) {
97     Mmsg(cmdline, "run job=\"%s\"", jr.Name);
98   } else {
99     Mmsg(cmdline, "run job=\"%s\" level=\"%s\"", jr.Name,
100          JobLevelToString(jr.JobLevel));
101   }
102   PmStrcpy(ua->cmd, cmdline);
103 
104   if (jr.ClientId) {
105     ClientDbRecord cr;
106 
107     cr.ClientId = jr.ClientId;
108     if (!ua->db->GetClientRecord(ua->jcr, &cr)) {
109       Jmsg(ua->jcr, M_WARNING, 0,
110            _("Error getting Client record for Job rerun: ERR=%s"),
111            ua->db->strerror());
112       goto bail_out;
113     }
114     Mmsg(cmdline, " client=\"%s\"", cr.Name);
115     PmStrcat(ua->cmd, cmdline);
116   }
117 
118   if (jr.PoolId) {
119     PoolDbRecord pr;
120 
121     pr.PoolId = jr.PoolId;
122     if (!ua->db->GetPoolRecord(ua->jcr, &pr)) {
123       Jmsg(ua->jcr, M_WARNING, 0,
124            _("Error getting Pool record for Job rerun: ERR=%s"),
125            ua->db->strerror());
126       goto bail_out;
127     }
128 
129     /*
130      * Source pool.
131      */
132     switch (jr.JobType) {
133       case JT_COPY:
134       case JT_MIGRATE: {
135         JobResource* job = NULL;
136 
137         job = ua->GetJobResWithName(jr.Name, false);
138         if (job) {
139           Mmsg(cmdline, " pool=\"%s\"", job->pool->resource_name_);
140           PmStrcat(ua->cmd, cmdline);
141         }
142         break;
143       }
144       case JT_BACKUP:
145         switch (jr.JobLevel) {
146           case L_VIRTUAL_FULL: {
147             JobResource* job = NULL;
148 
149             job = ua->GetJobResWithName(jr.Name, false);
150             if (job) {
151               Mmsg(cmdline, " pool=\"%s\"", job->pool->resource_name_);
152               PmStrcat(ua->cmd, cmdline);
153             }
154             break;
155           }
156           default:
157             Mmsg(cmdline, " pool=\"%s\"", pr.Name);
158             PmStrcat(ua->cmd, cmdline);
159             break;
160         }
161     }
162 
163     /*
164      * Next pool.
165      */
166     switch (jr.JobType) {
167       case JT_COPY:
168       case JT_MIGRATE:
169         Mmsg(cmdline, " nextpool=\"%s\"", pr.Name);
170         PmStrcat(ua->cmd, cmdline);
171         break;
172       case JT_BACKUP:
173         switch (jr.JobLevel) {
174           case L_VIRTUAL_FULL:
175             Mmsg(cmdline, " nextpool=\"%s\"", pr.Name);
176             PmStrcat(ua->cmd, cmdline);
177 
178             break;
179           default:
180             break;
181         }
182         break;
183     }
184   }
185 
186   if (jr.FileSetId) {
187     FileSetDbRecord fs;
188 
189     fs.FileSetId = jr.FileSetId;
190     if (!ua->db->GetFilesetRecord(ua->jcr, &fs)) {
191       Jmsg(ua->jcr, M_WARNING, 0,
192            _("Error getting FileSet record for Job rerun: ERR=%s"),
193            ua->db->strerror());
194       goto bail_out;
195     }
196     Mmsg(cmdline, " fileset=\"%s\"", fs.FileSet);
197     PmStrcat(ua->cmd, cmdline);
198   }
199 
200   bstrutime(dt, sizeof(dt), now);
201   Mmsg(cmdline, " when=\"%s\"", dt);
202   PmStrcat(ua->cmd, cmdline);
203 
204   if (yes) { PmStrcat(ua->cmd, " yes"); }
205 
206   Dmsg1(100, "rerun cmdline=%s\n", ua->cmd);
207 
208   ParseUaArgs(ua);
209   return RunCmd(ua, ua->cmd);
210 
211 bail_out:
212   return false;
213 }
214 
215 /**
216  * Rerun a job selection.
217  *
218  * Returns: 0 on error
219  *          1 if OK
220  */
reRunCmd(UaContext * ua,const char * cmd)221 bool reRunCmd(UaContext* ua, const char* cmd)
222 {
223   int i, j, d, h, s, u;
224   int days = 0;
225   int hours = 0;
226   int since_jobid = 0;
227   int until_jobid = 0;
228   JobId_t JobId;
229   dbid_list ids;
230   PoolMem query(PM_MESSAGE);
231   utime_t now;
232   time_t schedtime;
233   char dt[MAX_TIME_LENGTH];
234   char ed1[50];
235   char ed2[50];
236   bool yes = false;       /* Was "yes" given on cmdline*/
237   bool timeframe = false; /* Should the selection happen based on timeframe? */
238   bool since_jobid_given = false; /* Was since_jobid given? */
239   bool until_jobid_given = false; /* Was until_jobid given? */
240   const int secs_in_day = 86400;
241   const int secs_in_hour = 3600;
242 
243   if (!OpenClientDb(ua)) { return true; }
244 
245   now = (utime_t)time(NULL);
246 
247   /*
248    * Determine what cmdline arguments are given.
249    */
250   j = FindArgWithValue(ua, NT_("jobid"));
251   d = FindArgWithValue(ua, NT_("days"));
252   h = FindArgWithValue(ua, NT_("hours"));
253   s = FindArgWithValue(ua, NT_("since_jobid"));
254   u = FindArgWithValue(ua, NT_("until_jobid"));
255 
256   if (s > 0) {
257     since_jobid = str_to_int64(ua->argv[s]);
258     since_jobid_given = true;
259   }
260 
261   if (u > 0) {
262     until_jobid = str_to_int64(ua->argv[u]);
263     until_jobid_given = true;
264   }
265 
266   if (d > 0 || h > 0) { timeframe = true; }
267 
268   if (FindArg(ua, NT_("yes")) > 0) { yes = true; }
269 
270   if (j < 0 && !timeframe && !since_jobid_given) {
271     ua->SendMsg("Please specify jobid, since_jobid, hours or days\n");
272     goto bail_out;
273   }
274 
275   if (j >= 0 && since_jobid_given) {
276     ua->SendMsg(
277         "Please specify either jobid or since_jobid (and optionally "
278         "until_jobid)\n");
279     goto bail_out;
280   }
281 
282   if (j >= 0 && timeframe) {
283     ua->SendMsg("Please specify either jobid or timeframe\n");
284     goto bail_out;
285   }
286 
287   if (timeframe || since_jobid_given) {
288     schedtime = now;
289     if (d > 0) {
290       days = str_to_int64(ua->argv[d]);
291       schedtime = now - secs_in_day * days; /* Days in the past */
292     }
293     if (h > 0) {
294       hours = str_to_int64(ua->argv[h]);
295       schedtime = now - secs_in_hour * hours; /* Hours in the past */
296     }
297 
298     /*
299      * Job Query Start
300      */
301     bstrutime(dt, sizeof(dt), schedtime);
302 
303     if (since_jobid_given) {
304       if (until_jobid_given) {
305         Mmsg(query,
306              "SELECT JobId FROM Job WHERE JobStatus = 'f' AND JobId >= %s AND "
307              "JobId <= %s ORDER BY JobId",
308              edit_int64(since_jobid, ed1), edit_int64(until_jobid, ed2));
309       } else {
310         Mmsg(query,
311              "SELECT JobId FROM Job WHERE JobStatus = 'f' AND JobId >= %s "
312              "ORDER BY JobId",
313              edit_int64(since_jobid, ed1));
314       }
315 
316     } else {
317       Mmsg(query,
318            "SELECT JobId FROM Job WHERE JobStatus = 'f' AND SchedTime > '%s' "
319            "ORDER BY JobId",
320            dt);
321     }
322 
323     ua->db->GetQueryDbids(ua->jcr, query, ids);
324 
325     ua->SendMsg("The following ids were selected for rerun:\n");
326     for (i = 0; i < ids.num_ids; i++) {
327       if (i > 0) {
328         ua->SendMsg(",%d", ids.DBId[i]);
329       } else {
330         ua->SendMsg("%d", ids.DBId[i]);
331       }
332     }
333     ua->SendMsg("\n");
334 
335     if (!yes && (!GetYesno(ua, _("rerun these jobids? (yes/no): ")) ||
336                  !ua->pint32_val)) {
337       goto bail_out;
338     }
339     /*
340      * Job Query End
341      */
342 
343     /*
344      * Loop over all selected JobIds.
345      */
346     for (i = 0; i < ids.num_ids; i++) {
347       JobId = ids.DBId[i];
348       if (!reRunJob(ua, JobId, yes, now)) { goto bail_out; }
349     }
350   } else {
351     JobId = str_to_int64(ua->argv[j]);
352     if (!reRunJob(ua, JobId, yes, now)) { goto bail_out; }
353   }
354 
355   return true;
356 
357 bail_out:
358   return false;
359 }
360 
361 /**
362  * For Backup and Verify Jobs
363  *     run [job=]<job-name> level=<level-name>
364  *
365  * For Restore Jobs
366  *     run <job-name>
367  *
368  * Returns: 0 on error
369  *          JobId if OK
370  *
371  */
DoRunCmd(UaContext * ua,const char * cmd)372 int DoRunCmd(UaContext* ua, const char* cmd)
373 {
374   JobControlRecord* jcr = NULL;
375   RunContext rc;
376   int status, length;
377   bool valid_response;
378   bool do_pool_overrides = true;
379 
380   if (!OpenClientDb(ua)) { return 0; }
381 
382   if (!ScanCommandLineArguments(ua, rc)) { return 0; }
383 
384   if (FindArg(ua, NT_("fdcalled")) > 0) {
385     jcr->file_bsock = ua->UA_sock->clone();
386     ua->quit = true;
387   }
388 
389   /*
390    * Create JobControlRecord to run job.  NOTE!!! after this point, FreeJcr()
391    * before returning.
392    */
393   if (!jcr) {
394     jcr = NewDirectorJcr();
395     SetJcrDefaults(jcr, rc.job);
396     jcr->impl->unlink_bsr =
397         ua->jcr->impl->unlink_bsr; /* copy unlink flag from caller */
398     ua->jcr->impl->unlink_bsr = false;
399   }
400 
401   /*
402    * Transfer JobIds to new restore Job
403    */
404   if (ua->jcr->JobIds) {
405     jcr->JobIds = ua->jcr->JobIds;
406     ua->jcr->JobIds = NULL;
407   }
408 
409   /*
410    * Transfer selected restore tree to new restore Job
411    */
412   if (ua->jcr->impl->restore_tree_root) {
413     jcr->impl->restore_tree_root = ua->jcr->impl->restore_tree_root;
414     ua->jcr->impl->restore_tree_root = NULL;
415   }
416 
417 try_again:
418   if (!ResetRestoreContext(ua, jcr, rc)) { goto bail_out; }
419 
420   /*
421    * Run without prompting?
422    */
423   if (ua->batch || FindArg(ua, NT_("yes")) > 0) { goto start_job; }
424 
425   /*
426    * When doing interactive runs perform the pool level overrides
427    * early this way the user doesn't get nasty surprisses when
428    * a level override changes the pool the data will be saved to
429    * later. We only want to do these overrides once so we use
430    * a tracking boolean do_pool_overrides to see if we still
431    * need to do this (e.g. we pass by here multiple times when
432    * the user interactivly changes options.
433    */
434   if (do_pool_overrides) {
435     switch (jcr->getJobType()) {
436       case JT_BACKUP:
437         if (!jcr->is_JobLevel(L_VIRTUAL_FULL)) { ApplyPoolOverrides(jcr); }
438         break;
439       default:
440         break;
441     }
442     do_pool_overrides = false;
443   }
444 
445   /*
446    * Prompt User to see if all run job parameters are correct, and
447    * allow him to modify them.
448    */
449   if (!DisplayJobParameters(ua, jcr, rc)) { goto bail_out; }
450 
451   /*
452    * Prompt User until we have a valid response.
453    */
454   do {
455     if (!GetCmd(ua, _("OK to run? (yes/mod/no): "))) { goto bail_out; }
456 
457     /*
458      * Empty line equals yes, anything other we compare
459      * the cmdline for the length of the given input unless
460      * its mod or .mod where we compare only the keyword
461      * and a space as it can be followed by a full cmdline
462      * with new cmdline arguments that need to be parsed.
463      */
464     valid_response = false;
465     length = strlen(ua->cmd);
466     if (ua->cmd[0] == 0 || bstrncasecmp(ua->cmd, ".mod ", MIN(length, 5)) ||
467         bstrncasecmp(ua->cmd, "mod ", MIN(length, 4)) ||
468         bstrncasecmp(ua->cmd, NT_("yes"), length) ||
469         bstrncasecmp(ua->cmd, _("yes"), length) ||
470         bstrncasecmp(ua->cmd, NT_("no"), length) ||
471         bstrncasecmp(ua->cmd, _("no"), length)) {
472       valid_response = true;
473     }
474 
475     if (!valid_response) {
476       ua->WarningMsg(_("Illegal response %s\n"), ua->cmd);
477     }
478   } while (!valid_response);
479 
480   /*
481    * See if the .mod or mod has arguments.
482    */
483   if (bstrncasecmp(ua->cmd, ".mod ", 5) ||
484       (bstrncasecmp(ua->cmd, "mod ", 4) && strlen(ua->cmd) > 6)) {
485     ParseUaArgs(ua);
486     rc.mod = true;
487     if (!ScanCommandLineArguments(ua, rc)) { return 0; }
488     goto try_again;
489   }
490 
491   /*
492    * Allow the user to modify the settings
493    */
494   status = ModifyJobParameters(ua, jcr, rc);
495   switch (status) {
496     case 0:
497       goto try_again;
498     case 1:
499       break;
500     case -1:
501       goto bail_out;
502   }
503 
504   /*
505    * For interactive runs we set IgnoreLevelPoolOverrides as we already
506    * performed the actual overrrides.
507    */
508   jcr->impl->IgnoreLevelPoolOverides = true;
509 
510   if (ua->cmd[0] == 0 || bstrncasecmp(ua->cmd, NT_("yes"), strlen(ua->cmd)) ||
511       bstrncasecmp(ua->cmd, _("yes"), strlen(ua->cmd))) {
512     JobId_t JobId;
513     Dmsg1(800, "Calling RunJob job=%x\n", jcr->impl->res.job);
514 
515   start_job:
516     Dmsg3(100, "JobId=%u using pool %s priority=%d\n", (int)jcr->JobId,
517           jcr->impl->res.pool->resource_name_, jcr->JobPriority);
518     Dmsg1(900, "Running a job; its spool_data = %d\n", jcr->impl->spool_data);
519 
520     JobId = RunJob(jcr);
521 
522     Dmsg4(100, "JobId=%u NewJobId=%d using pool %s priority=%d\n",
523           (int)jcr->JobId, JobId, jcr->impl->res.pool->resource_name_,
524           jcr->JobPriority);
525 
526     FreeJcr(jcr); /* release jcr */
527 
528     if (JobId == 0) {
529       ua->ErrorMsg(_("Job failed.\n"));
530     } else {
531       char ed1[50];
532       ua->send->ObjectStart("run");
533       ua->send->ObjectKeyValue("jobid", edit_int64(JobId, ed1),
534                                _("Job queued. JobId=%s\n"));
535       ua->send->ObjectEnd("run");
536     }
537 
538     return JobId;
539   }
540 
541 bail_out:
542   ua->SendMsg(_("Job not run.\n"));
543   FreeJcr(jcr);
544 
545   return 0; /* do not run */
546 }
547 
RunCmd(UaContext * ua,const char * cmd)548 bool RunCmd(UaContext* ua, const char* cmd)
549 {
550   return (DoRunCmd(ua, ua->cmd) != 0);
551 }
552 
ModifyJobParameters(UaContext * ua,JobControlRecord * jcr,RunContext & rc)553 int ModifyJobParameters(UaContext* ua, JobControlRecord* jcr, RunContext& rc)
554 {
555   int opt;
556 
557   /*
558    * At user request modify parameters of job to be run.
559    */
560   if (ua->cmd[0] != 0 && bstrncasecmp(ua->cmd, _("mod"), strlen(ua->cmd))) {
561     StartPrompt(ua, _("Parameters to modify:\n"));
562 
563     AddPrompt(ua, _("Level"));   /* 0 */
564     AddPrompt(ua, _("Storage")); /* 1 */
565     AddPrompt(ua, _("Job"));     /* 2 */
566     AddPrompt(ua, _("FileSet")); /* 3 */
567     if (jcr->is_JobType(JT_RESTORE)) {
568       AddPrompt(ua, _("Restore Client")); /* 4 */
569     } else {
570       AddPrompt(ua, _("Client")); /* 4 */
571     }
572     AddPrompt(ua, _("Backup Format")); /* 5 */
573     AddPrompt(ua, _("When"));          /* 6 */
574     AddPrompt(ua, _("Priority"));      /* 7 */
575     if (jcr->is_JobType(JT_BACKUP) || jcr->is_JobType(JT_COPY) ||
576         jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_VERIFY)) {
577       AddPrompt(ua, _("Pool")); /* 8 */
578       if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
579           (jcr->is_JobType(JT_BACKUP) &&
580            jcr->is_JobLevel(L_VIRTUAL_FULL))) { /* NextPool */
581         AddPrompt(ua, _("NextPool"));           /* 9 */
582         if (jcr->is_JobType(JT_BACKUP)) {
583           AddPrompt(ua, _("Plugin Options")); /* 10 */
584         }
585       } else if (jcr->is_JobType(JT_VERIFY)) {
586         AddPrompt(ua, _("Verify Job")); /* 9 */
587       } else if (jcr->is_JobType(JT_BACKUP)) {
588         AddPrompt(ua, _("Plugin Options")); /* 9 */
589       }
590     } else if (jcr->is_JobType(JT_RESTORE)) {
591       AddPrompt(ua, _("Bootstrap"));       /* 8 */
592       AddPrompt(ua, _("Where"));           /* 9 */
593       AddPrompt(ua, _("File Relocation")); /* 10 */
594       AddPrompt(ua, _("Replace"));         /* 11 */
595       AddPrompt(ua, _("JobId"));           /* 12 */
596       AddPrompt(ua, _("Plugin Options"));  /* 13 */
597     }
598 
599     switch (DoPrompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
600       case 0:
601         /* Level */
602         SelectJobLevel(ua, jcr);
603         switch (jcr->getJobType()) {
604           case JT_BACKUP:
605             if (!rc.pool_override && !jcr->is_JobLevel(L_VIRTUAL_FULL)) {
606               ApplyPoolOverrides(jcr, true);
607               rc.pool = jcr->impl->res.pool;
608               rc.level_override = true;
609             }
610             break;
611           default:
612             break;
613         }
614         goto try_again;
615       case 1:
616         /* Storage */
617         rc.store->store = select_storage_resource(ua);
618         if (rc.store->store) {
619           PmStrcpy(rc.store->store_source, _("user selection"));
620           SetRwstorage(jcr, rc.store);
621           goto try_again;
622         }
623         break;
624       case 2:
625         /* Job */
626         rc.job = select_job_resource(ua);
627         if (rc.job) {
628           jcr->impl->res.job = rc.job;
629           SetJcrDefaults(jcr, rc.job);
630           goto try_again;
631         }
632         break;
633       case 3:
634         /* FileSet */
635         rc.fileset = select_fileset_resource(ua);
636         if (rc.fileset) {
637           jcr->impl->res.fileset = rc.fileset;
638           goto try_again;
639         }
640         break;
641       case 4:
642         /* Client */
643         rc.client = select_client_resource(ua);
644         if (rc.client) {
645           jcr->impl->res.client = rc.client;
646           goto try_again;
647         }
648         break;
649       case 5:
650         /* Backup Format */
651         if (GetCmd(ua, _("Please enter Backup Format: "))) {
652           if (jcr->impl->backup_format) {
653             free(jcr->impl->backup_format);
654             jcr->impl->backup_format = NULL;
655           }
656           jcr->impl->backup_format = strdup(ua->cmd);
657           goto try_again;
658         }
659         break;
660       case 6:
661         /* When */
662         if (GetCmd(ua, _("Please enter desired start time as YYYY-MM-DD "
663                          "HH:MM:SS (return for now): "))) {
664           if (ua->cmd[0] == 0) {
665             jcr->sched_time = time(NULL);
666           } else {
667             jcr->sched_time = StrToUtime(ua->cmd);
668             if (jcr->sched_time == 0) {
669               ua->SendMsg(_("Invalid time, using current time.\n"));
670               jcr->sched_time = time(NULL);
671             }
672           }
673           goto try_again;
674         }
675         break;
676       case 7:
677         /* Priority */
678         if (GetPint(ua, _("Enter new Priority: "))) {
679           if (!ua->pint32_val) {
680             ua->SendMsg(_("Priority must be a positive integer.\n"));
681           } else {
682             jcr->JobPriority = ua->pint32_val;
683           }
684           goto try_again;
685         }
686         break;
687       case 8:
688         /* Pool or Bootstrap depending on JobType */
689         if (jcr->is_JobType(JT_BACKUP) || jcr->is_JobType(JT_COPY) ||
690             jcr->is_JobType(JT_MIGRATE) ||
691             jcr->is_JobType(JT_VERIFY)) { /* Pool */
692           rc.pool = select_pool_resource(ua);
693           if (rc.pool) {
694             jcr->impl->res.pool = rc.pool;
695             rc.level_override = false;
696             rc.pool_override = true;
697             Dmsg1(100, "Set new pool=%s\n",
698                   jcr->impl->res.pool->resource_name_);
699             goto try_again;
700           }
701         } else {
702           /* Bootstrap */
703           if (GetCmd(ua, _("Please enter the Bootstrap file name: "))) {
704             if (jcr->RestoreBootstrap) {
705               free(jcr->RestoreBootstrap);
706               jcr->RestoreBootstrap = NULL;
707             }
708             if (ua->cmd[0] != 0) {
709               FILE* fd;
710 
711               jcr->RestoreBootstrap = strdup(ua->cmd);
712               fd = fopen(jcr->RestoreBootstrap, "rb");
713               if (!fd) {
714                 BErrNo be;
715                 ua->SendMsg(_("Warning cannot open %s: ERR=%s\n"),
716                             jcr->RestoreBootstrap, be.bstrerror());
717                 free(jcr->RestoreBootstrap);
718                 jcr->RestoreBootstrap = NULL;
719               } else {
720                 fclose(fd);
721               }
722             }
723             goto try_again;
724           }
725         }
726         break;
727       case 9:
728         /* NextPool/Verify Job/Where/Plugin Options depending on JobType */
729         if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
730             (jcr->is_JobType(JT_BACKUP) &&
731              jcr->is_JobLevel(L_VIRTUAL_FULL))) { /* NextPool */
732           rc.next_pool = select_pool_resource(ua);
733           if (rc.next_pool) {
734             jcr->impl->res.next_pool = rc.next_pool;
735             Dmsg1(100, "Set new next_pool=%s\n",
736                   jcr->impl->res.next_pool->resource_name_);
737             goto try_again;
738           }
739         } else if (jcr->is_JobType(JT_VERIFY)) { /* Verify Job */
740           rc.verify_job = select_job_resource(ua);
741           if (rc.verify_job) { jcr->impl->res.verify_job = rc.verify_job; }
742           goto try_again;
743         } else if (jcr->is_JobType(JT_RESTORE)) { /* Where */
744           if (GetCmd(ua, _("Please enter the full path prefix for restore (/ "
745                            "for none): "))) {
746             if (jcr->RegexWhere) { /* cannot use regexwhere and where */
747               free(jcr->RegexWhere);
748               jcr->RegexWhere = NULL;
749             }
750             if (jcr->where) {
751               free(jcr->where);
752               jcr->where = NULL;
753             }
754             if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
755               ua->cmd[0] = 0;
756             }
757             jcr->where = strdup(ua->cmd);
758             goto try_again;
759           }
760         } else { /* Plugin Options */
761           if (GetCmd(ua, _("Please enter Plugin Options string: "))) {
762             if (jcr->impl->plugin_options) {
763               free(jcr->impl->plugin_options);
764               jcr->impl->plugin_options = NULL;
765             }
766             jcr->impl->plugin_options = strdup(ua->cmd);
767             goto try_again;
768           }
769         }
770         break;
771       case 10:
772         /* File relocation/Plugin Options depending on JobType */
773         if (jcr->is_JobType(JT_RESTORE)) {
774           SelectWhereRegexp(ua, jcr);
775           goto try_again;
776         } else if (jcr->is_JobType(JT_BACKUP)) {
777           if (GetCmd(ua, _("Please enter Plugin Options string: "))) {
778             if (jcr->impl->plugin_options) {
779               free(jcr->impl->plugin_options);
780               jcr->impl->plugin_options = NULL;
781             }
782             jcr->impl->plugin_options = strdup(ua->cmd);
783             goto try_again;
784           }
785         }
786         break;
787       case 11:
788         /* Replace */
789         StartPrompt(ua, _("Replace:\n"));
790         for (int i = 0; ReplaceOptions[i].name; i++) {
791           AddPrompt(ua, ReplaceOptions[i].name);
792         }
793         opt = DoPrompt(ua, "", _("Select replace option"), NULL, 0);
794         if (opt >= 0) {
795           rc.replace = ReplaceOptions[opt].name;
796           jcr->impl->replace = ReplaceOptions[opt].token;
797         }
798         goto try_again;
799       case 12:
800         /* JobId */
801         rc.jid = NULL; /* force reprompt */
802         jcr->impl->RestoreJobId = 0;
803         if (jcr->RestoreBootstrap) {
804           ua->SendMsg(
805               _("You must set the bootstrap file to NULL to be able to specify "
806                 "a JobId.\n"));
807         }
808         goto try_again;
809       case 13:
810         /* Plugin Options */
811         if (GetCmd(ua, _("Please enter Plugin Options string: "))) {
812           if (jcr->impl->plugin_options) {
813             free(jcr->impl->plugin_options);
814             jcr->impl->plugin_options = NULL;
815           }
816           jcr->impl->plugin_options = strdup(ua->cmd);
817           goto try_again;
818         }
819         break;
820       case -1: /* error or cancel */
821         goto bail_out;
822       default:
823         goto try_again;
824     }
825     goto bail_out;
826   }
827   return 1;
828 
829 bail_out:
830   return -1;
831 
832 try_again:
833   return 0;
834 }
835 
836 /**
837  * Reset the restore context.
838  * This subroutine can be called multiple times, so it must keep any prior
839  * settings.
840  */
ResetRestoreContext(UaContext * ua,JobControlRecord * jcr,RunContext & rc)841 static bool ResetRestoreContext(UaContext* ua,
842                                 JobControlRecord* jcr,
843                                 RunContext& rc)
844 {
845   jcr->impl->res.verify_job = rc.verify_job;
846   jcr->impl->res.previous_job = rc.previous_job;
847   jcr->impl->res.pool = rc.pool;
848   jcr->impl->res.next_pool = rc.next_pool;
849 
850   /*
851    * See if an explicit pool override was performed.
852    * If so set the pool_source to command line and
853    * set the IgnoreLevelPoolOverides so any level Pool
854    * overrides are ignored.
855    */
856   if (rc.pool_name) {
857     PmStrcpy(jcr->impl->res.pool_source, _("command line"));
858     jcr->impl->IgnoreLevelPoolOverides = true;
859   } else if (!rc.level_override &&
860              jcr->impl->res.pool != jcr->impl->res.job->pool) {
861     PmStrcpy(jcr->impl->res.pool_source, _("user input"));
862   }
863   SetRwstorage(jcr, rc.store);
864 
865   if (rc.next_pool_name) {
866     PmStrcpy(jcr->impl->res.npool_source, _("command line"));
867     jcr->impl->res.run_next_pool_override = true;
868   } else if (jcr->impl->res.next_pool != jcr->impl->res.pool->NextPool) {
869     PmStrcpy(jcr->impl->res.npool_source, _("user input"));
870     jcr->impl->res.run_next_pool_override = true;
871   }
872 
873   jcr->impl->res.client = rc.client;
874   if (jcr->impl->res.client) {
875     PmStrcpy(jcr->client_name, rc.client->resource_name_);
876   }
877   jcr->impl->res.fileset = rc.fileset;
878   jcr->impl->ExpectedFiles = rc.files;
879 
880   if (rc.catalog) {
881     jcr->impl->res.catalog = rc.catalog;
882     PmStrcpy(jcr->impl->res.catalog_source, _("user input"));
883   }
884 
885   PmStrcpy(jcr->comment, rc.comment);
886 
887   if (rc.where) {
888     if (jcr->where) { free(jcr->where); }
889     jcr->where = strdup(rc.where);
890     rc.where = NULL;
891   }
892 
893   if (rc.regexwhere) {
894     if (jcr->RegexWhere) { free(jcr->RegexWhere); }
895     jcr->RegexWhere = strdup(rc.regexwhere);
896     rc.regexwhere = NULL;
897   }
898 
899   if (rc.when) {
900     jcr->sched_time = StrToUtime(rc.when);
901     if (jcr->sched_time == 0) {
902       ua->SendMsg(_("Invalid time, using current time.\n"));
903       jcr->sched_time = time(NULL);
904     }
905     rc.when = NULL;
906   }
907 
908   if (rc.bootstrap) {
909     if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); }
910     jcr->RestoreBootstrap = strdup(rc.bootstrap);
911     rc.bootstrap = NULL;
912   }
913 
914   if (rc.plugin_options) {
915     if (jcr->impl->plugin_options) { free(jcr->impl->plugin_options); }
916     jcr->impl->plugin_options = strdup(rc.plugin_options);
917     rc.plugin_options = NULL;
918   }
919 
920   if (rc.replace) {
921     jcr->impl->replace = 0;
922     for (int i = 0; ReplaceOptions[i].name; i++) {
923       if (Bstrcasecmp(rc.replace, ReplaceOptions[i].name)) {
924         jcr->impl->replace = ReplaceOptions[i].token;
925       }
926     }
927     if (!jcr->impl->replace) {
928       ua->SendMsg(_("Invalid replace option: %s\n"), rc.replace);
929       return false;
930     }
931   } else if (rc.job->replace) {
932     jcr->impl->replace = rc.job->replace;
933   } else {
934     jcr->impl->replace = REPLACE_ALWAYS;
935   }
936   rc.replace = NULL;
937 
938   if (rc.Priority) {
939     jcr->JobPriority = rc.Priority;
940     rc.Priority = 0;
941   }
942 
943   if (rc.since) {
944     if (!jcr->stime) { jcr->stime = GetPoolMemory(PM_MESSAGE); }
945     PmStrcpy(jcr->stime, rc.since);
946     rc.since = NULL;
947   }
948 
949   if (rc.cloned) {
950     jcr->impl->cloned = rc.cloned;
951     rc.cloned = false;
952   }
953 
954   /*
955    * If pool changed, update migration write storage
956    */
957   if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
958       (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) {
959     if (!SetMigrationWstorage(jcr, rc.pool, rc.next_pool,
960                               _("Storage from Run NextPool override"))) {
961       return false;
962     }
963   }
964   rc.replace = ReplaceOptions[0].name;
965   for (int i = 0; ReplaceOptions[i].name; i++) {
966     if (ReplaceOptions[i].token == jcr->impl->replace) {
967       rc.replace = ReplaceOptions[i].name;
968     }
969   }
970 
971   if (rc.level_name) {
972     if (!GetLevelFromName(jcr, rc.level_name)) {
973       ua->SendMsg(_("Level \"%s\" not valid.\n"), rc.level_name);
974       return false;
975     }
976     rc.level_name = NULL;
977   }
978 
979   if (rc.jid) {
980     if (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL)) {
981       if (!jcr->impl->vf_jobids) {
982         jcr->impl->vf_jobids = GetPoolMemory(PM_MESSAGE);
983       }
984       PmStrcpy(jcr->impl->vf_jobids, rc.jid);
985     } else {
986       /*
987        * Note, this is also MigrateJobId and a VerifyJobId
988        */
989       jcr->impl->RestoreJobId = str_to_int64(rc.jid);
990     }
991     rc.jid = NULL;
992   }
993 
994   if (rc.backup_format) {
995     if (jcr->impl->backup_format) { free(jcr->impl->backup_format); }
996     jcr->impl->backup_format = strdup(rc.backup_format);
997     rc.backup_format = NULL;
998   }
999 
1000   /*
1001    * Some options are not available through the menu
1002    * TODO: Add an advanced menu?
1003    */
1004   if (rc.spool_data_set) { jcr->impl->spool_data = rc.spool_data; }
1005 
1006   if (rc.accurate_set) { jcr->accurate = rc.accurate; }
1007 
1008   /*
1009    * Used by migration jobs that can have the same name,
1010    * but can run at the same time
1011    */
1012   if (rc.ignoreduplicatecheck_set) {
1013     jcr->impl->IgnoreDuplicateJobChecking = rc.ignoreduplicatecheck;
1014   }
1015 
1016   return true;
1017 }
1018 
SelectWhereRegexp(UaContext * ua,JobControlRecord * jcr)1019 static void SelectWhereRegexp(UaContext* ua, JobControlRecord* jcr)
1020 {
1021   alist* regs;
1022   char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
1023   strip_prefix = add_suffix = rwhere = add_prefix = NULL;
1024 
1025 try_again_reg:
1026   ua->SendMsg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
1027               NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
1028 
1029   StartPrompt(ua, _("This will replace your current Where value\n"));
1030   AddPrompt(ua, _("Strip prefix"));               /* 0 */
1031   AddPrompt(ua, _("Add prefix"));                 /* 1 */
1032   AddPrompt(ua, _("Add file suffix"));            /* 2 */
1033   AddPrompt(ua, _("Enter a regexp"));             /* 3 */
1034   AddPrompt(ua, _("Test filename manipulation")); /* 4 */
1035   AddPrompt(ua, _("Use this ?"));                 /* 5 */
1036 
1037   switch (DoPrompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
1038     case 0:
1039       /* Strip prefix */
1040       if (GetCmd(ua, _("Please enter the path prefix to strip: "))) {
1041         if (strip_prefix) free(strip_prefix);
1042         strip_prefix = strdup(ua->cmd);
1043       }
1044       goto try_again_reg;
1045     case 1:
1046       /* Add prefix */
1047       if (GetCmd(ua, _("Please enter the path prefix to add (/ for none): "))) {
1048         if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
1049           ua->cmd[0] = 0;
1050         }
1051 
1052         if (add_prefix) free(add_prefix);
1053         add_prefix = strdup(ua->cmd);
1054       }
1055       goto try_again_reg;
1056     case 2:
1057       /* Add suffix */
1058       if (GetCmd(ua, _("Please enter the file suffix to add: "))) {
1059         if (add_suffix) free(add_suffix);
1060         add_suffix = strdup(ua->cmd);
1061       }
1062       goto try_again_reg;
1063     case 3:
1064       /* Add rwhere */
1065       if (GetCmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
1066         if (rwhere) free(rwhere);
1067         rwhere = strdup(ua->cmd);
1068       }
1069       goto try_again_reg;
1070     case 4:
1071       /* Test regexp */
1072       char* result;
1073       char* regexp;
1074 
1075       if (rwhere && rwhere[0] != '\0') {
1076         regs = get_bregexps(rwhere);
1077         ua->SendMsg(_("regexwhere=%s\n"), NPRT(rwhere));
1078       } else {
1079         int len =
1080             BregexpGetBuildWhereSize(strip_prefix, add_prefix, add_suffix);
1081         regexp = (char*)malloc(len * sizeof(char));
1082         bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
1083         regs = get_bregexps(regexp);
1084         ua->SendMsg(
1085             _("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
1086             NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix),
1087             NPRT(regexp));
1088 
1089         free(regexp);
1090       }
1091 
1092       if (!regs) {
1093         ua->SendMsg(_("Cannot use your regexp\n"));
1094         goto try_again_reg;
1095       }
1096       ua->SendMsg(_("Enter a period (.) to stop this test\n"));
1097       while (GetCmd(ua, _("Please enter filename to test: "))) {
1098         ApplyBregexps(ua->cmd, regs, &result);
1099         ua->SendMsg(_("%s -> %s\n"), ua->cmd, result);
1100       }
1101       FreeBregexps(regs);
1102       delete regs;
1103       goto try_again_reg;
1104     case 5:
1105       /* OK */
1106       break;
1107     case -1: /* error or cancel */
1108       goto bail_out_reg;
1109     default:
1110       goto try_again_reg;
1111   }
1112 
1113   /* replace the existing where */
1114   if (jcr->where) {
1115     free(jcr->where);
1116     jcr->where = NULL;
1117   }
1118 
1119   /* replace the existing regexwhere */
1120   if (jcr->RegexWhere) {
1121     free(jcr->RegexWhere);
1122     jcr->RegexWhere = NULL;
1123   }
1124 
1125   if (rwhere) {
1126     jcr->RegexWhere = strdup(rwhere);
1127   } else if (strip_prefix || add_prefix || add_suffix) {
1128     int len = BregexpGetBuildWhereSize(strip_prefix, add_prefix, add_suffix);
1129     jcr->RegexWhere = (char*)malloc(len * sizeof(char));
1130     bregexp_build_where(jcr->RegexWhere, len, strip_prefix, add_prefix,
1131                         add_suffix);
1132   }
1133 
1134   regs = get_bregexps(jcr->RegexWhere);
1135   if (regs) {
1136     FreeBregexps(regs);
1137     delete regs;
1138   } else {
1139     if (jcr->RegexWhere) {
1140       free(jcr->RegexWhere);
1141       jcr->RegexWhere = NULL;
1142     }
1143     ua->SendMsg(_("Cannot use your regexp.\n"));
1144   }
1145 
1146 bail_out_reg:
1147   if (strip_prefix) { free(strip_prefix); }
1148   if (add_prefix) { free(add_prefix); }
1149   if (add_suffix) { free(add_suffix); }
1150   if (rwhere) { free(rwhere); }
1151 }
1152 
SelectJobLevel(UaContext * ua,JobControlRecord * jcr)1153 static void SelectJobLevel(UaContext* ua, JobControlRecord* jcr)
1154 {
1155   if (jcr->is_JobType(JT_BACKUP)) {
1156     StartPrompt(ua, _("Levels:\n"));
1157     //    AddPrompt(ua, _("Base"));
1158     AddPrompt(ua, _("Full"));
1159     AddPrompt(ua, _("Incremental"));
1160     AddPrompt(ua, _("Differential"));
1161     AddPrompt(ua, _("Since"));
1162     AddPrompt(ua, _("VirtualFull"));
1163     switch (DoPrompt(ua, "", _("Select level"), NULL, 0)) {
1164         //    case 0:
1165         //       jcr->JobLevel = L_BASE;
1166         //       break;
1167       case 0:
1168         jcr->setJobLevel(L_FULL);
1169         break;
1170       case 1:
1171         jcr->setJobLevel(L_INCREMENTAL);
1172         break;
1173       case 2:
1174         jcr->setJobLevel(L_DIFFERENTIAL);
1175         break;
1176       case 3:
1177         jcr->setJobLevel(L_SINCE);
1178         break;
1179       case 4:
1180         jcr->setJobLevel(L_VIRTUAL_FULL);
1181         break;
1182       default:
1183         break;
1184     }
1185   } else if (jcr->is_JobType(JT_VERIFY)) {
1186     StartPrompt(ua, _("Levels:\n"));
1187     AddPrompt(ua, _("Initialize Catalog"));
1188     AddPrompt(ua, _("Verify Catalog"));
1189     AddPrompt(ua, _("Verify Volume to Catalog"));
1190     AddPrompt(ua, _("Verify Disk to Catalog"));
1191     AddPrompt(ua, _("Verify Volume Data (not yet implemented)"));
1192     switch (DoPrompt(ua, "", _("Select level"), NULL, 0)) {
1193       case 0:
1194         jcr->setJobLevel(L_VERIFY_INIT);
1195         break;
1196       case 1:
1197         jcr->setJobLevel(L_VERIFY_CATALOG);
1198         break;
1199       case 2:
1200         jcr->setJobLevel(L_VERIFY_VOLUME_TO_CATALOG);
1201         break;
1202       case 3:
1203         jcr->setJobLevel(L_VERIFY_DISK_TO_CATALOG);
1204         break;
1205       case 4:
1206         jcr->setJobLevel(L_VERIFY_DATA);
1207         break;
1208       default:
1209         break;
1210     }
1211   } else {
1212     ua->WarningMsg(
1213         _("Level not appropriate for this Job. Cannot be changed.\n"));
1214   }
1215   return;
1216 }
1217 
DisplayJobParameters(UaContext * ua,JobControlRecord * jcr,RunContext & rc)1218 static bool DisplayJobParameters(UaContext* ua,
1219                                  JobControlRecord* jcr,
1220                                  RunContext& rc)
1221 {
1222   char ec1[30];
1223   JobResource* job = rc.job;
1224   char dt[MAX_TIME_LENGTH];
1225   const char* verify_list = rc.verify_list;
1226 
1227   Dmsg1(800, "JobType=%c\n", jcr->getJobType());
1228   switch (jcr->getJobType()) {
1229     case JT_ADMIN:
1230       if (ua->api) {
1231         ua->signal(BNET_RUN_CMD);
1232         ua->SendMsg(
1233             "Type: Admin\n"
1234             "Title: Run Admin Job\n"
1235             "JobName:  %s\n"
1236             "FileSet:  %s\n"
1237             "Client:   %s\n"
1238             "Storage:  %s\n"
1239             "When:     %s\n"
1240             "Priority: %d\n",
1241             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1242             NPRT(jcr->impl->res.client->resource_name_),
1243             jcr->impl->res.write_storage
1244                 ? jcr->impl->res.write_storage->resource_name_
1245                 : _("*None*"),
1246             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1247       } else {
1248         ua->SendMsg(
1249             _("Run Admin Job\n"
1250               "JobName:  %s\n"
1251               "FileSet:  %s\n"
1252               "Client:   %s\n"
1253               "Storage:  %s\n"
1254               "When:     %s\n"
1255               "Priority: %d\n"),
1256             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1257             NPRT(jcr->impl->res.client->resource_name_),
1258             jcr->impl->res.write_storage
1259                 ? jcr->impl->res.write_storage->resource_name_
1260                 : _("*None*"),
1261             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1262       }
1263       jcr->setJobLevel(L_FULL);
1264       break;
1265     case JT_ARCHIVE:
1266       if (ua->api) {
1267         ua->signal(BNET_RUN_CMD);
1268         ua->SendMsg(
1269             "Type: Archive\n"
1270             "Title: Run Archive Job\n"
1271             "JobName:  %s\n"
1272             "FileSet:  %s\n"
1273             "Client:   %s\n"
1274             "Storage:  %s\n"
1275             "When:     %s\n"
1276             "Priority: %d\n",
1277             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1278             NPRT(jcr->impl->res.client->resource_name_),
1279             jcr->impl->res.write_storage
1280                 ? jcr->impl->res.write_storage->resource_name_
1281                 : _("*None*"),
1282             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1283       } else {
1284         ua->SendMsg(
1285             _("Run Archive Job\n"
1286               "JobName:  %s\n"
1287               "FileSet:  %s\n"
1288               "Client:   %s\n"
1289               "Storage:  %s\n"
1290               "When:     %s\n"
1291               "Priority: %d\n"),
1292             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1293             NPRT(jcr->impl->res.client->resource_name_),
1294             jcr->impl->res.write_storage
1295                 ? jcr->impl->res.write_storage->resource_name_
1296                 : _("*None*"),
1297             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1298       }
1299       jcr->setJobLevel(L_FULL);
1300       break;
1301     case JT_CONSOLIDATE:
1302       if (ua->api) {
1303         ua->signal(BNET_RUN_CMD);
1304         ua->SendMsg(
1305             "Type: Consolidate\n"
1306             "Title: Run Consolidate Job\n"
1307             "JobName:  %s\n"
1308             "FileSet:  %s\n"
1309             "Client:   %s\n"
1310             "Storage:  %s\n"
1311             "When:     %s\n"
1312             "Priority: %d\n",
1313             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1314             NPRT(jcr->impl->res.client->resource_name_),
1315             jcr->impl->res.write_storage
1316                 ? jcr->impl->res.write_storage->resource_name_
1317                 : _("*None*"),
1318             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1319       } else {
1320         ua->SendMsg(
1321             _("Run Consolidate Job\n"
1322               "JobName:  %s\n"
1323               "FileSet:  %s\n"
1324               "Client:   %s\n"
1325               "Storage:  %s\n"
1326               "When:     %s\n"
1327               "Priority: %d\n"),
1328             job->resource_name_, jcr->impl->res.fileset->resource_name_,
1329             NPRT(jcr->impl->res.client->resource_name_),
1330             jcr->impl->res.write_storage
1331                 ? jcr->impl->res.write_storage->resource_name_
1332                 : _("*None*"),
1333             bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1334       }
1335       jcr->setJobLevel(L_FULL);
1336       break;
1337     case JT_BACKUP:
1338     case JT_VERIFY:
1339       if (jcr->is_JobType(JT_BACKUP)) {
1340         bool is_virtual = jcr->is_JobLevel(L_VIRTUAL_FULL);
1341 
1342         if (ua->api) {
1343           ua->signal(BNET_RUN_CMD);
1344           ua->SendMsg(
1345               "Type: Backup\n"
1346               "Title: Run Backup Job\n"
1347               "JobName:  %s\n"
1348               "Level:    %s\n"
1349               "Client:   %s\n"
1350               "Format:   %s\n"
1351               "FileSet:  %s\n"
1352               "Pool:     %s\n"
1353               "%s%s%s"
1354               "Storage:  %s\n"
1355               "When:     %s\n"
1356               "Priority: %d\n"
1357               "%s%s%s",
1358               job->resource_name_, JobLevelToString(jcr->getJobLevel()),
1359               jcr->impl->res.client->resource_name_, jcr->impl->backup_format,
1360               jcr->impl->res.fileset->resource_name_,
1361               NPRT(jcr->impl->res.pool->resource_name_),
1362               is_virtual ? "NextPool: " : "",
1363               is_virtual ? (jcr->impl->res.next_pool
1364                                 ? jcr->impl->res.next_pool->resource_name_
1365                                 : _("*None*"))
1366                          : "",
1367               is_virtual ? "\n" : "",
1368               jcr->impl->res.write_storage
1369                   ? jcr->impl->res.write_storage->resource_name_
1370                   : _("*None*"),
1371               bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority,
1372               jcr->impl->plugin_options ? "Plugin Options: " : "",
1373               jcr->impl->plugin_options ? jcr->impl->plugin_options : "",
1374               jcr->impl->plugin_options ? "\n" : "");
1375         } else {
1376           ua->SendMsg(
1377               _("Run Backup job\n"
1378                 "JobName:  %s\n"
1379                 "Level:    %s\n"
1380                 "Client:   %s\n"
1381                 "Format:   %s\n"
1382                 "FileSet:  %s\n"
1383                 "Pool:     %s (From %s)\n"
1384                 "%s%s%s%s%s"
1385                 "Storage:  %s (From %s)\n"
1386                 "When:     %s\n"
1387                 "Priority: %d\n"
1388                 "%s%s%s"),
1389               job->resource_name_, JobLevelToString(jcr->getJobLevel()),
1390               jcr->impl->res.client->resource_name_, jcr->impl->backup_format,
1391               jcr->impl->res.fileset->resource_name_,
1392               NPRT(jcr->impl->res.pool->resource_name_),
1393               jcr->impl->res.pool_source, is_virtual ? "NextPool: " : "",
1394               is_virtual ? (jcr->impl->res.next_pool
1395                                 ? jcr->impl->res.next_pool->resource_name_
1396                                 : _("*None*"))
1397                          : "",
1398               is_virtual ? " (From " : "",
1399               is_virtual ? jcr->impl->res.npool_source : "",
1400               is_virtual ? ")\n" : "",
1401               jcr->impl->res.write_storage
1402                   ? jcr->impl->res.write_storage->resource_name_
1403                   : _("*None*"),
1404               jcr->impl->res.wstore_source,
1405               bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority,
1406               jcr->impl->plugin_options ? "Plugin Options: " : "",
1407               jcr->impl->plugin_options ? jcr->impl->plugin_options : "",
1408               jcr->impl->plugin_options ? "\n" : "");
1409         }
1410       } else { /* JT_VERIFY */
1411         JobDbRecord jr;
1412         const char* Name;
1413         if (jcr->impl->res.verify_job) {
1414           Name = jcr->impl->res.verify_job->resource_name_;
1415         } else if (jcr->impl
1416                        ->RestoreJobId) { /* Display job name if jobid requested
1417                                           */
1418           jr.JobId = jcr->impl->RestoreJobId;
1419           if (!ua->db->GetJobRecord(jcr, &jr)) {
1420             ua->ErrorMsg(
1421                 _("Could not get job record for selected JobId. ERR=%s"),
1422                 ua->db->strerror());
1423             return false;
1424           }
1425           Name = jr.Job;
1426         } else {
1427           Name = "";
1428         }
1429         if (!verify_list) { verify_list = job->WriteVerifyList; }
1430         if (!verify_list) { verify_list = ""; }
1431         if (ua->api) {
1432           ua->signal(BNET_RUN_CMD);
1433           ua->SendMsg(
1434               "Type: Verify\n"
1435               "Title: Run Verify Job\n"
1436               "JobName:     %s\n"
1437               "Level:       %s\n"
1438               "Client:      %s\n"
1439               "FileSet:     %s\n"
1440               "Pool:        %s (From %s)\n"
1441               "Storage:     %s (From %s)\n"
1442               "Verify Job:  %s\n"
1443               "Verify List: %s\n"
1444               "When:        %s\n"
1445               "Priority:    %d\n",
1446               job->resource_name_, JobLevelToString(jcr->getJobLevel()),
1447               jcr->impl->res.client->resource_name_,
1448               jcr->impl->res.fileset->resource_name_,
1449               NPRT(jcr->impl->res.pool->resource_name_),
1450               jcr->impl->res.pool_source,
1451               jcr->impl->res.read_storage->resource_name_,
1452               jcr->impl->res.rstore_source, Name, verify_list,
1453               bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority);
1454         } else {
1455           ua->SendMsg(_("Run Verify Job\n"
1456                         "JobName:     %s\n"
1457                         "Level:       %s\n"
1458                         "Client:      %s\n"
1459                         "FileSet:     %s\n"
1460                         "Pool:        %s (From %s)\n"
1461                         "Storage:     %s (From %s)\n"
1462                         "Verify Job:  %s\n"
1463                         "Verify List: %s\n"
1464                         "When:        %s\n"
1465                         "Priority:    %d\n"),
1466                       job->resource_name_, JobLevelToString(jcr->getJobLevel()),
1467                       jcr->impl->res.client->resource_name_,
1468                       jcr->impl->res.fileset->resource_name_,
1469                       NPRT(jcr->impl->res.pool->resource_name_),
1470                       jcr->impl->res.pool_source,
1471                       jcr->impl->res.read_storage->resource_name_,
1472                       jcr->impl->res.rstore_source, Name, verify_list,
1473                       bstrutime(dt, sizeof(dt), jcr->sched_time),
1474                       jcr->JobPriority);
1475         }
1476       }
1477       break;
1478     case JT_RESTORE:
1479       if (jcr->impl->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
1480         if (rc.jid) {
1481           jcr->impl->RestoreJobId = str_to_int64(rc.jid);
1482         } else {
1483           if (!GetPint(ua, _("Please enter a JobId for restore: "))) {
1484             return false;
1485           }
1486           jcr->impl->RestoreJobId = ua->int64_val;
1487         }
1488       }
1489       jcr->setJobLevel(L_FULL); /* default level */
1490       Dmsg1(800, "JobId to restore=%d\n", jcr->impl->RestoreJobId);
1491       if (jcr->impl->RestoreJobId == 0) {
1492         /* RegexWhere is take before RestoreWhere */
1493         if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
1494           if (ua->api) {
1495             ua->signal(BNET_RUN_CMD);
1496             ua->SendMsg(
1497                 "Type: Restore\n"
1498                 "Title: Run Restore Job\n"
1499                 "JobName:         %s\n"
1500                 "Bootstrap:       %s\n"
1501                 "RegexWhere:      %s\n"
1502                 "Replace:         %s\n"
1503                 "FileSet:         %s\n"
1504                 "Backup Client:   %s\n"
1505                 "Restore Client:  %s\n"
1506                 "Format:          %s\n"
1507                 "Storage:         %s\n"
1508                 "When:            %s\n"
1509                 "Catalog:         %s\n"
1510                 "Priority:        %d\n"
1511                 "Plugin Options:  %s\n",
1512                 job->resource_name_, NPRT(jcr->RestoreBootstrap),
1513                 jcr->RegexWhere ? jcr->RegexWhere : job->RegexWhere, rc.replace,
1514                 jcr->impl->res.fileset->resource_name_, rc.client_name,
1515                 jcr->impl->res.client->resource_name_,
1516                 jcr->impl->backup_format,
1517                 jcr->impl->res.read_storage->resource_name_,
1518                 bstrutime(dt, sizeof(dt), jcr->sched_time),
1519                 jcr->impl->res.catalog->resource_name_, jcr->JobPriority,
1520                 NPRT(jcr->impl->plugin_options));
1521           } else {
1522             ua->SendMsg(_("Run Restore job\n"
1523                           "JobName:         %s\n"
1524                           "Bootstrap:       %s\n"
1525                           "RegexWhere:      %s\n"
1526                           "Replace:         %s\n"
1527                           "FileSet:         %s\n"
1528                           "Backup Client:   %s\n"
1529                           "Restore Client:  %s\n"
1530                           "Format:          %s\n"
1531                           "Storage:         %s\n"
1532                           "When:            %s\n"
1533                           "Catalog:         %s\n"
1534                           "Priority:        %d\n"
1535                           "Plugin Options:  %s\n"),
1536                         job->resource_name_, NPRT(jcr->RestoreBootstrap),
1537                         jcr->RegexWhere ? jcr->RegexWhere : job->RegexWhere,
1538                         rc.replace, jcr->impl->res.fileset->resource_name_,
1539                         rc.client_name, jcr->impl->res.client->resource_name_,
1540                         jcr->impl->backup_format,
1541                         jcr->impl->res.read_storage->resource_name_,
1542                         bstrutime(dt, sizeof(dt), jcr->sched_time),
1543                         jcr->impl->res.catalog->resource_name_,
1544                         jcr->JobPriority, NPRT(jcr->impl->plugin_options));
1545           }
1546         } else {
1547           if (ua->api) {
1548             ua->signal(BNET_RUN_CMD);
1549             ua->SendMsg(
1550                 "Type: Restore\n"
1551                 "Title: Run Restore job\n"
1552                 "JobName:         %s\n"
1553                 "Bootstrap:       %s\n"
1554                 "Where:           %s\n"
1555                 "Replace:         %s\n"
1556                 "FileSet:         %s\n"
1557                 "Backup Client:   %s\n"
1558                 "Restore Client:  %s\n"
1559                 "Format:          %s\n"
1560                 "Storage:         %s\n"
1561                 "When:            %s\n"
1562                 "Catalog:         %s\n"
1563                 "Priority:        %d\n"
1564                 "Plugin Options:  %s\n",
1565                 job->resource_name_, NPRT(jcr->RestoreBootstrap),
1566                 jcr->where ? jcr->where : NPRT(job->RestoreWhere), rc.replace,
1567                 jcr->impl->res.fileset->resource_name_, rc.client_name,
1568                 jcr->impl->res.client->resource_name_,
1569                 jcr->impl->backup_format,
1570                 jcr->impl->res.read_storage->resource_name_,
1571                 bstrutime(dt, sizeof(dt), jcr->sched_time),
1572                 jcr->impl->res.catalog->resource_name_, jcr->JobPriority,
1573                 NPRT(jcr->impl->plugin_options));
1574           } else {
1575             ua->SendMsg(_("Run Restore job\n"
1576                           "JobName:         %s\n"
1577                           "Bootstrap:       %s\n"
1578                           "Where:           %s\n"
1579                           "Replace:         %s\n"
1580                           "FileSet:         %s\n"
1581                           "Backup Client:   %s\n"
1582                           "Restore Client:  %s\n"
1583                           "Format:          %s\n"
1584                           "Storage:         %s\n"
1585                           "When:            %s\n"
1586                           "Catalog:         %s\n"
1587                           "Priority:        %d\n"
1588                           "Plugin Options:  %s\n"),
1589                         job->resource_name_, NPRT(jcr->RestoreBootstrap),
1590                         jcr->where ? jcr->where : NPRT(job->RestoreWhere),
1591                         rc.replace, jcr->impl->res.fileset->resource_name_,
1592                         rc.client_name, jcr->impl->res.client->resource_name_,
1593                         jcr->impl->backup_format,
1594                         jcr->impl->res.read_storage->resource_name_,
1595                         bstrutime(dt, sizeof(dt), jcr->sched_time),
1596                         jcr->impl->res.catalog->resource_name_,
1597                         jcr->JobPriority, NPRT(jcr->impl->plugin_options));
1598           }
1599         }
1600 
1601       } else {
1602         /* ***FIXME*** This needs to be fixed for bat */
1603         if (ua->api) ua->signal(BNET_RUN_CMD);
1604         ua->SendMsg(_("Run Restore job\n"
1605                       "JobName:    %s\n"
1606                       "Bootstrap:  %s\n"),
1607                     job->resource_name_, NPRT(jcr->RestoreBootstrap));
1608 
1609         /* RegexWhere is take before RestoreWhere */
1610         if (jcr->RegexWhere || (job->RegexWhere && !jcr->where)) {
1611           ua->SendMsg(_("RegexWhere: %s\n"),
1612                       jcr->RegexWhere ? jcr->RegexWhere : job->RegexWhere);
1613         } else {
1614           ua->SendMsg(_("Where:      %s\n"),
1615                       jcr->where ? jcr->where : NPRT(job->RestoreWhere));
1616         }
1617 
1618         ua->SendMsg(_("Replace:         %s\n"
1619                       "Client:          %s\n"
1620                       "Format:          %s\n"
1621                       "Storage:         %s\n"
1622                       "JobId:           %s\n"
1623                       "When:            %s\n"
1624                       "Catalog:         %s\n"
1625                       "Priority:        %d\n"
1626                       "Plugin Options:  %s\n"),
1627                     rc.replace, jcr->impl->res.client->resource_name_,
1628                     jcr->impl->backup_format,
1629                     jcr->impl->res.read_storage->resource_name_,
1630                     (jcr->impl->RestoreJobId == 0)
1631                         ? _("*None*")
1632                         : edit_uint64(jcr->impl->RestoreJobId, ec1),
1633                     bstrutime(dt, sizeof(dt), jcr->sched_time),
1634                     jcr->impl->res.catalog->resource_name_, jcr->JobPriority,
1635                     NPRT(jcr->impl->plugin_options));
1636       }
1637       break;
1638     case JT_COPY:
1639     case JT_MIGRATE:
1640       const char* prt_type;
1641 
1642       jcr->setJobLevel(L_FULL); /* default level */
1643       if (ua->api) {
1644         ua->signal(BNET_RUN_CMD);
1645         if (jcr->is_JobType(JT_COPY)) {
1646           prt_type = _("Type: Copy\nTitle: Run Copy Job\n");
1647         } else {
1648           prt_type = _("Type: Migration\nTitle: Run Migration Job\n");
1649         }
1650         ua->SendMsg(
1651             "%s"
1652             "JobName:       %s\n"
1653             "Bootstrap:     %s\n"
1654             "Read Storage:  %s\n"
1655             "Pool:          %s\n"
1656             "Write Storage: %s\n"
1657             "NextPool:      %s\n"
1658             "JobId:         %s\n"
1659             "When:          %s\n"
1660             "Catalog:       %s\n"
1661             "Priority:      %d\n",
1662             prt_type, job->resource_name_, NPRT(jcr->RestoreBootstrap),
1663             jcr->impl->res.read_storage
1664                 ? jcr->impl->res.read_storage->resource_name_
1665                 : _("*None*"),
1666             NPRT(jcr->impl->res.pool->resource_name_),
1667             jcr->impl->res.next_pool
1668                 ? jcr->impl->res.next_pool->resource_name_
1669                 : _("*None*"),
1670             jcr->impl->res.write_storage
1671                 ? jcr->impl->res.write_storage->resource_name_
1672                 : _("*None*"),
1673             (jcr->impl->MigrateJobId == 0)
1674                 ? _("*None*")
1675                 : edit_uint64(jcr->impl->MigrateJobId, ec1),
1676             bstrutime(dt, sizeof(dt), jcr->sched_time),
1677             jcr->impl->res.catalog->resource_name_, jcr->JobPriority);
1678       } else {
1679         if (jcr->is_JobType(JT_COPY)) {
1680           prt_type = _("Run Copy job\n");
1681         } else {
1682           prt_type = _("Run Migration job\n");
1683         }
1684         ua->SendMsg(_("%s"
1685                       "JobName:       %s\n"
1686                       "Bootstrap:     %s\n"
1687                       "Read Storage:  %s (From %s)\n"
1688                       "Pool:          %s (From %s)\n"
1689                       "Write Storage: %s (From %s)\n"
1690                       "NextPool:      %s (From %s)\n"
1691                       "JobId:         %s\n"
1692                       "When:          %s\n"
1693                       "Catalog:       %s\n"
1694                       "Priority:      %d\n"),
1695                     prt_type, job->resource_name_, NPRT(jcr->RestoreBootstrap),
1696                     jcr->impl->res.read_storage
1697                         ? jcr->impl->res.read_storage->resource_name_
1698                         : _("*None*"),
1699                     jcr->impl->res.rstore_source,
1700                     NPRT(jcr->impl->res.pool->resource_name_),
1701                     jcr->impl->res.pool_source,
1702                     jcr->impl->res.write_storage
1703                         ? jcr->impl->res.write_storage->resource_name_
1704                         : _("*None*"),
1705                     jcr->impl->res.wstore_source,
1706                     jcr->impl->res.next_pool
1707                         ? jcr->impl->res.next_pool->resource_name_
1708                         : _("*None*"),
1709                     NPRT(jcr->impl->res.npool_source),
1710                     jcr->impl->MigrateJobId == 0
1711                         ? _("*None*")
1712                         : edit_uint64(jcr->impl->MigrateJobId, ec1),
1713                     bstrutime(dt, sizeof(dt), jcr->sched_time),
1714                     jcr->impl->res.catalog->resource_name_, jcr->JobPriority);
1715       }
1716       break;
1717     default:
1718       ua->ErrorMsg(_("Unknown Job Type=%d\n"), jcr->getJobType());
1719       return false;
1720   }
1721   return true;
1722 }
1723 
ScanCommandLineArguments(UaContext * ua,RunContext & rc)1724 static bool ScanCommandLineArguments(UaContext* ua, RunContext& rc)
1725 {
1726   bool kw_ok;
1727   int i, j;
1728   static const char* kw[] = {
1729       /* command line arguments */
1730       "job",                  /* 0 */
1731       "jobid",                /* 1 */
1732       "client",               /* 2 */
1733       "fd",                   /* 3 */
1734       "fileset",              /* 4 */
1735       "level",                /* 5 */
1736       "storage",              /* 6 */
1737       "sd",                   /* 7 */
1738       "regexwhere",           /* 8 - where string as a bregexp */
1739       "where",                /* 9 */
1740       "bootstrap",            /* 10 */
1741       "replace",              /* 11 */
1742       "when",                 /* 12 */
1743       "priority",             /* 13 */
1744       "yes",                  /* 14 -- if you change this change YES_POS too */
1745       "verifyjob",            /* 15 */
1746       "files",                /* 16 - number of files to restore */
1747       "catalog",              /* 17 - override catalog */
1748       "since",                /* 18 - since */
1749       "cloned",               /* 19 - cloned */
1750       "verifylist",           /* 20 - verify output list */
1751       "migrationjob",         /* 21 - migration job name */
1752       "pool",                 /* 22 */
1753       "nextpool",             /* 23 */
1754       "backupclient",         /* 24 */
1755       "restoreclient",        /* 25 */
1756       "pluginoptions",        /* 26 */
1757       "spooldata",            /* 27 */
1758       "comment",              /* 28 */
1759       "ignoreduplicatecheck", /* 29 */
1760       "accurate",             /* 30 */
1761       "backupformat",         /* 31 */
1762       NULL};
1763 
1764 #define YES_POS 14
1765 
1766   rc.catalog_name = NULL;
1767   rc.job_name = NULL;
1768   rc.pool_name = NULL;
1769   rc.next_pool_name = NULL;
1770   rc.StoreName = NULL;
1771   rc.client_name = NULL;
1772   rc.restore_client_name = NULL;
1773   rc.fileset_name = NULL;
1774   rc.verify_job_name = NULL;
1775   rc.previous_job_name = NULL;
1776   rc.accurate_set = false;
1777   rc.spool_data_set = false;
1778   rc.ignoreduplicatecheck = false;
1779   rc.comment = NULL;
1780   rc.backup_format = NULL;
1781 
1782   for (i = 1; i < ua->argc; i++) {
1783     Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
1784     kw_ok = false;
1785 
1786     /*
1787      * Keep looking until we find a good keyword
1788      */
1789     for (j = 0; !kw_ok && kw[j]; j++) {
1790       if (Bstrcasecmp(ua->argk[i], kw[j])) {
1791         /*
1792          * Note, yes and run have no value, so do not fail
1793          */
1794         if (!ua->argv[i] && j != YES_POS /*yes*/) {
1795           ua->SendMsg(_("Value missing for keyword %s\n"), ua->argk[i]);
1796           return false;
1797         }
1798         Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j]));
1799         switch (j) {
1800           case 0: /* job */
1801             if (rc.job_name) {
1802               ua->SendMsg(_("Job name specified twice.\n"));
1803               return false;
1804             }
1805             rc.job_name = ua->argv[i];
1806             kw_ok = true;
1807             break;
1808           case 1: /* JobId */
1809             if (rc.jid && !rc.mod) {
1810               ua->SendMsg(_("JobId specified twice.\n"));
1811               return false;
1812             }
1813             rc.jid = ua->argv[i];
1814             kw_ok = true;
1815             break;
1816           case 2: /* client */
1817           case 3: /* fd */
1818             if (rc.client_name) {
1819               ua->SendMsg(_("Client specified twice.\n"));
1820               return false;
1821             }
1822             rc.client_name = ua->argv[i];
1823             kw_ok = true;
1824             break;
1825           case 4: /* fileset */
1826             if (rc.fileset_name) {
1827               ua->SendMsg(_("FileSet specified twice.\n"));
1828               return false;
1829             }
1830             rc.fileset_name = ua->argv[i];
1831             kw_ok = true;
1832             break;
1833           case 5: /* level */
1834             if (rc.level_name) {
1835               ua->SendMsg(_("Level specified twice.\n"));
1836               return false;
1837             }
1838             rc.level_name = ua->argv[i];
1839             kw_ok = true;
1840             break;
1841           case 6: /* storage */
1842           case 7: /* sd */
1843             if (rc.StoreName) {
1844               ua->SendMsg(_("Storage specified twice.\n"));
1845               return false;
1846             }
1847             rc.StoreName = ua->argv[i];
1848             kw_ok = true;
1849             break;
1850           case 8: /* regexwhere */
1851             if ((rc.regexwhere || rc.where) && !rc.mod) {
1852               ua->SendMsg(_("RegexWhere or Where specified twice.\n"));
1853               return false;
1854             }
1855             rc.regexwhere = ua->argv[i];
1856             if (!ua->AclAccessOk(Where_ACL, rc.regexwhere, true)) {
1857               ua->SendMsg(
1858                   _("No authorization for \"regexwhere\" specification.\n"));
1859               return false;
1860             }
1861             kw_ok = true;
1862             break;
1863           case 9: /* where */
1864             if ((rc.where || rc.regexwhere) && !rc.mod) {
1865               ua->SendMsg(_("Where or RegexWhere specified twice.\n"));
1866               return false;
1867             }
1868             rc.where = ua->argv[i];
1869             if (!ua->AclAccessOk(Where_ACL, rc.where, true)) {
1870               ua->SendMsg(_("No authoriztion for \"where\" specification.\n"));
1871               return false;
1872             }
1873             kw_ok = true;
1874             break;
1875           case 10: /* bootstrap */
1876             if (rc.bootstrap && !rc.mod) {
1877               ua->SendMsg(_("Bootstrap specified twice.\n"));
1878               return false;
1879             }
1880             rc.bootstrap = ua->argv[i];
1881             kw_ok = true;
1882             break;
1883           case 11: /* replace */
1884             if (rc.replace && !rc.mod) {
1885               ua->SendMsg(_("Replace specified twice.\n"));
1886               return false;
1887             }
1888             rc.replace = ua->argv[i];
1889             kw_ok = true;
1890             break;
1891           case 12: /* When */
1892             if (rc.when && !rc.mod) {
1893               ua->SendMsg(_("When specified twice.\n"));
1894               return false;
1895             }
1896             rc.when = ua->argv[i];
1897             kw_ok = true;
1898             break;
1899           case 13: /* Priority */
1900             if (rc.Priority && !rc.mod) {
1901               ua->SendMsg(_("Priority specified twice.\n"));
1902               return false;
1903             }
1904             rc.Priority = atoi(ua->argv[i]);
1905             if (rc.Priority <= 0) {
1906               ua->SendMsg(
1907                   _("Priority must be positive nonzero setting it to 10.\n"));
1908               rc.Priority = 10;
1909             }
1910             kw_ok = true;
1911             break;
1912           case 14: /* yes */
1913             kw_ok = true;
1914             break;
1915           case 15: /* Verify Job */
1916             if (rc.verify_job_name) {
1917               ua->SendMsg(_("Verify Job specified twice.\n"));
1918               return false;
1919             }
1920             rc.verify_job_name = ua->argv[i];
1921             kw_ok = true;
1922             break;
1923           case 16: /* files */
1924             rc.files = atoi(ua->argv[i]);
1925             kw_ok = true;
1926             break;
1927           case 17: /* catalog */
1928             rc.catalog_name = ua->argv[i];
1929             kw_ok = true;
1930             break;
1931           case 18: /* since */
1932             rc.since = ua->argv[i];
1933             kw_ok = true;
1934             break;
1935           case 19: /* cloned */
1936             rc.cloned = true;
1937             kw_ok = true;
1938             break;
1939           case 20: /* write verify list output */
1940             rc.verify_list = ua->argv[i];
1941             kw_ok = true;
1942             break;
1943           case 21: /* Migration Job */
1944             if (rc.previous_job_name) {
1945               ua->SendMsg(_("Migration Job specified twice.\n"));
1946               return false;
1947             }
1948             rc.previous_job_name = ua->argv[i];
1949             kw_ok = true;
1950             break;
1951           case 22: /* pool */
1952             if (rc.pool_name) {
1953               ua->SendMsg(_("Pool specified twice.\n"));
1954               return false;
1955             }
1956             rc.pool_name = ua->argv[i];
1957             kw_ok = true;
1958             break;
1959           case 23: /* nextpool */
1960             if (rc.next_pool_name) {
1961               ua->SendMsg(_("NextPool specified twice.\n"));
1962               return false;
1963             }
1964             rc.next_pool_name = ua->argv[i];
1965             kw_ok = true;
1966             break;
1967           case 24: /* backupclient */
1968             if (rc.client_name) {
1969               ua->SendMsg(_("Client specified twice.\n"));
1970               return 0;
1971             }
1972             rc.client_name = ua->argv[i];
1973             kw_ok = true;
1974             break;
1975           case 25: /* restoreclient */
1976             if (rc.restore_client_name && !rc.mod) {
1977               ua->SendMsg(_("Restore Client specified twice.\n"));
1978               return false;
1979             }
1980             rc.restore_client_name = ua->argv[i];
1981             kw_ok = true;
1982             break;
1983           case 26: /* pluginoptions */
1984             if (rc.plugin_options) {
1985               ua->SendMsg(_("Plugin Options specified twice.\n"));
1986               return false;
1987             }
1988             rc.plugin_options = ua->argv[i];
1989             if (!ua->AclAccessOk(PluginOptions_ACL, rc.plugin_options, true)) {
1990               ua->SendMsg(
1991                   _("No authorization for \"PluginOptions\" specification.\n"));
1992               return false;
1993             }
1994             kw_ok = true;
1995             break;
1996           case 27: /* spooldata */
1997             if (rc.spool_data_set) {
1998               ua->SendMsg(_("Spool flag specified twice.\n"));
1999               return false;
2000             }
2001             if (IsYesno(ua->argv[i], &rc.spool_data)) {
2002               rc.spool_data_set = true;
2003               kw_ok = true;
2004             } else {
2005               ua->SendMsg(_("Invalid spooldata flag.\n"));
2006             }
2007             break;
2008           case 28: /* comment */
2009             rc.comment = ua->argv[i];
2010             kw_ok = true;
2011             break;
2012           case 29: /* ignoreduplicatecheck */
2013             if (rc.ignoreduplicatecheck_set) {
2014               ua->SendMsg(_("IgnoreDuplicateCheck flag specified twice.\n"));
2015               return false;
2016             }
2017             if (IsYesno(ua->argv[i], &rc.ignoreduplicatecheck)) {
2018               rc.ignoreduplicatecheck_set = true;
2019               kw_ok = true;
2020             } else {
2021               ua->SendMsg(_("Invalid ignoreduplicatecheck flag.\n"));
2022             }
2023             break;
2024           case 30: /* accurate */
2025             if (rc.accurate_set) {
2026               ua->SendMsg(_("Accurate flag specified twice.\n"));
2027               return false;
2028             }
2029             if (IsYesno(ua->argv[i], &rc.accurate)) {
2030               rc.accurate_set = true;
2031               kw_ok = true;
2032             } else {
2033               ua->SendMsg(_("Invalid accurate flag.\n"));
2034             }
2035             break;
2036           case 31: /* backupformat */
2037             if (rc.backup_format && !rc.mod) {
2038               ua->SendMsg(_("Backup Format specified twice.\n"));
2039               return false;
2040             }
2041             rc.backup_format = ua->argv[i];
2042             kw_ok = true;
2043             break;
2044           default:
2045             break;
2046         }
2047       } /* end strcase compare */
2048     }   /* end keyword loop */
2049 
2050     /*
2051      * End of keyword for loop -- if not found, we got a bogus keyword
2052      */
2053     if (!kw_ok) {
2054       Dmsg1(800, "%s not found\n", ua->argk[i]);
2055       /*
2056        * Special case for Job Name, it can be the first
2057        * keyword that has no value.
2058        */
2059       if (!rc.job_name && !ua->argv[i]) {
2060         rc.job_name = ua->argk[i]; /* use keyword as job name */
2061         Dmsg1(800, "Set jobname=%s\n", rc.job_name);
2062       } else {
2063         ua->SendMsg(_("Invalid keyword: %s\n"), ua->argk[i]);
2064         return false;
2065       }
2066     }
2067   } /* end argc loop */
2068 
2069   Dmsg0(800, "Done scan.\n");
2070   if (rc.comment) {
2071     if (!IsCommentLegal(ua, rc.comment)) { return false; }
2072   }
2073   if (rc.catalog_name) {
2074     rc.catalog = ua->GetCatalogResWithName(rc.catalog_name);
2075     if (rc.catalog == NULL) {
2076       ua->ErrorMsg(_("Catalog \"%s\" not found\n"), rc.catalog_name);
2077       return false;
2078     }
2079   }
2080   Dmsg1(800, "Using catalog=%s\n", NPRT(rc.catalog_name));
2081 
2082   if (rc.job_name) {
2083     /* Find Job */
2084     rc.job = ua->GetJobResWithName(rc.job_name);
2085     if (!rc.job) {
2086       if (*rc.job_name != 0) {
2087         ua->SendMsg(_("Job \"%s\" not found\n"), rc.job_name);
2088       }
2089       rc.job = select_job_resource(ua);
2090     } else {
2091       Dmsg1(800, "Found job=%s\n", rc.job_name);
2092     }
2093   } else if (!rc.job) {
2094     ua->SendMsg(_("A job name must be specified.\n"));
2095     rc.job = select_job_resource(ua);
2096   }
2097   if (!rc.job) { return false; }
2098 
2099   if (rc.pool_name) {
2100     rc.pool = ua->GetPoolResWithName(rc.pool_name);
2101     if (!rc.pool) {
2102       if (*rc.pool_name != 0) {
2103         ua->WarningMsg(_("Pool \"%s\" not found.\n"), rc.pool_name);
2104       }
2105       rc.pool = select_pool_resource(ua);
2106     }
2107   } else if (!rc.pool) {
2108     rc.pool = rc.job->pool; /* use default */
2109   }
2110   if (!rc.pool) { return false; }
2111   Dmsg1(100, "Using pool %s\n", rc.pool->resource_name_);
2112 
2113   if (rc.next_pool_name) {
2114     rc.next_pool = ua->GetPoolResWithName(rc.next_pool_name);
2115     if (!rc.next_pool) {
2116       if (*rc.next_pool_name != 0) {
2117         ua->WarningMsg(_("Pool \"%s\" not found.\n"), rc.next_pool_name);
2118       }
2119       rc.next_pool = select_pool_resource(ua);
2120     }
2121   } else if (!rc.next_pool) {
2122     rc.next_pool = rc.pool->NextPool; /* use default */
2123   }
2124   if (rc.next_pool) {
2125     Dmsg1(100, "Using next pool %s\n", rc.next_pool->resource_name_);
2126   }
2127 
2128   if (rc.StoreName) {
2129     rc.store->store = ua->GetStoreResWithName(rc.StoreName);
2130     PmStrcpy(rc.store->store_source, _("command line"));
2131     if (!rc.store->store) {
2132       if (*rc.StoreName != 0) {
2133         ua->WarningMsg(_("Storage \"%s\" not found.\n"), rc.StoreName);
2134       }
2135       rc.store->store = select_storage_resource(ua);
2136       PmStrcpy(rc.store->store_source, _("user selection"));
2137     }
2138   } else if (!rc.store->store) {
2139     GetJobStorage(rc.store, rc.job, NULL); /* use default */
2140   }
2141 
2142   /*
2143    * For certain Jobs an explicit setting of the read storage is not
2144    * required as its determined when the Job is executed automatically.
2145    */
2146   switch (rc.job->JobType) {
2147     case JT_COPY:
2148     case JT_MIGRATE:
2149       break;
2150     default:
2151       if (!rc.store->store) {
2152         ua->ErrorMsg(_("No storage specified.\n"));
2153         return false;
2154       } else if (!ua->AclAccessOk(Storage_ACL, rc.store->store->resource_name_,
2155                                   true)) {
2156         ua->ErrorMsg(_("No authorization. Storage \"%s\".\n"),
2157                      rc.store->store->resource_name_);
2158         return false;
2159       }
2160       Dmsg1(800, "Using storage=%s\n", rc.store->store->resource_name_);
2161       break;
2162   }
2163 
2164   if (rc.client_name) {
2165     rc.client = ua->GetClientResWithName(rc.client_name);
2166     if (!rc.client) {
2167       if (*rc.client_name != 0) {
2168         ua->WarningMsg(_("Client \"%s\" not found.\n"), rc.client_name);
2169       }
2170       rc.client = select_client_resource(ua);
2171     }
2172   } else if (!rc.client) {
2173     rc.client = rc.job->client; /* use default */
2174   }
2175 
2176   if (rc.client) {
2177     if (!ua->AclAccessOk(Client_ACL, rc.client->resource_name_, true)) {
2178       ua->ErrorMsg(_("No authorization. Client \"%s\".\n"),
2179                    rc.client->resource_name_);
2180       return false;
2181     } else {
2182       Dmsg1(800, "Using client=%s\n", rc.client->resource_name_);
2183     }
2184   }
2185 
2186   if (rc.restore_client_name) {
2187     rc.client = ua->GetClientResWithName(rc.restore_client_name);
2188     if (!rc.client) {
2189       if (*rc.restore_client_name != 0) {
2190         ua->WarningMsg(_("Restore Client \"%s\" not found.\n"),
2191                        rc.restore_client_name);
2192       }
2193       rc.client = select_client_resource(ua);
2194     }
2195   } else if (!rc.client) {
2196     rc.client = rc.job->client; /* use default */
2197   }
2198 
2199   if (rc.client) {
2200     if (!ua->AclAccessOk(Client_ACL, rc.client->resource_name_, true)) {
2201       ua->ErrorMsg(_("No authorization. Client \"%s\".\n"),
2202                    rc.client->resource_name_);
2203       return false;
2204     } else {
2205       Dmsg1(800, "Using restore client=%s\n", rc.client->resource_name_);
2206     }
2207   }
2208 
2209   if (rc.fileset_name) {
2210     rc.fileset = ua->GetFileSetResWithName(rc.fileset_name);
2211     if (!rc.fileset) {
2212       ua->SendMsg(_("FileSet \"%s\" not found.\n"), rc.fileset_name);
2213       rc.fileset = select_fileset_resource(ua);
2214       if (!rc.fileset) { return false; }
2215     }
2216   } else if (!rc.fileset) {
2217     rc.fileset = rc.job->fileset; /* use default */
2218   }
2219 
2220   if (rc.verify_job_name) {
2221     rc.verify_job = ua->GetJobResWithName(rc.verify_job_name);
2222     if (!rc.verify_job) {
2223       ua->SendMsg(_("Verify Job \"%s\" not found.\n"), rc.verify_job_name);
2224       rc.verify_job = select_job_resource(ua);
2225     }
2226   } else if (!rc.verify_job) {
2227     rc.verify_job = rc.job->verify_job;
2228   }
2229 
2230   if (rc.previous_job_name) {
2231     rc.previous_job = ua->GetJobResWithName(rc.previous_job_name);
2232     if (!rc.previous_job) {
2233       ua->SendMsg(_("Migration Job \"%s\" not found.\n"), rc.previous_job_name);
2234       rc.previous_job = select_job_resource(ua);
2235     }
2236   } else {
2237     rc.previous_job = rc.job->verify_job;
2238   }
2239   return true;
2240 }
2241 } /* namespace directordaemon */
2242