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 */
108 if (!open_client_db(ua)) {
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);
325 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
326 bsendmsg(ua, _("No authorization. Catalog \"%s\".\n"), catalog->name());
330 Dmsg1(800, "Using catalog=%s\n", NPRT(catalog_name));
334 job = (JOB *)GetResWithName(R_JOB, job_name);
336 if (*job_name != 0) {
337 bsendmsg(ua, _("Job \"%s\" not found\n"), job_name);
339 job = select_job_resource(ua);
341 Dmsg1(800, "Found job=%s\n", job_name);
344 bsendmsg(ua, _("A job name must be specified.\n"));
345 job = select_job_resource(ua);
349 } else if (!acl_access_ok(ua, Job_ACL, job->name())) {
350 bsendmsg(ua, _("No authorization. Job \"%s\".\n"),
356 pool = (POOL *)GetResWithName(R_POOL, pool_name);
358 if (*pool_name != 0) {
359 bsendmsg(ua, _("Pool \"%s\" not found.\n"), pool_name);
361 pool = select_pool_resource(ua);
364 pool = job->pool; /* use default */
368 } else if (!acl_access_ok(ua, Pool_ACL, pool->name())) {
369 bsendmsg(ua, _("No authorization. Pool \"%s\".\n"),
373 Dmsg1(100, "Using pool %s\n", pool->name());
376 store.store = (STORE *)GetResWithName(R_STORAGE, store_name);
377 pm_strcpy(store.store_source, _("command line"));
379 if (*store_name != 0) {
380 bsendmsg(ua, _("Storage \"%s\" not found.\n"), store_name);
382 store.store = select_storage_resource(ua);
383 pm_strcpy(store.store_source, _("user selection"));
386 get_job_storage(&store, job, NULL); /* use default */
389 bsendmsg(ua, _("No storage specified.\n"));
391 } else if (!acl_access_ok(ua, Storage_ACL, store.store->name())) {
392 bsendmsg(ua, _("No authorization. Storage \"%s\".\n"),
393 store.store->name());
396 Dmsg1(800, "Using storage=%s\n", store.store->name());
399 client = (CLIENT *)GetResWithName(R_CLIENT, client_name);
401 if (*client_name != 0) {
402 bsendmsg(ua, _("Client \"%s\" not found.\n"), client_name);
404 client = select_client_resource(ua);
407 client = job->client; /* use default */
411 } else if (!acl_access_ok(ua, Client_ACL, client->name())) {
412 bsendmsg(ua, _("No authorization. Client \"%s\".\n"),
416 Dmsg1(800, "Using client=%s\n", client->name());
419 fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
421 bsendmsg(ua, _("FileSet \"%s\" not found.\n"), fileset_name);
422 fileset = select_fileset_resource(ua);
425 fileset = job->fileset; /* use default */
429 } else if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
430 bsendmsg(ua, _("No authorization. FileSet \"%s\".\n"),
435 if (verify_job_name) {
436 verify_job = (JOB *)GetResWithName(R_JOB, verify_job_name);
438 bsendmsg(ua, _("Verify Job \"%s\" not found.\n"), verify_job_name);
439 verify_job = select_job_resource(ua);
442 verify_job = job->verify_job;
445 if (previous_job_name) {
446 previous_job = (JOB *)GetResWithName(R_JOB, previous_job_name);
448 bsendmsg(ua, _("Migration Job \"%s\" not found.\n"), previous_job_name);
449 previous_job = select_job_resource(ua);
452 previous_job = job->verify_job;
457 * Create JCR to run job. NOTE!!! after this point, free_jcr()
460 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
461 set_jcr_defaults(jcr, job);
462 jcr->unlink_bsr = ua->jcr->unlink_bsr; /* copy unlink flag from caller */
463 ua->jcr->unlink_bsr = false;
465 jcr->verify_job = verify_job;
466 jcr->previous_job = previous_job;
468 set_rwstorage(jcr, &store);
469 jcr->client = client;
470 jcr->fileset = fileset;
471 jcr->ExpectedFiles = files;
472 if (catalog != NULL) {
473 jcr->catalog = catalog;
479 jcr->where = bstrdup(where);
483 jcr->sched_time = str_to_utime(when);
484 if (jcr->sched_time == 0) {
485 bsendmsg(ua, _("Invalid time, using current time.\n"));
486 jcr->sched_time = time(NULL);
491 if (jcr->RestoreBootstrap) {
492 free(jcr->RestoreBootstrap);
494 jcr->RestoreBootstrap = bstrdup(bootstrap);
499 for (i=0; ReplaceOptions[i].name; i++) {
500 if (strcasecmp(replace, ReplaceOptions[i].name) == 0) {
501 jcr->replace = ReplaceOptions[i].token;
505 bsendmsg(ua, _("Invalid replace option: %s\n"), replace);
508 } else if (job->replace) {
509 jcr->replace = job->replace;
511 jcr->replace = REPLACE_ALWAYS;
515 jcr->JobPriority = Priority;
520 jcr->stime = get_pool_memory(PM_MESSAGE);
522 pm_strcpy(jcr->stime, since);
525 jcr->cloned = cloned;
527 if (find_arg(ua, NT_("fdcalled")) > 0) {
528 jcr->file_bsock = dup_bsock(ua->UA_sock);
533 /* If pool changed, update migration write storage */
534 if (jcr->JobType == JT_MIGRATE) {
535 if (!set_migration_wstorage(jcr, pool)) {
539 replace = ReplaceOptions[0].name;
540 for (i=0; ReplaceOptions[i].name; i++) {
541 if (ReplaceOptions[i].token == jcr->replace) {
542 replace = ReplaceOptions[i].name;
546 if (!get_level_from_name(jcr, level_name)) {
547 bsendmsg(ua, _("Level %s not valid.\n"), level_name);
552 /* Note, this is also MigrateJobId */
553 jcr->RestoreJobId = str_to_int64(jid);
556 /* Run without prompting? */
557 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
562 * Prompt User to see if all run job parameters are correct, and
563 * allow him to modify them.
565 if (!display_job_parameters(ua, jcr, job, verify_list, jid, replace)) {
569 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
574 * At user request modify parameters of job to be run.
576 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
579 start_prompt(ua, _("Parameters to modify:\n"));
580 add_prompt(ua, _("Level")); /* 0 */
581 add_prompt(ua, _("Storage")); /* 1 */
582 add_prompt(ua, _("Job")); /* 2 */
583 add_prompt(ua, _("FileSet")); /* 3 */
584 add_prompt(ua, _("Client")); /* 4 */
585 add_prompt(ua, _("When")); /* 5 */
586 add_prompt(ua, _("Priority")); /* 6 */
587 if (jcr->JobType == JT_BACKUP ||
588 jcr->JobType == JT_MIGRATE ||
589 jcr->JobType == JT_VERIFY) {
590 add_prompt(ua, _("Pool")); /* 7 */
591 if (jcr->JobType == JT_VERIFY) {
592 add_prompt(ua, _("Verify Job")); /* 8 */
594 } else if (jcr->JobType == JT_RESTORE) {
595 add_prompt(ua, _("Bootstrap")); /* 7 */
596 add_prompt(ua, _("Where")); /* 8 */
597 add_prompt(ua, _("Replace")); /* 9 */
598 add_prompt(ua, _("JobId")); /* 10 */
600 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
603 select_job_level(ua, jcr);
607 store.store = select_storage_resource(ua);
609 pm_strcpy(store.store_source, _("user selection"));
610 set_rwstorage(jcr, &store);
616 job = select_job_resource(ua);
619 set_jcr_defaults(jcr, job);
625 fileset = select_fileset_resource(ua);
627 jcr->fileset = fileset;
633 client = select_client_resource(ua);
635 jcr->client = client;
641 if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
644 if (ua->cmd[0] == 0) {
645 jcr->sched_time = time(NULL);
647 jcr->sched_time = str_to_utime(ua->cmd);
648 if (jcr->sched_time == 0) {
649 bsendmsg(ua, _("Invalid time, using current time.\n"));
650 jcr->sched_time = time(NULL);
656 if (!get_pint(ua, _("Enter new Priority: "))) {
659 if (ua->pint32_val == 0) {
660 bsendmsg(ua, _("Priority must be a positive integer.\n"));
662 jcr->JobPriority = ua->pint32_val;
666 /* Pool or Bootstrap depending on JobType */
667 if (jcr->JobType == JT_BACKUP ||
668 jcr->JobType == JT_MIGRATE ||
669 jcr->JobType == JT_VERIFY) { /* Pool */
670 pool = select_pool_resource(ua);
673 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
680 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
683 if (jcr->RestoreBootstrap) {
684 free(jcr->RestoreBootstrap);
685 jcr->RestoreBootstrap = NULL;
687 if (ua->cmd[0] != 0) {
688 jcr->RestoreBootstrap = bstrdup(ua->cmd);
689 fd = fopen(jcr->RestoreBootstrap, "rb");
691 bsendmsg(ua, _("Warning cannot open %s: ERR=%s\n"),
692 jcr->RestoreBootstrap, strerror(errno));
693 free(jcr->RestoreBootstrap);
694 jcr->RestoreBootstrap = NULL;
702 if (jcr->JobType == JT_VERIFY) {
703 verify_job = select_job_resource(ua);
705 jcr->verify_job = verify_job;
710 if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) {
717 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
720 jcr->where = bstrdup(ua->cmd);
724 start_prompt(ua, _("Replace:\n"));
725 for (i=0; ReplaceOptions[i].name; i++) {
726 add_prompt(ua, ReplaceOptions[i].name);
728 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
730 jcr->replace = ReplaceOptions[opt].token;
735 jid = NULL; /* force reprompt */
736 jcr->RestoreJobId = 0;
737 if (jcr->RestoreBootstrap) {
738 bsendmsg(ua, _("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
741 case -1: /* error or cancel */
749 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
751 Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
753 Dmsg1(100, "Using pool %s\n", jcr->pool->name());
754 JobId = run_job(jcr);
756 bsendmsg(ua, "<job director=\"console\" time=\"%u\" status=\"%c\" type=\"%c\" "
757 "jobid=\"%u\" job=\"%s\" level=\"%c\" finished=\"false\" priority=\"%u\"/>\n",
758 time(NULL), jcr->JobStatus, jcr->JobType, jcr->JobId,
759 jcr->Job, jcr->JobLevel, jcr->JobPriority);
761 free_jcr(jcr); /* release jcr */
763 bsendmsg(ua, _("Job failed.\n"));
766 bsendmsg(ua, _("Job queued. JobId=%s\n"), edit_int64(JobId,ed1));
772 bsendmsg(ua, _("Job not run.\n"));
774 return 0; /* do not run */
777 static void select_job_level(UAContext *ua, JCR *jcr)
779 if (jcr->JobType == JT_BACKUP) {
780 start_prompt(ua, _("Levels:\n"));
781 add_prompt(ua, _("Base"));
782 add_prompt(ua, _("Full"));
783 add_prompt(ua, _("Incremental"));
784 add_prompt(ua, _("Differential"));
785 add_prompt(ua, _("Since"));
786 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
788 jcr->JobLevel = L_BASE;
791 jcr->JobLevel = L_FULL;
794 jcr->JobLevel = L_INCREMENTAL;
797 jcr->JobLevel = L_DIFFERENTIAL;
800 jcr->JobLevel = L_SINCE;
805 } else if (jcr->JobType == JT_VERIFY) {
806 start_prompt(ua, _("Levels:\n"));
807 add_prompt(ua, _("Initialize Catalog"));
808 add_prompt(ua, _("Verify Catalog"));
809 add_prompt(ua, _("Verify Volume to Catalog"));
810 add_prompt(ua, _("Verify Disk to Catalog"));
811 add_prompt(ua, _("Verify Volume Data (not yet implemented)"));
812 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
814 jcr->JobLevel = L_VERIFY_INIT;
817 jcr->JobLevel = L_VERIFY_CATALOG;
820 jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
823 jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
826 jcr->JobLevel = L_VERIFY_DATA;
832 bsendmsg(ua, _("Level not appropriate for this Job. Cannot be changed.\n"));
837 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list,
838 char *jid, const char *replace)
840 Dmsg1(800, "JobType=%c\n", jcr->JobType);
841 switch (jcr->JobType) {
843 char dt[MAX_TIME_LENGTH];
845 bsendmsg(ua, _("Run %s job\n"
854 jcr->fileset->name(),
855 NPRT(jcr->client->name()),
856 jcr->wstore?jcr->wstore->name():"*None*",
857 bstrutime(dt, sizeof(dt), jcr->sched_time),
859 jcr->JobLevel = L_FULL;
863 if (jcr->JobType == JT_BACKUP) {
864 bsendmsg(ua, _("Run %s job\n"
869 "Pool: %s (From %s)\n"
870 "Storage: %s (From %s)\n"
875 level_to_str(jcr->JobLevel),
877 jcr->fileset->name(),
878 NPRT(jcr->pool->name()), jcr->pool_source,
879 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
880 bstrutime(dt, sizeof(dt), jcr->sched_time),
882 } else { /* JT_VERIFY */
884 if (jcr->verify_job) {
885 Name = jcr->verify_job->name();
890 verify_list = job->WriteVerifyList;
895 bsendmsg(ua, _("Run %s job\n"
900 "Pool: %s (From %s)\n"
901 "Storage: %s (From %s)\n"
908 level_to_str(jcr->JobLevel),
910 jcr->fileset->name(),
911 NPRT(jcr->pool->name()), jcr->pool_source,
912 jcr->rstore->name(), jcr->rstore_source,
915 bstrutime(dt, sizeof(dt), jcr->sched_time),
920 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
922 jcr->RestoreJobId = str_to_int64(jid);
924 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
927 jcr->RestoreJobId = ua->int64_val;
930 jcr->JobLevel = L_FULL; /* default level */
931 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
932 if (jcr->RestoreJobId == 0) {
933 bsendmsg(ua, _("Run Restore job\n"
945 NPRT(jcr->RestoreBootstrap),
946 jcr->where?jcr->where:NPRT(job->RestoreWhere),
948 jcr->fileset->name(),
951 bstrutime(dt, sizeof(dt), jcr->sched_time),
952 jcr->catalog->name(),
955 bsendmsg(ua, _("Run Restore job\n"
967 NPRT(jcr->RestoreBootstrap),
968 jcr->where?jcr->where:NPRT(job->RestoreWhere),
972 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
973 bstrutime(dt, sizeof(dt), jcr->sched_time),
974 jcr->catalog->name(),
979 jcr->JobLevel = L_FULL; /* default level */
980 bsendmsg(ua, _("Run Migration job\n"
985 "Pool: %s (From %s)\n"
986 "Read Storage: %s (From %s)\n"
987 "Write Storage: %s (From %s)\n"
993 NPRT(jcr->RestoreBootstrap),
995 jcr->fileset->name(),
996 NPRT(jcr->pool->name()), jcr->pool_source,
997 jcr->rstore->name(), jcr->rstore_source,
998 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
999 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
1000 bstrutime(dt, sizeof(dt), jcr->sched_time),
1001 jcr->catalog->name(),
1005 bsendmsg(ua, _("Unknown Job Type=%d\n"), jcr->JobType);