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 amended 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 if (do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) {
155 store = (STORE *)GetResWithName(R_STORAGE, name);
160 * Select a FileSet resource from prompt list
162 FILESET *select_fileset_resource(UAContext *ua)
164 char name[MAX_NAME_LENGTH];
167 start_prompt(ua, _("The defined FileSet resources are:\n"));
169 foreach_res(fs, R_FILESET) {
170 if (acl_access_ok(ua, FileSet_ACL, fs->hdr.name)) {
171 add_prompt(ua, fs->hdr.name);
175 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) {
178 fs = (FILESET *)GetResWithName(R_FILESET, name);
184 * Get a catalog resource from prompt list
186 CAT *get_catalog_resource(UAContext *ua)
188 char name[MAX_NAME_LENGTH];
192 for (i=1; i<ua->argc; i++) {
193 if (strcasecmp(ua->argk[i], _("catalog")) == 0 && ua->argv[i]) {
194 if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
195 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
201 start_prompt(ua, _("The defined Catalog resources are:\n"));
203 foreach_res(catalog, R_CATALOG) {
204 if (acl_access_ok(ua, Catalog_ACL, catalog->hdr.name)) {
205 add_prompt(ua, catalog->hdr.name);
209 if (do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name)) < 0) {
212 catalog = (CAT *)GetResWithName(R_CATALOG, name);
219 * Select a Job resource from prompt list
221 JOB *select_job_resource(UAContext *ua)
223 char name[MAX_NAME_LENGTH];
226 start_prompt(ua, _("The defined Job resources are:\n"));
228 foreach_res(job, R_JOB) {
229 if (acl_access_ok(ua, Job_ACL, job->hdr.name)) {
230 add_prompt(ua, job->hdr.name);
234 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
237 job = (JOB *)GetResWithName(R_JOB, name);
242 * Select a Restore Job resource from prompt list
244 JOB *select_restore_job_resource(UAContext *ua)
246 char name[MAX_NAME_LENGTH];
249 start_prompt(ua, _("The defined Restore Job resources are:\n"));
251 foreach_res(job, R_JOB) {
252 if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->hdr.name)) {
253 add_prompt(ua, job->hdr.name);
257 if (do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
260 job = (JOB *)GetResWithName(R_JOB, name);
267 * Select a client resource from prompt list
269 CLIENT *select_client_resource(UAContext *ua)
271 char name[MAX_NAME_LENGTH];
274 start_prompt(ua, _("The defined Client resources are:\n"));
276 foreach_res(client, R_CLIENT) {
277 if (acl_access_ok(ua, Client_ACL, client->hdr.name)) {
278 add_prompt(ua, client->hdr.name);
282 if (do_prompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) {
285 client = (CLIENT *)GetResWithName(R_CLIENT, name);
290 * Get client resource, start by looking for
291 * client=<client-name>
292 * if we don't find the keyword, we prompt the user.
294 CLIENT *get_client_resource(UAContext *ua)
296 CLIENT *client = NULL;
299 for (i=1; i<ua->argc; i++) {
300 if ((strcasecmp(ua->argk[i], N_("client")) == 0 ||
301 strcasecmp(ua->argk[i], N_("fd")) == 0) && ua->argv[i]) {
302 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
305 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
309 bsendmsg(ua, _("Error: Client resource %s does not exist.\n"), ua->argv[i]);
313 return select_client_resource(ua);
316 /* Scan what the user has entered looking for:
318 * client=<client-name>
320 * if error or not found, put up a list of client DBRs
323 * returns: 0 on error
324 * 1 on success and fills in CLIENT_DBR
326 int get_client_dbr(UAContext *ua, CLIENT_DBR *cr)
330 if (cr->Name[0]) { /* If name already supplied */
331 if (db_get_client_record(ua->jcr, ua->db, cr)) {
334 bsendmsg(ua, _("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
336 for (i=1; i<ua->argc; i++) {
337 if ((strcasecmp(ua->argk[i], _("client")) == 0 ||
338 strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
339 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
342 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
343 if (!db_get_client_record(ua->jcr, ua->db, cr)) {
344 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
345 db_strerror(ua->db));
352 if (!select_client_dbr(ua, cr)) { /* try once more by proposing a list */
359 * Select a Client record from the catalog
360 * Returns 1 on success
363 int select_client_dbr(UAContext *ua, CLIENT_DBR *cr)
366 char name[MAX_NAME_LENGTH];
372 if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
373 bsendmsg(ua, _("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
376 if (num_clients <= 0) {
377 bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n"));
381 start_prompt(ua, _("Defined Clients:\n"));
382 for (i=0; i < num_clients; i++) {
383 ocr.ClientId = ids[i];
384 if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
385 !acl_access_ok(ua, Client_ACL, ocr.Name)) {
388 add_prompt(ua, ocr.Name);
391 if (do_prompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) < 0) {
394 memset(&ocr, 0, sizeof(ocr));
395 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
397 if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
398 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
401 memcpy(cr, &ocr, sizeof(ocr));
407 /* Scan what the user has entered looking for:
411 * if error or not found, put up a list of pool DBRs
414 * returns: false on error
415 * true on success and fills in POOL_DBR
417 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr)
419 if (pr->Name[0]) { /* If name already supplied */
420 if (db_get_pool_record(ua->jcr, ua->db, pr) &&
421 acl_access_ok(ua, Pool_ACL, pr->Name)) {
424 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
426 if (!select_pool_dbr(ua, pr)) { /* try once more */
433 * Select a Pool record from the catalog
435 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
438 char name[MAX_NAME_LENGTH];
442 for (i=1; i<ua->argc; i++) {
443 if (strcasecmp(ua->argk[i], N_("pool")) == 0 && ua->argv[i] &&
444 acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
445 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
446 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
447 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
448 db_strerror(ua->db));
457 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
458 bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
461 if (num_pools <= 0) {
462 bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
466 start_prompt(ua, _("Defined Pools:\n"));
467 for (i=0; i < num_pools; i++) {
469 if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
470 !acl_access_ok(ua, Pool_ACL, opr.Name)) {
473 add_prompt(ua, opr.Name);
476 if (do_prompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
479 memset(&opr, 0, sizeof(opr));
480 bstrncpy(opr.Name, name, sizeof(opr.Name));
482 if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
483 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
486 memcpy(pr, &opr, sizeof(opr));
491 * Select a Pool and a Media (Volume) record from the database
493 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
496 if (!select_media_dbr(ua, mr)) {
499 memset(pr, 0, sizeof(POOL_DBR));
500 pr->PoolId = mr->PoolId;
501 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
502 bsendmsg(ua, "%s", db_strerror(ua->db));
505 if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
506 bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
512 /* Select a Media (Volume) record from the database */
513 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
517 memset(mr, 0, sizeof(MEDIA_DBR));
519 i = find_arg_with_value(ua, "volume");
521 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
523 if (mr->VolumeName[0] == 0) {
525 memset(&pr, 0, sizeof(pr));
526 /* Get the pool from pool=<pool-name> */
527 if (!get_pool_dbr(ua, &pr)) {
530 mr->PoolId = pr.PoolId;
531 db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
532 if (!get_cmd(ua, _("Enter MediaId or Volume name: "))) {
535 if (is_a_number(ua->cmd)) {
536 mr->MediaId = str_to_int64(ua->cmd);
538 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
542 if (!db_get_media_record(ua->jcr, ua->db, mr)) {
543 bsendmsg(ua, "%s", db_strerror(ua->db));
551 * Select a pool resource from prompt list
553 POOL *select_pool_resource(UAContext *ua)
555 char name[MAX_NAME_LENGTH];
558 start_prompt(ua, _("The defined Pool resources are:\n"));
560 foreach_res(pool, R_POOL) {
561 if (acl_access_ok(ua, Pool_ACL, pool->hdr.name)) {
562 add_prompt(ua, pool->hdr.name);
566 if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
569 pool = (POOL *)GetResWithName(R_POOL, name);
575 * If you are thinking about using it, you
576 * probably want to use select_pool_dbr()
577 * or get_pool_dbr() above.
579 POOL *get_pool_resource(UAContext *ua)
584 i = find_arg_with_value(ua, "pool");
585 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
586 pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
590 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
592 return select_pool_resource(ua);
596 * List all jobs and ask user to select one
598 int select_job_dbr(UAContext *ua, JOB_DBR *jr)
600 db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
601 if (!get_pint(ua, _("Enter the JobId to select: "))) {
604 jr->JobId = ua->int64_val;
605 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
606 bsendmsg(ua, "%s", db_strerror(ua->db));
614 /* Scan what the user has entered looking for:
618 * if error or not found, put up a list of Jobs
621 * returns: 0 on error
622 * JobId on success and fills in JOB_DBR
624 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
628 for (i=1; i<ua->argc; i++) {
629 if (strcasecmp(ua->argk[i], N_("job")) == 0 && ua->argv[i]) {
631 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
632 } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0 && ua->argv[i]) {
633 jr->JobId = str_to_int64(ua->argv[i]);
637 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
638 bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
639 db_strerror(ua->db));
646 if (!select_job_dbr(ua, jr)) { /* try once more */
653 * Implement unique set of prompts
655 void start_prompt(UAContext *ua, const char *msg)
657 if (ua->max_prompts == 0) {
658 ua->max_prompts = 10;
659 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
662 ua->prompt[0] = bstrdup(msg);
666 * Add to prompts -- keeping them unique
668 void add_prompt(UAContext *ua, const char *prompt)
671 if (ua->num_prompts == ua->max_prompts) {
672 ua->max_prompts *= 2;
673 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
676 for (i=1; i < ua->num_prompts; i++) {
677 if (strcmp(ua->prompt[i], prompt) == 0) {
681 ua->prompt[ua->num_prompts++] = bstrdup(prompt);
685 * Display prompts and get user's choice
687 * Returns: -1 on error
688 * index base 0 on success, and choice
689 * is copied to prompt if not NULL
690 * prompt is set to the chosen prompt item string
692 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
695 char pmsg[MAXSTRING];
700 if (ua->num_prompts == 2) {
703 bstrncpy(prompt, ua->prompt[1], max_prompt);
705 bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
708 /* If running non-interactive, bail out */
710 bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
714 bsendmsg(ua, ua->prompt[0]);
715 for (i=1; i < ua->num_prompts; i++) {
716 bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
720 /* First item is the prompt string, not the items */
721 if (ua->num_prompts == 1) {
722 bsendmsg(ua, _("Selection is empty!\n"));
723 item = -1; /* list is empty ! */
726 if (ua->num_prompts == 2) {
728 bsendmsg(ua, _("Item 1 selected automatically.\n"));
730 bstrncpy(prompt, ua->prompt[1], max_prompt);
734 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
736 /* Either a . or an @ will get you out of the loop */
737 if (!get_pint(ua, pmsg)) {
738 item = -1; /* error */
739 bsendmsg(ua, _("Selection aborted, nothing done.\n"));
742 item = ua->pint32_val;
743 if (item < 1 || item >= ua->num_prompts) {
744 bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
748 bstrncpy(prompt, ua->prompt[item], max_prompt);
754 for (i=0; i < ua->num_prompts; i++) {
758 return item>0 ? item-1 : item;
763 * We scan what the user has entered looking for
764 * storage=<storage-resource>
767 * ? (prompt him with storage list)
768 * <some-error> (prompt him with storage list)
770 * If use_default is set, we assume that any keyword without a value
771 * is the name of the Storage resource wanted.
773 STORE *get_storage_resource(UAContext *ua, bool use_default)
775 char *store_name = NULL;
782 for (i=1; i<ua->argc; i++) {
783 if (use_default && !ua->argv[i]) {
784 /* Ignore slots, scan and barcode(s) keywords */
785 if (strcasecmp("scan", ua->argk[i]) == 0 ||
786 strcasecmp("barcode", ua->argk[i]) == 0 ||
787 strcasecmp("barcodes", ua->argk[i]) == 0 ||
788 strcasecmp("slots", ua->argk[i]) == 0) {
791 /* Default argument is storage */
793 bsendmsg(ua, _("Storage name given twice.\n"));
796 store_name = ua->argk[i];
797 if (*store_name == '?') {
802 if (strcasecmp(ua->argk[i], N_("storage")) == 0 ||
803 strcasecmp(ua->argk[i], N_("sd")) == 0) {
804 store_name = ua->argv[i];
807 } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
808 jobid = str_to_int64(ua->argv[i]);
810 bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
813 if (!(jcr=get_jcr_by_id(jobid))) {
814 bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
821 } else if (strcasecmp(ua->argk[i], N_("job")) == 0) {
823 bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
826 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
827 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
836 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
840 if (!store && store_name && store_name[0] != 0) {
841 store = (STORE *)GetResWithName(R_STORAGE, store_name);
843 bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
846 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
849 /* No keywords found, so present a selection list */
851 store = select_storage_resource(ua);
856 /* Get drive that we are working with for this storage */
857 int get_storage_drive(UAContext *ua, STORE *store)
860 /* Get drive for autochanger if possible */
861 i = find_arg_with_value(ua, "drive");
863 drive = atoi(ua->argv[i]);
864 } else if (store && store->autochanger) {
865 /* If our structure is not set ask SD for # drives */
866 if (store->drives == 0) {
867 store->drives = get_num_drives_from_SD(ua);
869 /* If only one drive, default = 0 */
870 if (store->drives == 1) {
873 /* Ask user to enter drive number */
875 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
876 drive = -1; /* None */
878 drive = atoi(ua->cmd);
887 * Scan looking for mediatype=
889 * if not found or error, put up selection list
891 * Returns: 0 on error
892 * 1 on success, MediaType is set
894 int get_media_type(UAContext *ua, char *MediaType, int max_media)
899 i = find_arg_with_value(ua, "mediatype");
901 bstrncpy(MediaType, ua->argv[i], max_media);
905 start_prompt(ua, _("Media Types defined in conf file:\n"));
907 foreach_res(store, R_STORAGE) {
908 add_prompt(ua, store->media_type);
911 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
914 bool get_level_from_name(JCR *jcr, const char *level_name)
916 /* Look up level name and pull code */
918 for (int i=0; joblevels[i].level_name; i++) {
919 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
920 jcr->JobLevel = joblevels[i].level;