3 * Bacula Director -- Run Command
5 * Kern Sibbald, December MMI
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
40 /* Forward referenced subroutines */
41 static void select_job_level(UAContext *ua, JCR *jcr);
42 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list,
43 char *jid, const char *replace);
46 /* Imported variables */
47 extern struct s_kw ReplaceOptions[];
50 * For Backup and Verify Jobs
51 * run [job=]<job-name> level=<level-name>
60 int run_cmd(UAContext *ua, const char *cmd)
63 char *job_name, *level_name, *jid, *store_name, *pool_name;
64 char *where, *fileset_name, *client_name, *bootstrap;
66 char *when, *verify_job_name, *catalog_name;
67 char *previous_job_name;
72 int i, j, opt, files = 0;
75 JOB *verify_job = NULL;
76 JOB *previous_job = NULL;
78 CLIENT *client = NULL;
79 FILESET *fileset = NULL;
81 static const char *kw[] = { /* command line arguments */
82 "job", /* Used in a switch() */
96 "yes", /* 14 -- if you change this change YES_POS too */
98 "files", /* 16 number of files to restore */
99 "catalog", /* 17 override catalog */
100 "since", /* 18 since */
101 "cloned", /* 19 cloned */
102 "verifylist", /* 20 verify output list */
103 "migrationjob", /* 21 migration job name */
123 verify_job_name = NULL;
124 previous_job_name = NULL;
128 for (i=1; i<ua->argc; i++) {
129 Dmsg2(800, "Doing arg %d = %s\n", i, ua->argk[i]);
131 /* Keep looking until we find a good keyword */
132 for (j=0; !kw_ok && kw[j]; j++) {
133 if (strcasecmp(ua->argk[i], kw[j]) == 0) {
134 /* Note, yes and run have no value, so do not fail */
135 if (!ua->argv[i] && j != YES_POS /*yes*/) {
136 bsendmsg(ua, _("Value missing for keyword %s\n"), ua->argk[i]);
139 Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j]));
143 bsendmsg(ua, _("Job name specified twice.\n"));
146 job_name = ua->argv[i];
151 bsendmsg(ua, _("JobId specified twice.\n"));
160 bsendmsg(ua, _("Client specified twice.\n"));
163 client_name = ua->argv[i];
166 case 4: /* fileset */
168 bsendmsg(ua, _("FileSet specified twice.\n"));
171 fileset_name = ua->argv[i];
176 bsendmsg(ua, _("Level specified twice.\n"));
179 level_name = ua->argv[i];
182 case 6: /* storage */
185 bsendmsg(ua, _("Storage specified twice.\n"));
188 store_name = ua->argv[i];
193 bsendmsg(ua, _("Pool specified twice.\n"));
196 pool_name = ua->argv[i];
201 bsendmsg(ua, _("Where specified twice.\n"));
205 if (!acl_access_ok(ua, Where_ACL, where)) {
206 bsendmsg(ua, _("Forbidden \"where\" specified.\n"));
211 case 10: /* bootstrap */
213 bsendmsg(ua, _("Bootstrap specified twice.\n"));
216 bootstrap = ua->argv[i];
219 case 11: /* replace */
221 bsendmsg(ua, _("Replace specified twice.\n"));
224 replace = ua->argv[i];
229 bsendmsg(ua, _("When specified twice.\n"));
235 case 13: /* Priority */
237 bsendmsg(ua, _("Priority specified twice.\n"));
240 Priority = atoi(ua->argv[i]);
242 bsendmsg(ua, _("Priority must be positive nonzero setting it to 10.\n"));
250 case 15: /* Verify Job */
251 if (verify_job_name) {
252 bsendmsg(ua, _("Verify Job specified twice.\n"));
255 verify_job_name = ua->argv[i];
259 files = atoi(ua->argv[i]);
263 case 17: /* catalog */
264 catalog_name = ua->argv[i];
273 case 19: /* cloned */
278 case 20: /* write verify list output */
279 verify_list = ua->argv[i];
282 case 21: /* Migration Job */
283 if (previous_job_name) {
284 bsendmsg(ua, _("Migration Job specified twice.\n"));
287 previous_job_name = ua->argv[i];
295 } /* end strcase compare */
296 } /* end keyword loop */
298 * End of keyword for loop -- if not found, we got a bogus keyword
301 Dmsg1(800, "%s not found\n", ua->argk[i]);
303 * Special case for Job Name, it can be the first
304 * keyword that has no value.
306 if (!job_name && !ua->argv[i]) {
307 job_name = ua->argk[i]; /* use keyword as job name */
308 Dmsg1(800, "Set jobname=%s\n", job_name);
310 bsendmsg(ua, _("Invalid keyword: %s\n"), ua->argk[i]);
314 } /* end argc loop */
316 Dmsg0(800, "Done scan.\n");
319 if (catalog_name != NULL) {
320 catalog = (CAT *)GetResWithName(R_CATALOG, catalog_name);
321 if (catalog == NULL) {
322 bsendmsg(ua, _("Catalog \"%s\" not found\n"), catalog_name);
326 Dmsg1(800, "Using catalog=%s\n", NPRT(catalog_name));
330 job = (JOB *)GetResWithName(R_JOB, job_name);
332 if (*job_name != 0) {
333 bsendmsg(ua, _("Job \"%s\" not found\n"), job_name);
335 job = select_job_resource(ua);
337 Dmsg1(800, "Found job=%s\n", job_name);
340 bsendmsg(ua, _("A job name must be specified.\n"));
341 job = select_job_resource(ua);
345 } else if (!acl_access_ok(ua, Job_ACL, job->name())) {
346 bsendmsg(ua, _("No authorization. Job \"%s\".\n"),
352 pool = (POOL *)GetResWithName(R_POOL, pool_name);
354 if (*pool_name != 0) {
355 bsendmsg(ua, _("Pool \"%s\" not found.\n"), pool_name);
357 pool = select_pool_resource(ua);
360 pool = job->pool; /* use default */
364 } else if (!acl_access_ok(ua, Pool_ACL, pool->name())) {
365 bsendmsg(ua, _("No authorization. Pool \"%s\".\n"),
369 Dmsg1(100, "Using pool %s\n", pool->name());
372 store.store = (STORE *)GetResWithName(R_STORAGE, store_name);
373 pm_strcpy(store.store_source, _("command line"));
375 if (*store_name != 0) {
376 bsendmsg(ua, _("Storage \"%s\" not found.\n"), store_name);
378 store.store = select_storage_resource(ua);
379 pm_strcpy(store.store_source, _("user selection"));
382 get_job_storage(&store, job, NULL); /* use default */
385 bsendmsg(ua, _("No storage specified.\n"));
387 } else if (!acl_access_ok(ua, Storage_ACL, store.store->name())) {
388 bsendmsg(ua, _("No authorization. Storage \"%s\".\n"),
389 store.store->name());
392 Dmsg1(800, "Using storage=%s\n", store.store->name());
395 client = (CLIENT *)GetResWithName(R_CLIENT, client_name);
397 if (*client_name != 0) {
398 bsendmsg(ua, _("Client \"%s\" not found.\n"), client_name);
400 client = select_client_resource(ua);
403 client = job->client; /* use default */
407 } else if (!acl_access_ok(ua, Client_ACL, client->name())) {
408 bsendmsg(ua, _("No authorization. Client \"%s\".\n"),
412 Dmsg1(800, "Using client=%s\n", client->name());
415 fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
417 bsendmsg(ua, _("FileSet \"%s\" not found.\n"), fileset_name);
418 fileset = select_fileset_resource(ua);
421 fileset = job->fileset; /* use default */
425 } else if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
426 bsendmsg(ua, _("No authorization. FileSet \"%s\".\n"),
431 if (verify_job_name) {
432 verify_job = (JOB *)GetResWithName(R_JOB, verify_job_name);
434 bsendmsg(ua, _("Verify Job \"%s\" not found.\n"), verify_job_name);
435 verify_job = select_job_resource(ua);
438 verify_job = job->verify_job;
441 if (previous_job_name) {
442 previous_job = (JOB *)GetResWithName(R_JOB, previous_job_name);
444 bsendmsg(ua, _("Migration Job \"%s\" not found.\n"), previous_job_name);
445 previous_job = select_job_resource(ua);
448 previous_job = job->verify_job;
453 * Create JCR to run job. NOTE!!! after this point, free_jcr()
456 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
457 set_jcr_defaults(jcr, job);
458 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
459 ua->jcr->unlink_bsr = false;
461 jcr->verify_job = verify_job;
462 jcr->previous_job = previous_job;
464 set_rwstorage(jcr, &store);
465 jcr->client = client;
466 jcr->fileset = fileset;
467 jcr->ExpectedFiles = files;
468 if (catalog != NULL) {
469 jcr->catalog = catalog;
475 jcr->where = bstrdup(where);
479 jcr->sched_time = str_to_utime(when);
480 if (jcr->sched_time == 0) {
481 bsendmsg(ua, _("Invalid time, using current time.\n"));
482 jcr->sched_time = time(NULL);
487 if (jcr->RestoreBootstrap) {
488 free(jcr->RestoreBootstrap);
490 jcr->RestoreBootstrap = bstrdup(bootstrap);
495 for (i=0; ReplaceOptions[i].name; i++) {
496 if (strcasecmp(replace, ReplaceOptions[i].name) == 0) {
497 jcr->replace = ReplaceOptions[i].token;
501 bsendmsg(ua, _("Invalid replace option: %s\n"), replace);
504 } else if (job->replace) {
505 jcr->replace = job->replace;
507 jcr->replace = REPLACE_ALWAYS;
511 jcr->JobPriority = Priority;
516 jcr->stime = get_pool_memory(PM_MESSAGE);
518 pm_strcpy(jcr->stime, since);
521 jcr->cloned = cloned;
523 if (find_arg(ua, NT_("fdcalled")) > 0) {
524 jcr->file_bsock = dup_bsock(ua->UA_sock);
529 /* If pool changed, update migration write storage */
530 if (jcr->JobType == JT_MIGRATE) {
531 if (!set_migration_wstorage(jcr, pool)) {
535 replace = ReplaceOptions[0].name;
536 for (i=0; ReplaceOptions[i].name; i++) {
537 if (ReplaceOptions[i].token == jcr->replace) {
538 replace = ReplaceOptions[i].name;
542 if (!get_level_from_name(jcr, level_name)) {
543 bsendmsg(ua, _("Level %s not valid.\n"), level_name);
548 /* Note, this is also MigrateJobId */
549 jcr->RestoreJobId = str_to_int64(jid);
552 /* Run without prompting? */
553 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
558 * Prompt User to see if all run job parameters are correct, and
559 * allow him to modify them.
561 if (!display_job_parameters(ua, jcr, job, verify_list, jid, replace)) {
565 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
570 * At user request modify parameters of job to be run.
572 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
575 start_prompt(ua, _("Parameters to modify:\n"));
576 add_prompt(ua, _("Level")); /* 0 */
577 add_prompt(ua, _("Storage")); /* 1 */
578 add_prompt(ua, _("Job")); /* 2 */
579 add_prompt(ua, _("FileSet")); /* 3 */
580 add_prompt(ua, _("Client")); /* 4 */
581 add_prompt(ua, _("When")); /* 5 */
582 add_prompt(ua, _("Priority")); /* 6 */
583 if (jcr->JobType == JT_BACKUP ||
584 jcr->JobType == JT_MIGRATE ||
585 jcr->JobType == JT_VERIFY) {
586 add_prompt(ua, _("Pool")); /* 7 */
587 if (jcr->JobType == JT_VERIFY) {
588 add_prompt(ua, _("Verify Job")); /* 8 */
590 } else if (jcr->JobType == JT_RESTORE) {
591 add_prompt(ua, _("Bootstrap")); /* 7 */
592 add_prompt(ua, _("Where")); /* 8 */
593 add_prompt(ua, _("Replace")); /* 9 */
594 add_prompt(ua, _("JobId")); /* 10 */
596 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
599 select_job_level(ua, jcr);
603 store.store = select_storage_resource(ua);
605 pm_strcpy(store.store_source, _("user selection"));
606 set_rwstorage(jcr, &store);
612 job = select_job_resource(ua);
615 set_jcr_defaults(jcr, job);
621 fileset = select_fileset_resource(ua);
623 jcr->fileset = fileset;
629 client = select_client_resource(ua);
631 jcr->client = client;
637 if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
640 if (ua->cmd[0] == 0) {
641 jcr->sched_time = time(NULL);
643 jcr->sched_time = str_to_utime(ua->cmd);
644 if (jcr->sched_time == 0) {
645 bsendmsg(ua, _("Invalid time, using current time.\n"));
646 jcr->sched_time = time(NULL);
652 if (!get_pint(ua, _("Enter new Priority: "))) {
655 if (ua->pint32_val == 0) {
656 bsendmsg(ua, _("Priority must be a positive integer.\n"));
658 jcr->JobPriority = ua->pint32_val;
662 /* Pool or Bootstrap depending on JobType */
663 if (jcr->JobType == JT_BACKUP ||
664 jcr->JobType == JT_MIGRATE ||
665 jcr->JobType == JT_VERIFY) { /* Pool */
666 pool = select_pool_resource(ua);
669 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
676 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
679 if (jcr->RestoreBootstrap) {
680 free(jcr->RestoreBootstrap);
681 jcr->RestoreBootstrap = NULL;
683 if (ua->cmd[0] != 0) {
684 jcr->RestoreBootstrap = bstrdup(ua->cmd);
685 fd = fopen(jcr->RestoreBootstrap, "rb");
687 bsendmsg(ua, _("Warning cannot open %s: ERR=%s\n"),
688 jcr->RestoreBootstrap, strerror(errno));
689 free(jcr->RestoreBootstrap);
690 jcr->RestoreBootstrap = NULL;
698 if (jcr->JobType == JT_VERIFY) {
699 verify_job = select_job_resource(ua);
701 jcr->verify_job = verify_job;
706 if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) {
713 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
716 jcr->where = bstrdup(ua->cmd);
720 start_prompt(ua, _("Replace:\n"));
721 for (i=0; ReplaceOptions[i].name; i++) {
722 add_prompt(ua, ReplaceOptions[i].name);
724 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
726 jcr->replace = ReplaceOptions[opt].token;
731 jid = NULL; /* force reprompt */
732 jcr->RestoreJobId = 0;
733 if (jcr->RestoreBootstrap) {
734 bsendmsg(ua, _("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
737 case -1: /* error or cancel */
745 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
747 Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
749 Dmsg1(100, "Using pool %s\n", jcr->pool->name());
750 JobId = run_job(jcr);
752 bsendmsg(ua, "<job director=\"console\" time=\"%u\" status=\"%c\" type=\"%c\" "
753 "jobid=\"%u\" job=\"%s\" level=\"%c\" finished=\"false\" priority=\"%u\"/>\n",
754 time(NULL), jcr->JobStatus, jcr->JobType, jcr->JobId,
755 jcr->Job, jcr->JobLevel, jcr->JobPriority);
757 free_jcr(jcr); /* release jcr */
759 bsendmsg(ua, _("Job failed.\n"));
762 bsendmsg(ua, _("Job queued. JobId=%s\n"), edit_int64(JobId,ed1));
768 bsendmsg(ua, _("Job not run.\n"));
770 return 0; /* do not run */
773 static void select_job_level(UAContext *ua, JCR *jcr)
775 if (jcr->JobType == JT_BACKUP) {
776 start_prompt(ua, _("Levels:\n"));
777 add_prompt(ua, _("Base"));
778 add_prompt(ua, _("Full"));
779 add_prompt(ua, _("Incremental"));
780 add_prompt(ua, _("Differential"));
781 add_prompt(ua, _("Since"));
782 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
784 jcr->JobLevel = L_BASE;
787 jcr->JobLevel = L_FULL;
790 jcr->JobLevel = L_INCREMENTAL;
793 jcr->JobLevel = L_DIFFERENTIAL;
796 jcr->JobLevel = L_SINCE;
801 } else if (jcr->JobType == JT_VERIFY) {
802 start_prompt(ua, _("Levels:\n"));
803 add_prompt(ua, _("Initialize Catalog"));
804 add_prompt(ua, _("Verify Catalog"));
805 add_prompt(ua, _("Verify Volume to Catalog"));
806 add_prompt(ua, _("Verify Disk to Catalog"));
807 add_prompt(ua, _("Verify Volume Data (not yet implemented)"));
808 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
810 jcr->JobLevel = L_VERIFY_INIT;
813 jcr->JobLevel = L_VERIFY_CATALOG;
816 jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
819 jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
822 jcr->JobLevel = L_VERIFY_DATA;
828 bsendmsg(ua, _("Level not appropriate for this Job. Cannot be changed.\n"));
833 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list,
834 char *jid, const char *replace)
836 Dmsg1(800, "JobType=%c\n", jcr->JobType);
837 switch (jcr->JobType) {
839 char dt[MAX_TIME_LENGTH];
841 bsendmsg(ua, _("Run %s job\n"
850 jcr->fileset->name(),
851 NPRT(jcr->client->name()),
852 jcr->wstore?jcr->wstore->name():"*None*",
853 bstrutime(dt, sizeof(dt), jcr->sched_time),
855 jcr->JobLevel = L_FULL;
859 if (jcr->JobType == JT_BACKUP) {
860 bsendmsg(ua, _("Run %s job\n"
865 "Pool: %s (From %s)\n"
866 "Storage: %s (From %s)\n"
871 level_to_str(jcr->JobLevel),
873 jcr->fileset->name(),
874 NPRT(jcr->pool->name()), jcr->pool_source,
875 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
876 bstrutime(dt, sizeof(dt), jcr->sched_time),
878 } else { /* JT_VERIFY */
880 if (jcr->verify_job) {
881 Name = jcr->verify_job->name();
886 verify_list = job->WriteVerifyList;
891 bsendmsg(ua, _("Run %s job\n"
896 "Pool: %s (From %s)\n"
897 "Storage: %s (From %s)\n"
904 level_to_str(jcr->JobLevel),
906 jcr->fileset->name(),
907 NPRT(jcr->pool->name()), jcr->pool_source,
908 jcr->rstore->name(), jcr->rstore_source,
911 bstrutime(dt, sizeof(dt), jcr->sched_time),
916 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
918 jcr->RestoreJobId = str_to_int64(jid);
920 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
923 jcr->RestoreJobId = ua->int64_val;
926 jcr->JobLevel = L_FULL; /* default level */
927 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
928 if (jcr->RestoreJobId == 0) {
929 bsendmsg(ua, _("Run Restore job\n"
941 NPRT(jcr->RestoreBootstrap),
942 jcr->where?jcr->where:NPRT(job->RestoreWhere),
944 jcr->fileset->name(),
947 bstrutime(dt, sizeof(dt), jcr->sched_time),
948 jcr->catalog->name(),
951 bsendmsg(ua, _("Run Restore job\n"
963 NPRT(jcr->RestoreBootstrap),
964 jcr->where?jcr->where:NPRT(job->RestoreWhere),
968 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
969 bstrutime(dt, sizeof(dt), jcr->sched_time),
970 jcr->catalog->name(),
975 jcr->JobLevel = L_FULL; /* default level */
976 bsendmsg(ua, _("Run Migration job\n"
981 "Pool: %s (From %s)\n"
982 "Read Storage: %s (From %s)\n"
983 "Write Storage: %s (From %s)\n"
989 NPRT(jcr->RestoreBootstrap),
991 jcr->fileset->name(),
992 NPRT(jcr->pool->name()), jcr->pool_source,
993 jcr->rstore->name(), jcr->rstore_source,
994 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
995 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
996 bstrutime(dt, sizeof(dt), jcr->sched_time),
997 jcr->catalog->name(),
1001 bsendmsg(ua, _("Unknown Job Type=%d\n"), jcr->JobType);