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 ua->send_msg(_("Value missing for keyword %s\n"), ua->argk[i]);
139 Dmsg1(800, "Got keyword=%s\n", NPRT(kw[j]));
143 ua->send_msg(_("Job name specified twice.\n"));
146 job_name = ua->argv[i];
151 ua->send_msg(_("JobId specified twice.\n"));
160 ua->send_msg(_("Client specified twice.\n"));
163 client_name = ua->argv[i];
166 case 4: /* fileset */
168 ua->send_msg(_("FileSet specified twice.\n"));
171 fileset_name = ua->argv[i];
176 ua->send_msg(_("Level specified twice.\n"));
179 level_name = ua->argv[i];
182 case 6: /* storage */
185 ua->send_msg(_("Storage specified twice.\n"));
188 store_name = ua->argv[i];
193 ua->send_msg(_("Pool specified twice.\n"));
196 pool_name = ua->argv[i];
201 ua->send_msg(_("Where specified twice.\n"));
205 if (!acl_access_ok(ua, Where_ACL, where)) {
206 ua->send_msg(_("Forbidden \"where\" specified.\n"));
211 case 10: /* bootstrap */
213 ua->send_msg(_("Bootstrap specified twice.\n"));
216 bootstrap = ua->argv[i];
219 case 11: /* replace */
221 ua->send_msg(_("Replace specified twice.\n"));
224 replace = ua->argv[i];
229 ua->send_msg(_("When specified twice.\n"));
235 case 13: /* Priority */
237 ua->send_msg(_("Priority specified twice.\n"));
240 Priority = atoi(ua->argv[i]);
242 ua->send_msg(_("Priority must be positive nonzero setting it to 10.\n"));
250 case 15: /* Verify Job */
251 if (verify_job_name) {
252 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->error_msg(_("Catalog \"%s\" not found\n"), catalog_name);
325 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
326 ua->error_msg(_("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 ua->send_msg(_("Job \"%s\" not found\n"), job_name);
339 job = select_job_resource(ua);
341 Dmsg1(800, "Found job=%s\n", job_name);
344 ua->send_msg(_("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 ua->error_msg( _("No authorization. Job \"%s\".\n"),
356 pool = (POOL *)GetResWithName(R_POOL, pool_name);
358 if (*pool_name != 0) {
359 ua->warning_msg(_("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 ua->error_msg(_("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 ua->warning_msg(_("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 ua->error_msg(_("No storage specified.\n"));
391 } else if (!acl_access_ok(ua, Storage_ACL, store.store->name())) {
392 ua->error_msg(_("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 ua->warning_msg(_("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 ua->error_msg(_("No authorization. Client \"%s\".\n"),
416 Dmsg1(800, "Using client=%s\n", client->name());
419 fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
421 ua->send_msg(_("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 ua->send_msg(_("No authorization. FileSet \"%s\".\n"),
435 if (verify_job_name) {
436 verify_job = (JOB *)GetResWithName(R_JOB, verify_job_name);
438 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg(_("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 ua->send_msg("<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 ua->error_msg(_("Job failed.\n"));
767 ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId,ed1));
773 ua->send_msg(_("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 ua->warning_msg(_("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 if (ua->api) ua->signal(BNET_RUN_CMD);
847 ua->send_msg(_("Run %s job\n"
856 jcr->fileset->name(),
857 NPRT(jcr->client->name()),
858 jcr->wstore?jcr->wstore->name():"*None*",
859 bstrutime(dt, sizeof(dt), jcr->sched_time),
861 jcr->JobLevel = L_FULL;
865 if (jcr->JobType == JT_BACKUP) {
866 if (ua->api) ua->signal(BNET_RUN_CMD);
867 ua->send_msg(_("Run %s job\n"
872 "Pool: %s (From %s)\n"
873 "Storage: %s (From %s)\n"
878 level_to_str(jcr->JobLevel),
880 jcr->fileset->name(),
881 NPRT(jcr->pool->name()), jcr->pool_source,
882 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
883 bstrutime(dt, sizeof(dt), jcr->sched_time),
885 } else { /* JT_VERIFY */
887 if (jcr->verify_job) {
888 Name = jcr->verify_job->name();
893 verify_list = job->WriteVerifyList;
898 if (ua->api) ua->signal(BNET_RUN_CMD);
899 ua->send_msg(_("Run %s job\n"
904 "Pool: %s (From %s)\n"
905 "Storage: %s (From %s)\n"
912 level_to_str(jcr->JobLevel),
914 jcr->fileset->name(),
915 NPRT(jcr->pool->name()), jcr->pool_source,
916 jcr->rstore->name(), jcr->rstore_source,
919 bstrutime(dt, sizeof(dt), jcr->sched_time),
924 if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) {
926 jcr->RestoreJobId = str_to_int64(jid);
928 if (!get_pint(ua, _("Please enter a JobId for restore: "))) {
931 jcr->RestoreJobId = ua->int64_val;
934 jcr->JobLevel = L_FULL; /* default level */
935 Dmsg1(800, "JobId to restore=%d\n", jcr->RestoreJobId);
936 if (jcr->RestoreJobId == 0) {
937 if (ua->api) ua->signal(BNET_RUN_CMD);
938 ua->send_msg(_("Run Restore job\n"
950 NPRT(jcr->RestoreBootstrap),
951 jcr->where?jcr->where:NPRT(job->RestoreWhere),
953 jcr->fileset->name(),
956 bstrutime(dt, sizeof(dt), jcr->sched_time),
957 jcr->catalog->name(),
960 if (ua->api) ua->signal(BNET_RUN_CMD);
961 ua->send_msg(_("Run Restore job\n"
973 NPRT(jcr->RestoreBootstrap),
974 jcr->where?jcr->where:NPRT(job->RestoreWhere),
978 jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
979 bstrutime(dt, sizeof(dt), jcr->sched_time),
980 jcr->catalog->name(),
985 jcr->JobLevel = L_FULL; /* default level */
986 if (ua->api) ua->signal(BNET_RUN_CMD);
987 ua->send_msg(_("Run Migration job\n"
992 "Pool: %s (From %s)\n"
993 "Read Storage: %s (From %s)\n"
994 "Write Storage: %s (From %s)\n"
1000 NPRT(jcr->RestoreBootstrap),
1001 jcr->client->name(),
1002 jcr->fileset->name(),
1003 NPRT(jcr->pool->name()), jcr->pool_source,
1004 jcr->rstore->name(), jcr->rstore_source,
1005 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
1006 jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
1007 bstrutime(dt, sizeof(dt), jcr->sched_time),
1008 jcr->catalog->name(),
1012 ua->error_msg(_("Unknown Job Type=%d\n"), jcr->JobType);