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);
459 jcr->verify_job = verify_job;
460 jcr->previous_job = previous_job;
462 set_rwstorage(jcr, &store);
463 jcr->client = client;
464 jcr->fileset = fileset;
465 jcr->ExpectedFiles = files;
466 if (catalog != NULL) {
467 jcr->catalog = catalog;
473 jcr->where = bstrdup(where);
477 jcr->sched_time = str_to_utime(when);
478 if (jcr->sched_time == 0) {
479 bsendmsg(ua, _("Invalid time, using current time.\n"));
480 jcr->sched_time = time(NULL);
485 if (jcr->RestoreBootstrap) {
486 free(jcr->RestoreBootstrap);
488 jcr->RestoreBootstrap = bstrdup(bootstrap);
493 for (i=0; ReplaceOptions[i].name; i++) {
494 if (strcasecmp(replace, ReplaceOptions[i].name) == 0) {
495 jcr->replace = ReplaceOptions[i].token;
499 bsendmsg(ua, _("Invalid replace option: %s\n"), replace);
502 } else if (job->replace) {
503 jcr->replace = job->replace;
505 jcr->replace = REPLACE_ALWAYS;
509 jcr->JobPriority = Priority;
514 jcr->stime = get_pool_memory(PM_MESSAGE);
516 pm_strcpy(jcr->stime, since);
519 jcr->cloned = cloned;
521 if (find_arg(ua, NT_("fdcalled")) > 0) {
522 jcr->file_bsock = dup_bsock(ua->UA_sock);
527 /* If pool changed, update migration write storage */
528 if (jcr->JobType == JT_MIGRATE) {
529 if (!set_migration_wstorage(jcr, pool)) {
533 replace = ReplaceOptions[0].name;
534 for (i=0; ReplaceOptions[i].name; i++) {
535 if (ReplaceOptions[i].token == jcr->replace) {
536 replace = ReplaceOptions[i].name;
540 if (!get_level_from_name(jcr, level_name)) {
541 bsendmsg(ua, _("Level %s not valid.\n"), level_name);
546 /* Note, this is also MigrateJobId */
547 jcr->RestoreJobId = str_to_int64(jid);
550 /* Run without prompting? */
551 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
556 * Prompt User to see if all run job parameters are correct, and
557 * allow him to modify them.
559 if (!display_job_parameters(ua, jcr, job, verify_list, jid, replace)) {
563 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
568 * At user request modify parameters of job to be run.
570 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
573 start_prompt(ua, _("Parameters to modify:\n"));
574 add_prompt(ua, _("Level")); /* 0 */
575 add_prompt(ua, _("Storage")); /* 1 */
576 add_prompt(ua, _("Job")); /* 2 */
577 add_prompt(ua, _("FileSet")); /* 3 */
578 add_prompt(ua, _("Client")); /* 4 */
579 add_prompt(ua, _("When")); /* 5 */
580 add_prompt(ua, _("Priority")); /* 6 */
581 if (jcr->JobType == JT_BACKUP ||
582 jcr->JobType == JT_MIGRATE ||
583 jcr->JobType == JT_VERIFY) {
584 add_prompt(ua, _("Pool")); /* 7 */
585 if (jcr->JobType == JT_VERIFY) {
586 add_prompt(ua, _("Verify Job")); /* 8 */
588 } else if (jcr->JobType == JT_RESTORE) {
589 add_prompt(ua, _("Bootstrap")); /* 7 */
590 add_prompt(ua, _("Where")); /* 8 */
591 add_prompt(ua, _("Replace")); /* 9 */
592 add_prompt(ua, _("JobId")); /* 10 */
594 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
597 select_job_level(ua, jcr);
601 store.store = select_storage_resource(ua);
603 pm_strcpy(store.store_source, _("user selection"));
604 set_rwstorage(jcr, &store);
610 job = select_job_resource(ua);
613 set_jcr_defaults(jcr, job);
619 fileset = select_fileset_resource(ua);
621 jcr->fileset = fileset;
627 client = select_client_resource(ua);
629 jcr->client = client;
635 if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
638 if (ua->cmd[0] == 0) {
639 jcr->sched_time = time(NULL);
641 jcr->sched_time = str_to_utime(ua->cmd);
642 if (jcr->sched_time == 0) {
643 bsendmsg(ua, _("Invalid time, using current time.\n"));
644 jcr->sched_time = time(NULL);
650 if (!get_pint(ua, _("Enter new Priority: "))) {
653 if (ua->pint32_val == 0) {
654 bsendmsg(ua, _("Priority must be a positive integer.\n"));
656 jcr->JobPriority = ua->pint32_val;
660 /* Pool or Bootstrap depending on JobType */
661 if (jcr->JobType == JT_BACKUP ||
662 jcr->JobType == JT_MIGRATE ||
663 jcr->JobType == JT_VERIFY) { /* Pool */
664 pool = select_pool_resource(ua);
667 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
674 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
677 if (jcr->RestoreBootstrap) {
678 free(jcr->RestoreBootstrap);
679 jcr->RestoreBootstrap = NULL;
681 if (ua->cmd[0] != 0) {
682 jcr->RestoreBootstrap = bstrdup(ua->cmd);
683 fd = fopen(jcr->RestoreBootstrap, "rb");
685 bsendmsg(ua, _("Warning cannot open %s: ERR=%s\n"),
686 jcr->RestoreBootstrap, strerror(errno));
687 free(jcr->RestoreBootstrap);
688 jcr->RestoreBootstrap = NULL;
696 if (jcr->JobType == JT_VERIFY) {
697 verify_job = select_job_resource(ua);
699 jcr->verify_job = verify_job;
704 if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) {
711 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
714 jcr->where = bstrdup(ua->cmd);
718 start_prompt(ua, _("Replace:\n"));
719 for (i=0; ReplaceOptions[i].name; i++) {
720 add_prompt(ua, ReplaceOptions[i].name);
722 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
724 jcr->replace = ReplaceOptions[opt].token;
729 jid = NULL; /* force reprompt */
730 jcr->RestoreJobId = 0;
731 if (jcr->RestoreBootstrap) {
732 bsendmsg(ua, _("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
735 case -1: /* error or cancel */
743 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
745 Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
747 Dmsg1(100, "Using pool %s\n", jcr->pool->name());
748 JobId = run_job(jcr);
750 bsendmsg(ua, "<job director=\"console\" time=\"%u\" status=\"%c\" type=\"%c\" "
751 "jobid=\"%u\" job=\"%s\" level=\"%c\" finished=\"false\" priority=\"%u\"/>\n",
752 time(NULL), jcr->JobStatus, jcr->JobType, jcr->JobId,
753 jcr->Job, jcr->JobLevel, jcr->JobPriority);
755 free_jcr(jcr); /* release jcr */
757 bsendmsg(ua, _("Job failed.\n"));
760 bsendmsg(ua, _("Job queued. JobId=%s\n"), edit_int64(JobId,ed1));
766 bsendmsg(ua, _("Job not run.\n"));
768 return 0; /* do not run */
771 static void select_job_level(UAContext *ua, JCR *jcr)
773 if (jcr->JobType == JT_BACKUP) {
774 start_prompt(ua, _("Levels:\n"));
775 add_prompt(ua, _("Base"));
776 add_prompt(ua, _("Full"));
777 add_prompt(ua, _("Incremental"));
778 add_prompt(ua, _("Differential"));
779 add_prompt(ua, _("Since"));
780 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
782 jcr->JobLevel = L_BASE;
785 jcr->JobLevel = L_FULL;
788 jcr->JobLevel = L_INCREMENTAL;
791 jcr->JobLevel = L_DIFFERENTIAL;
794 jcr->JobLevel = L_SINCE;
799 } else if (jcr->JobType == JT_VERIFY) {
800 start_prompt(ua, _("Levels:\n"));
801 add_prompt(ua, _("Initialize Catalog"));
802 add_prompt(ua, _("Verify Catalog"));
803 add_prompt(ua, _("Verify Volume to Catalog"));
804 add_prompt(ua, _("Verify Disk to Catalog"));
805 add_prompt(ua, _("Verify Volume Data (not yet implemented)"));
806 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
808 jcr->JobLevel = L_VERIFY_INIT;
811 jcr->JobLevel = L_VERIFY_CATALOG;
814 jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
817 jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
820 jcr->JobLevel = L_VERIFY_DATA;
826 bsendmsg(ua, _("Level not appropriate for this Job. Cannot be changed.\n"));
831 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list,
832 char *jid, const char *replace)
834 Dmsg1(800, "JobType=%c\n", jcr->JobType);
835 switch (jcr->JobType) {
837 char dt[MAX_TIME_LENGTH];
839 bsendmsg(ua, _("Run %s job\n"
848 jcr->fileset->name(),
849 NPRT(jcr->client->name()),
850 jcr->wstore?jcr->wstore->name():"*None*",
851 bstrutime(dt, sizeof(dt), jcr->sched_time),
853 jcr->JobLevel = L_FULL;
857 if (jcr->JobType == JT_BACKUP) {
858 bsendmsg(ua, _("Run %s job\n"
863 "Pool: %s (From %s)\n"
864 "Storage: %s (From %s)\n"
869 level_to_str(jcr->JobLevel),
871 jcr->fileset->name(),
872 NPRT(jcr->pool->name()), jcr->pool_source,
873 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
874 bstrutime(dt, sizeof(dt), jcr->sched_time),
876 } else { /* JT_VERIFY */
878 if (jcr->verify_job) {
879 Name = jcr->verify_job->name();
884 verify_list = job->WriteVerifyList;
889 bsendmsg(ua, _("Run %s job\n"
894 "Pool: %s (From %s)\n"
895 "Storage: %s (From %s)\n"
902 level_to_str(jcr->JobLevel),
904 jcr->fileset->name(),
905 NPRT(jcr->pool->name()), jcr->pool_source,
906 jcr->rstore->name(), jcr->rstore_source,
909 bstrutime(dt, sizeof(dt), jcr->sched_time),
914 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
916 jcr->RestoreJobId = str_to_int64(jid);
918 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
921 jcr->RestoreJobId = ua->int64_val;
924 jcr->JobLevel = L_FULL; /* default level */
925 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
926 if (jcr->RestoreJobId == 0) {
927 bsendmsg(ua, _("Run Restore job\n"
939 NPRT(jcr->RestoreBootstrap),
940 jcr->where?jcr->where:NPRT(job->RestoreWhere),
942 jcr->fileset->name(),
945 bstrutime(dt, sizeof(dt), jcr->sched_time),
946 jcr->catalog->name(),
949 bsendmsg(ua, _("Run Restore job\n"
961 NPRT(jcr->RestoreBootstrap),
962 jcr->where?jcr->where:NPRT(job->RestoreWhere),
966 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
967 bstrutime(dt, sizeof(dt), jcr->sched_time),
968 jcr->catalog->name(),
973 jcr->JobLevel = L_FULL; /* default level */
974 bsendmsg(ua, _("Run Migration job\n"
979 "Pool: %s (From %s)\n"
980 "Read Storage: %s (From %s)\n"
981 "Write Storage: %s (From %s)\n"
987 NPRT(jcr->RestoreBootstrap),
989 jcr->fileset->name(),
990 NPRT(jcr->pool->name()), jcr->pool_source,
991 jcr->rstore->name(), jcr->rstore_source,
992 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
993 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
994 bstrutime(dt, sizeof(dt), jcr->sched_time),
995 jcr->catalog->name(),
999 bsendmsg(ua, _("Unknown Job Type=%d\n"), jcr->JobType);