3 * Bacula Director -- User Agent Prompt and Selection code
5 * Kern Sibbald, October MMI
10 Copyright (C) 2001-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as ammended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Imported variables */
28 extern struct s_jl joblevels[];
32 * Confirm a retention period
34 int confirm_retention(UAContext *ua, utime_t *ret, const char *msg)
39 bsendmsg(ua, _("The current %s retention period is: %s\n"),
40 msg, edit_utime(*ret, ed1, sizeof(ed1)));
41 if (!get_cmd(ua, _("Continue? (yes/mod/no): "))) {
44 if (strcasecmp(ua->cmd, _("mod")) == 0) {
45 if (!get_cmd(ua, _("Enter new retention period: "))) {
48 if (!duration_to_utime(ua->cmd, ret)) {
49 bsendmsg(ua, _("Invalid period.\n"));
54 if (strcasecmp(ua->cmd, _("yes")) == 0) {
57 if (strcasecmp(ua->cmd, _("no")) == 0) {
65 * Given a list of keywords, find the first one
66 * that is in the argument list.
67 * Returns: -1 if not found
68 * index into list (base 0) on success
70 int find_arg_keyword(UAContext *ua, const char **list)
72 for (int i=1; i<ua->argc; i++) {
73 for(int j=0; list[j]; j++) {
74 if (strcasecmp(_(list[j]), ua->argk[i]) == 0) {
83 * Given one keyword, find the first one that
84 * is in the argument list.
85 * Returns: argk index (always gt 0)
88 int find_arg(UAContext *ua, const char *keyword)
90 for (int i=1; i<ua->argc; i++) {
91 if (strcasecmp(keyword, ua->argk[i]) == 0) {
99 * Given a single keyword, find it in the argument list, but
100 * it must have a value
101 * Returns: -1 if not found or no value
102 * list index (base 0) on success
104 int find_arg_with_value(UAContext *ua, const char *keyword)
106 for (int i=1; i<ua->argc; i++) {
107 if (strcasecmp(keyword, ua->argk[i]) == 0) {
119 * Given a list of keywords, prompt the user
122 * Returns: -1 on failure
123 * index into list (base 0) on success
125 int do_keyword_prompt(UAContext *ua, const char *msg, const char **list)
128 start_prompt(ua, _("You have the following choices:\n"));
129 for (i=0; list[i]; i++) {
130 add_prompt(ua, list[i]);
132 return do_prompt(ua, "", msg, NULL, 0);
137 * Select a Storage resource from prompt list
139 STORE *select_storage_resource(UAContext *ua)
141 char name[MAX_NAME_LENGTH];
144 start_prompt(ua, _("The defined Storage resources are:\n"));
146 foreach_res(store, R_STORAGE) {
147 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
148 add_prompt(ua, store->hdr.name);
152 do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name));
153 store = (STORE *)GetResWithName(R_STORAGE, name);
158 * Select a FileSet resource from prompt list
160 FILESET *select_fileset_resource(UAContext *ua)
162 char name[MAX_NAME_LENGTH];
165 start_prompt(ua, _("The defined FileSet resources are:\n"));
167 foreach_res(fs, R_FILESET) {
168 if (acl_access_ok(ua, FileSet_ACL, fs->hdr.name)) {
169 add_prompt(ua, fs->hdr.name);
173 do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name));
174 fs = (FILESET *)GetResWithName(R_FILESET, name);
180 * Get a catalog resource from prompt list
182 CAT *get_catalog_resource(UAContext *ua)
184 char name[MAX_NAME_LENGTH];
188 for (i=1; i<ua->argc; i++) {
189 if (strcasecmp(ua->argk[i], _("catalog")) == 0 && ua->argv[i]) {
190 if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
191 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
197 start_prompt(ua, _("The defined Catalog resources are:\n"));
199 foreach_res(catalog, R_CATALOG) {
200 if (acl_access_ok(ua, Catalog_ACL, catalog->hdr.name)) {
201 add_prompt(ua, catalog->hdr.name);
205 do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name));
206 catalog = (CAT *)GetResWithName(R_CATALOG, name);
213 * Select a Job resource from prompt list
215 JOB *select_job_resource(UAContext *ua)
217 char name[MAX_NAME_LENGTH];
220 start_prompt(ua, _("The defined Job resources are:\n"));
222 foreach_res(job, R_JOB) {
223 if (acl_access_ok(ua, Job_ACL, job->hdr.name)) {
224 add_prompt(ua, job->hdr.name);
228 do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name));
229 job = (JOB *)GetResWithName(R_JOB, name);
234 * Select a Restore Job resource from prompt list
236 JOB *select_restore_job_resource(UAContext *ua)
238 char name[MAX_NAME_LENGTH];
241 start_prompt(ua, _("The defined Restore Job resources are:\n"));
243 foreach_res(job, R_JOB) {
244 if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->hdr.name)) {
245 add_prompt(ua, job->hdr.name);
249 do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name));
250 job = (JOB *)GetResWithName(R_JOB, name);
257 * Select a client resource from prompt list
259 CLIENT *select_client_resource(UAContext *ua)
261 char name[MAX_NAME_LENGTH];
264 start_prompt(ua, _("The defined Client resources are:\n"));
266 foreach_res(client, R_CLIENT) {
267 if (acl_access_ok(ua, Client_ACL, client->hdr.name)) {
268 add_prompt(ua, client->hdr.name);
272 do_prompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name));
273 client = (CLIENT *)GetResWithName(R_CLIENT, name);
278 * Get client resource, start by looking for
279 * client=<client-name>
280 * if we don't find the keyword, we prompt the user.
282 CLIENT *get_client_resource(UAContext *ua)
284 CLIENT *client = NULL;
287 for (i=1; i<ua->argc; i++) {
288 if ((strcasecmp(ua->argk[i], _("client")) == 0 ||
289 strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
290 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
293 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
297 bsendmsg(ua, _("Error: Client resource %s does not exist.\n"), ua->argv[i]);
301 return select_client_resource(ua);
304 /* Scan what the user has entered looking for:
306 * client=<client-name>
308 * if error or not found, put up a list of client DBRs
311 * returns: 0 on error
312 * 1 on success and fills in CLIENT_DBR
314 int get_client_dbr(UAContext *ua, CLIENT_DBR *cr)
318 if (cr->Name[0]) { /* If name already supplied */
319 if (db_get_client_record(ua->jcr, ua->db, cr)) {
322 bsendmsg(ua, _("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
324 for (i=1; i<ua->argc; i++) {
325 if ((strcasecmp(ua->argk[i], _("client")) == 0 ||
326 strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
327 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
330 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
331 if (!db_get_client_record(ua->jcr, ua->db, cr)) {
332 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
333 db_strerror(ua->db));
340 if (!select_client_dbr(ua, cr)) { /* try once more by proposing a list */
347 * Select a Client record from the catalog
348 * Returns 1 on success
351 int select_client_dbr(UAContext *ua, CLIENT_DBR *cr)
354 char name[MAX_NAME_LENGTH];
360 if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
361 bsendmsg(ua, _("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
364 if (num_clients <= 0) {
365 bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n"));
369 start_prompt(ua, _("Defined Clients:\n"));
370 for (i=0; i < num_clients; i++) {
371 ocr.ClientId = ids[i];
372 if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
373 !acl_access_ok(ua, Client_ACL, ocr.Name)) {
376 add_prompt(ua, ocr.Name);
379 if (do_prompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) < 0) {
382 memset(&ocr, 0, sizeof(ocr));
383 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
385 if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
386 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
389 memcpy(cr, &ocr, sizeof(ocr));
395 /* Scan what the user has entered looking for:
399 * if error or not found, put up a list of pool DBRs
402 * returns: false on error
403 * true on success and fills in POOL_DBR
405 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr)
407 if (pr->Name[0]) { /* If name already supplied */
408 if (db_get_pool_record(ua->jcr, ua->db, pr) &&
409 acl_access_ok(ua, Pool_ACL, pr->Name)) {
412 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
414 if (!select_pool_dbr(ua, pr)) { /* try once more */
421 * Select a Pool record from the catalog
423 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
426 char name[MAX_NAME_LENGTH];
430 for (i=1; i<ua->argc; i++) {
431 if (strcasecmp(ua->argk[i], _("pool")) == 0 && ua->argv[i] &&
432 acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
433 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
434 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
435 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
436 db_strerror(ua->db));
445 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
446 bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
449 if (num_pools <= 0) {
450 bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
454 start_prompt(ua, _("Defined Pools:\n"));
455 for (i=0; i < num_pools; i++) {
457 if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
458 !acl_access_ok(ua, Pool_ACL, opr.Name)) {
461 add_prompt(ua, opr.Name);
464 if (do_prompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
467 memset(&opr, 0, sizeof(opr));
468 bstrncpy(opr.Name, name, sizeof(opr.Name));
470 if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
471 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
474 memcpy(pr, &opr, sizeof(opr));
479 * Select a Pool and a Media (Volume) record from the database
481 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
484 if (!select_media_dbr(ua, mr)) {
487 memset(pr, 0, sizeof(POOL_DBR));
488 pr->PoolId = mr->PoolId;
489 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
490 bsendmsg(ua, "%s", db_strerror(ua->db));
493 if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
494 bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
500 /* Select a Media (Volume) record from the database */
501 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
505 memset(mr, 0, sizeof(MEDIA_DBR));
507 i = find_arg_with_value(ua, "volume");
509 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
511 if (mr->VolumeName[0] == 0) {
513 memset(&pr, 0, sizeof(pr));
514 /* Get the pool from pool=<pool-name> */
515 if (!get_pool_dbr(ua, &pr)) {
518 mr->PoolId = pr.PoolId;
519 db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
520 if (!get_cmd(ua, _("Enter MediaId or Volume name: "))) {
523 if (is_a_number(ua->cmd)) {
524 mr->MediaId = str_to_int64(ua->cmd);
526 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
530 if (!db_get_media_record(ua->jcr, ua->db, mr)) {
531 bsendmsg(ua, "%s", db_strerror(ua->db));
539 * Select a pool resource from prompt list
541 POOL *select_pool_resource(UAContext *ua)
543 char name[MAX_NAME_LENGTH];
546 start_prompt(ua, _("The defined Pool resources are:\n"));
548 foreach_res(pool, R_POOL) {
549 if (acl_access_ok(ua, Pool_ACL, pool->hdr.name)) {
550 add_prompt(ua, pool->hdr.name);
554 do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name));
555 pool = (POOL *)GetResWithName(R_POOL, name);
561 * If you are thinking about using it, you
562 * probably want to use select_pool_dbr()
563 * or get_pool_dbr() above.
565 POOL *get_pool_resource(UAContext *ua)
570 i = find_arg_with_value(ua, "pool");
571 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
572 pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
576 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
578 return select_pool_resource(ua);
582 * List all jobs and ask user to select one
584 int select_job_dbr(UAContext *ua, JOB_DBR *jr)
586 db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
587 if (!get_pint(ua, _("Enter the JobId to select: "))) {
590 jr->JobId = ua->int64_val;
591 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
592 bsendmsg(ua, "%s", db_strerror(ua->db));
600 /* Scan what the user has entered looking for:
604 * if error or not found, put up a list of Jobs
607 * returns: 0 on error
608 * JobId on success and fills in JOB_DBR
610 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
614 for (i=1; i<ua->argc; i++) {
615 if (strcasecmp(ua->argk[i], _("job")) == 0 && ua->argv[i]) {
617 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
618 } else if (strcasecmp(ua->argk[i], _("jobid")) == 0 && ua->argv[i]) {
619 jr->JobId = str_to_int64(ua->argv[i]);
623 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
624 bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
625 db_strerror(ua->db));
632 if (!select_job_dbr(ua, jr)) { /* try once more */
639 * Implement unique set of prompts
641 void start_prompt(UAContext *ua, const char *msg)
643 if (ua->max_prompts == 0) {
644 ua->max_prompts = 10;
645 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
648 ua->prompt[0] = bstrdup(msg);
652 * Add to prompts -- keeping them unique
654 void add_prompt(UAContext *ua, const char *prompt)
657 if (ua->num_prompts == ua->max_prompts) {
658 ua->max_prompts *= 2;
659 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
662 for (i=1; i < ua->num_prompts; i++) {
663 if (strcmp(ua->prompt[i], prompt) == 0) {
667 ua->prompt[ua->num_prompts++] = bstrdup(prompt);
671 * Display prompts and get user's choice
673 * Returns: -1 on error
674 * index base 0 on success, and choice
675 * is copied to prompt if not NULL
677 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
680 char pmsg[MAXSTRING];
682 if (ua->num_prompts == 2) {
685 bstrncpy(prompt, ua->prompt[1], max_prompt);
687 bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
690 /* If running non-interactive, bail out */
692 bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
696 bsendmsg(ua, ua->prompt[0]);
697 for (i=1; i < ua->num_prompts; i++) {
698 bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
706 /* First item is the prompt string, not the items */
707 if (ua->num_prompts == 1) {
708 bsendmsg(ua, _("Selection is empty!\n"));
709 item = 0; /* list is empty ! */
712 if (ua->num_prompts == 2) {
714 bsendmsg(ua, _("Item 1 selected automatically.\n"));
716 bstrncpy(prompt, ua->prompt[1], max_prompt);
720 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
722 /* Either a . or an @ will get you out of the loop */
723 if (!get_pint(ua, pmsg)) {
724 item = -1; /* error */
725 bsendmsg(ua, _("Selection aborted, nothing done.\n"));
728 item = ua->pint32_val;
729 if (item < 1 || item >= ua->num_prompts) {
730 bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
734 bstrncpy(prompt, ua->prompt[item], max_prompt);
740 for (i=0; i < ua->num_prompts; i++) {
749 * We scan what the user has entered looking for
750 * storage=<storage-resource>
753 * ? (prompt him with storage list)
754 * <some-error> (prompt him with storage list)
756 * If use_default is set, we assume that any keyword without a value
757 * is the name of the Storage resource wanted.
759 STORE *get_storage_resource(UAContext *ua, int use_default)
761 char *store_name = NULL;
769 for (i=1; i<ua->argc; i++) {
770 if (use_default && !ua->argv[i]) {
771 /* Ignore slots, scan and barcode(s) keywords */
772 if (strncasecmp("scan", ua->argk[i], 4) == 0 ||
773 strncasecmp("barcode", ua->argk[i], 7) == 0 ||
774 strncasecmp("slots", ua->argk[i], 5) == 0) {
777 /* Default argument is storage */
779 bsendmsg(ua, _("Storage name given twice.\n"));
782 store_name = ua->argk[i];
783 if (*store_name == '?') {
788 if (strcasecmp(ua->argk[i], _("storage")) == 0 ||
789 strcasecmp(ua->argk[i], _("sd")) == 0) {
790 store_name = ua->argv[i];
793 } else if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
794 jobid = str_to_int64(ua->argv[i]);
796 bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
799 if (!(jcr=get_jcr_by_id(jobid))) {
800 bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
807 } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
809 bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
812 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
813 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
822 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
826 if (!store && store_name) {
827 store = (STORE *)GetResWithName(R_STORAGE, store_name);
829 bsendmsg(ua, "Storage resource \"%s\": not found\n", store_name);
832 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
835 /* No keywords found, so present a selection list */
837 store = select_storage_resource(ua);
844 * Scan looking for mediatype=
846 * if not found or error, put up selection list
848 * Returns: 0 on error
849 * 1 on success, MediaType is set
851 int get_media_type(UAContext *ua, char *MediaType, int max_media)
856 i = find_arg_with_value(ua, "mediatype");
858 bstrncpy(MediaType, ua->argv[i], max_media);
862 start_prompt(ua, _("Media Types defined in conf file:\n"));
864 foreach_res(store, R_STORAGE) {
865 add_prompt(ua, store->media_type);
868 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
871 bool get_level_from_name(JCR *jcr, const char *level_name)
873 /* Look up level name and pull code */
875 for (int i=0; joblevels[i].level_name; i++) {
876 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
877 jcr->JobLevel = joblevels[i].level;