3 * Bacula Director -- Run Command
5 * Kern Sibbald, December MMI
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2001-2007 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 pm_strcpy(jcr->client_name, client->name());
471 jcr->fileset = fileset;
472 jcr->ExpectedFiles = files;
473 if (catalog != NULL) {
474 jcr->catalog = catalog;
480 jcr->where = bstrdup(where);
484 jcr->sched_time = str_to_utime(when);
485 if (jcr->sched_time == 0) {
486 bsendmsg(ua, _("Invalid time, using current time.\n"));
487 jcr->sched_time = time(NULL);
492 if (jcr->RestoreBootstrap) {
493 free(jcr->RestoreBootstrap);
495 jcr->RestoreBootstrap = bstrdup(bootstrap);
500 for (i=0; ReplaceOptions[i].name; i++) {
501 if (strcasecmp(replace, ReplaceOptions[i].name) == 0) {
502 jcr->replace = ReplaceOptions[i].token;
506 bsendmsg(ua, _("Invalid replace option: %s\n"), replace);
509 } else if (job->replace) {
510 jcr->replace = job->replace;
512 jcr->replace = REPLACE_ALWAYS;
516 jcr->JobPriority = Priority;
521 jcr->stime = get_pool_memory(PM_MESSAGE);
523 pm_strcpy(jcr->stime, since);
526 jcr->cloned = cloned;
528 if (find_arg(ua, NT_("fdcalled")) > 0) {
529 jcr->file_bsock = dup_bsock(ua->UA_sock);
534 /* If pool changed, update migration write storage */
535 if (jcr->JobType == JT_MIGRATE) {
536 if (!set_migration_wstorage(jcr, pool)) {
540 replace = ReplaceOptions[0].name;
541 for (i=0; ReplaceOptions[i].name; i++) {
542 if (ReplaceOptions[i].token == jcr->replace) {
543 replace = ReplaceOptions[i].name;
547 if (!get_level_from_name(jcr, level_name)) {
548 bsendmsg(ua, _("Level %s not valid.\n"), level_name);
553 /* Note, this is also MigrateJobId */
554 jcr->RestoreJobId = str_to_int64(jid);
557 /* Run without prompting? */
558 if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
563 * Prompt User to see if all run job parameters are correct, and
564 * allow him to modify them.
566 if (!display_job_parameters(ua, jcr, job, verify_list, jid, replace)) {
570 if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
575 * At user request modify parameters of job to be run.
577 if (ua->cmd[0] != 0 && strncasecmp(ua->cmd, _("mod"), strlen(ua->cmd)) == 0) {
580 start_prompt(ua, _("Parameters to modify:\n"));
581 add_prompt(ua, _("Level")); /* 0 */
582 add_prompt(ua, _("Storage")); /* 1 */
583 add_prompt(ua, _("Job")); /* 2 */
584 add_prompt(ua, _("FileSet")); /* 3 */
585 add_prompt(ua, _("Client")); /* 4 */
586 add_prompt(ua, _("When")); /* 5 */
587 add_prompt(ua, _("Priority")); /* 6 */
588 if (jcr->JobType == JT_BACKUP ||
589 jcr->JobType == JT_MIGRATE ||
590 jcr->JobType == JT_VERIFY) {
591 add_prompt(ua, _("Pool")); /* 7 */
592 if (jcr->JobType == JT_VERIFY) {
593 add_prompt(ua, _("Verify Job")); /* 8 */
595 } else if (jcr->JobType == JT_RESTORE) {
596 add_prompt(ua, _("Bootstrap")); /* 7 */
597 add_prompt(ua, _("Where")); /* 8 */
598 add_prompt(ua, _("Replace")); /* 9 */
599 add_prompt(ua, _("JobId")); /* 10 */
601 switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
604 select_job_level(ua, jcr);
608 store.store = select_storage_resource(ua);
610 pm_strcpy(store.store_source, _("user selection"));
611 set_rwstorage(jcr, &store);
617 job = select_job_resource(ua);
620 set_jcr_defaults(jcr, job);
626 fileset = select_fileset_resource(ua);
628 jcr->fileset = fileset;
634 client = select_client_resource(ua);
636 jcr->client = client;
642 if (!get_cmd(ua, _("Please enter desired start time as YYYY-MM-DD HH:MM:SS (return for now): "))) {
645 if (ua->cmd[0] == 0) {
646 jcr->sched_time = time(NULL);
648 jcr->sched_time = str_to_utime(ua->cmd);
649 if (jcr->sched_time == 0) {
650 bsendmsg(ua, _("Invalid time, using current time.\n"));
651 jcr->sched_time = time(NULL);
657 if (!get_pint(ua, _("Enter new Priority: "))) {
660 if (ua->pint32_val == 0) {
661 bsendmsg(ua, _("Priority must be a positive integer.\n"));
663 jcr->JobPriority = ua->pint32_val;
667 /* Pool or Bootstrap depending on JobType */
668 if (jcr->JobType == JT_BACKUP ||
669 jcr->JobType == JT_MIGRATE ||
670 jcr->JobType == JT_VERIFY) { /* Pool */
671 pool = select_pool_resource(ua);
674 Dmsg1(100, "Set new pool=%s\n", jcr->pool->name());
681 if (!get_cmd(ua, _("Please enter the Bootstrap file name: "))) {
684 if (jcr->RestoreBootstrap) {
685 free(jcr->RestoreBootstrap);
686 jcr->RestoreBootstrap = NULL;
688 if (ua->cmd[0] != 0) {
689 jcr->RestoreBootstrap = bstrdup(ua->cmd);
690 fd = fopen(jcr->RestoreBootstrap, "rb");
692 bsendmsg(ua, _("Warning cannot open %s: ERR=%s\n"),
693 jcr->RestoreBootstrap, strerror(errno));
694 free(jcr->RestoreBootstrap);
695 jcr->RestoreBootstrap = NULL;
703 if (jcr->JobType == JT_VERIFY) {
704 verify_job = select_job_resource(ua);
706 jcr->verify_job = verify_job;
711 if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) {
718 if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
721 jcr->where = bstrdup(ua->cmd);
725 start_prompt(ua, _("Replace:\n"));
726 for (i=0; ReplaceOptions[i].name; i++) {
727 add_prompt(ua, ReplaceOptions[i].name);
729 opt = do_prompt(ua, "", _("Select replace option"), NULL, 0);
731 jcr->replace = ReplaceOptions[opt].token;
736 jid = NULL; /* force reprompt */
737 jcr->RestoreJobId = 0;
738 if (jcr->RestoreBootstrap) {
739 bsendmsg(ua, _("You must set the bootstrap file to NULL to be able to specify a JobId.\n"));
742 case -1: /* error or cancel */
750 if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
752 Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
754 Dmsg1(100, "Using pool %s\n", jcr->pool->name());
755 JobId = run_job(jcr);
757 bsendmsg(ua, "<job director=\"console\" time=\"%u\" status=\"%c\" type=\"%c\" "
758 "jobid=\"%u\" job=\"%s\" level=\"%c\" finished=\"false\" priority=\"%u\"/>\n",
759 time(NULL), jcr->JobStatus, jcr->JobType, jcr->JobId,
760 jcr->Job, jcr->JobLevel, jcr->JobPriority);
762 free_jcr(jcr); /* release jcr */
764 bsendmsg(ua, _("Job failed.\n"));
767 bsendmsg(ua, _("Job queued. JobId=%s\n"), edit_int64(JobId,ed1));
773 bsendmsg(ua, _("Job not run.\n"));
775 return 0; /* do not run */
778 static void select_job_level(UAContext *ua, JCR *jcr)
780 if (jcr->JobType == JT_BACKUP) {
781 start_prompt(ua, _("Levels:\n"));
782 add_prompt(ua, _("Base"));
783 add_prompt(ua, _("Full"));
784 add_prompt(ua, _("Incremental"));
785 add_prompt(ua, _("Differential"));
786 add_prompt(ua, _("Since"));
787 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
789 jcr->JobLevel = L_BASE;
792 jcr->JobLevel = L_FULL;
795 jcr->JobLevel = L_INCREMENTAL;
798 jcr->JobLevel = L_DIFFERENTIAL;
801 jcr->JobLevel = L_SINCE;
806 } else if (jcr->JobType == JT_VERIFY) {
807 start_prompt(ua, _("Levels:\n"));
808 add_prompt(ua, _("Initialize Catalog"));
809 add_prompt(ua, _("Verify Catalog"));
810 add_prompt(ua, _("Verify Volume to Catalog"));
811 add_prompt(ua, _("Verify Disk to Catalog"));
812 add_prompt(ua, _("Verify Volume Data (not yet implemented)"));
813 switch (do_prompt(ua, "", _("Select level"), NULL, 0)) {
815 jcr->JobLevel = L_VERIFY_INIT;
818 jcr->JobLevel = L_VERIFY_CATALOG;
821 jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
824 jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
827 jcr->JobLevel = L_VERIFY_DATA;
833 bsendmsg(ua, _("Level not appropriate for this Job. Cannot be changed.\n"));
838 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list,
839 char *jid, const char *replace)
841 Dmsg1(800, "JobType=%c\n", jcr->JobType);
842 switch (jcr->JobType) {
844 char dt[MAX_TIME_LENGTH];
846 bsendmsg(ua, _("Run %s job\n"
855 jcr->fileset->name(),
856 NPRT(jcr->client->name()),
857 jcr->wstore?jcr->wstore->name():"*None*",
858 bstrutime(dt, sizeof(dt), jcr->sched_time),
860 jcr->JobLevel = L_FULL;
864 if (jcr->JobType == JT_BACKUP) {
865 bsendmsg(ua, _("Run %s job\n"
870 "Pool: %s (From %s)\n"
871 "Storage: %s (From %s)\n"
876 level_to_str(jcr->JobLevel),
878 jcr->fileset->name(),
879 NPRT(jcr->pool->name()), jcr->pool_source,
880 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
881 bstrutime(dt, sizeof(dt), jcr->sched_time),
883 } else { /* JT_VERIFY */
885 if (jcr->verify_job) {
886 Name = jcr->verify_job->name();
891 verify_list = job->WriteVerifyList;
896 bsendmsg(ua, _("Run %s job\n"
901 "Pool: %s (From %s)\n"
902 "Storage: %s (From %s)\n"
909 level_to_str(jcr->JobLevel),
911 jcr->fileset->name(),
912 NPRT(jcr->pool->name()), jcr->pool_source,
913 jcr->rstore->name(), jcr->rstore_source,
916 bstrutime(dt, sizeof(dt), jcr->sched_time),
921 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
923 jcr->RestoreJobId = str_to_int64(jid);
925 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
928 jcr->RestoreJobId = ua->int64_val;
931 jcr->JobLevel = L_FULL; /* default level */
932 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
933 if (jcr->RestoreJobId == 0) {
934 bsendmsg(ua, _("Run Restore job\n"
946 NPRT(jcr->RestoreBootstrap),
947 jcr->where?jcr->where:NPRT(job->RestoreWhere),
949 jcr->fileset->name(),
952 bstrutime(dt, sizeof(dt), jcr->sched_time),
953 jcr->catalog->name(),
956 bsendmsg(ua, _("Run Restore job\n"
968 NPRT(jcr->RestoreBootstrap),
969 jcr->where?jcr->where:NPRT(job->RestoreWhere),
973 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
974 bstrutime(dt, sizeof(dt), jcr->sched_time),
975 jcr->catalog->name(),
980 jcr->JobLevel = L_FULL; /* default level */
981 bsendmsg(ua, _("Run Migration job\n"
986 "Pool: %s (From %s)\n"
987 "Read Storage: %s (From %s)\n"
988 "Write Storage: %s (From %s)\n"
994 NPRT(jcr->RestoreBootstrap),
996 jcr->fileset->name(),
997 NPRT(jcr->pool->name()), jcr->pool_source,
998 jcr->rstore->name(), jcr->rstore_source,
999 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1000 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
1001 bstrutime(dt, sizeof(dt), jcr->sched_time),
1002 jcr->catalog->name(),
1006 bsendmsg(ua, _("Unknown Job Type=%d\n"), jcr->JobType);