3 * Bacula Director -- User Agent Prompt and Selection code
5 * Kern Sibbald, October MMI
10 Copyright (C) 2001-2006 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)
40 bsendmsg(ua, _("The current %s retention period is: %s\n"),
41 msg, edit_utime(*ret, ed1, sizeof(ed1)));
42 if (!get_cmd(ua, _("Continue? (yes/mod/no): "))) {
45 if (strcasecmp(ua->cmd, _("mod")) == 0) {
46 if (!get_cmd(ua, _("Enter new retention period: "))) {
49 if (!duration_to_utime(ua->cmd, ret)) {
50 bsendmsg(ua, _("Invalid period.\n"));
55 if (is_yesno(ua->cmd, &val)) {
56 return val; /* is 1 for yes, 0 for no */
63 * Given a list of keywords, find the first one
64 * that is in the argument list.
65 * Returns: -1 if not found
66 * index into list (base 0) on success
68 int find_arg_keyword(UAContext *ua, const char **list)
70 for (int i=1; i<ua->argc; i++) {
71 for(int j=0; list[j]; j++) {
72 if (strcasecmp(list[j], ua->argk[i]) == 0) {
81 * Given one keyword, find the first one that
82 * is in the argument list.
83 * Returns: argk index (always gt 0)
86 int find_arg(UAContext *ua, const char *keyword)
88 for (int i=1; i<ua->argc; i++) {
89 if (strcasecmp(keyword, ua->argk[i]) == 0) {
97 * Given a single keyword, find it in the argument list, but
98 * it must have a value
99 * Returns: -1 if not found or no value
100 * list index (base 0) on success
102 int find_arg_with_value(UAContext *ua, const char *keyword)
104 for (int i=1; i<ua->argc; i++) {
105 if (strcasecmp(keyword, ua->argk[i]) == 0) {
117 * Given a list of keywords, prompt the user
120 * Returns: -1 on failure
121 * index into list (base 0) on success
123 int do_keyword_prompt(UAContext *ua, const char *msg, const char **list)
126 start_prompt(ua, _("You have the following choices:\n"));
127 for (i=0; list[i]; i++) {
128 add_prompt(ua, list[i]);
130 return do_prompt(ua, "", msg, NULL, 0);
135 * Select a Storage resource from prompt list
137 STORE *select_storage_resource(UAContext *ua)
139 char name[MAX_NAME_LENGTH];
142 start_prompt(ua, _("The defined Storage resources are:\n"));
144 foreach_res(store, R_STORAGE) {
145 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
146 add_prompt(ua, store->hdr.name);
150 if (do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) {
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 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) {
176 fs = (FILESET *)GetResWithName(R_FILESET, name);
182 * Get a catalog resource from prompt list
184 CAT *get_catalog_resource(UAContext *ua)
186 char name[MAX_NAME_LENGTH];
190 for (i=1; i<ua->argc; i++) {
191 if (strcasecmp(ua->argk[i], NT_("catalog")) == 0 && ua->argv[i]) {
192 if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
193 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
199 start_prompt(ua, _("The defined Catalog resources are:\n"));
201 foreach_res(catalog, R_CATALOG) {
202 if (acl_access_ok(ua, Catalog_ACL, catalog->hdr.name)) {
203 add_prompt(ua, catalog->hdr.name);
207 if (do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name)) < 0) {
210 catalog = (CAT *)GetResWithName(R_CATALOG, name);
217 * Select a Job resource from prompt list
219 JOB *select_job_resource(UAContext *ua)
221 char name[MAX_NAME_LENGTH];
224 start_prompt(ua, _("The defined Job resources are:\n"));
226 foreach_res(job, R_JOB) {
227 if (acl_access_ok(ua, Job_ACL, job->hdr.name)) {
228 add_prompt(ua, job->hdr.name);
232 if (do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) {
235 job = (JOB *)GetResWithName(R_JOB, name);
240 * Select a Restore Job resource from prompt list
242 JOB *select_restore_job_resource(UAContext *ua)
244 char name[MAX_NAME_LENGTH];
247 start_prompt(ua, _("The defined Restore Job resources are:\n"));
249 foreach_res(job, R_JOB) {
250 if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->hdr.name)) {
251 add_prompt(ua, job->hdr.name);
255 if (do_prompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) {
258 job = (JOB *)GetResWithName(R_JOB, name);
265 * Select a client resource from prompt list
267 CLIENT *select_client_resource(UAContext *ua)
269 char name[MAX_NAME_LENGTH];
272 start_prompt(ua, _("The defined Client resources are:\n"));
274 foreach_res(client, R_CLIENT) {
275 if (acl_access_ok(ua, Client_ACL, client->hdr.name)) {
276 add_prompt(ua, client->hdr.name);
280 if (do_prompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) {
283 client = (CLIENT *)GetResWithName(R_CLIENT, name);
288 * Get client resource, start by looking for
289 * client=<client-name>
290 * if we don't find the keyword, we prompt the user.
292 CLIENT *get_client_resource(UAContext *ua)
294 CLIENT *client = NULL;
297 for (i=1; i<ua->argc; i++) {
298 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
299 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
300 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
303 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
307 bsendmsg(ua, _("Error: Client resource %s does not exist.\n"), ua->argv[i]);
311 return select_client_resource(ua);
314 /* Scan what the user has entered looking for:
316 * client=<client-name>
318 * if error or not found, put up a list of client DBRs
321 * returns: 0 on error
322 * 1 on success and fills in CLIENT_DBR
324 int get_client_dbr(UAContext *ua, CLIENT_DBR *cr)
328 if (cr->Name[0]) { /* If name already supplied */
329 if (db_get_client_record(ua->jcr, ua->db, cr)) {
332 bsendmsg(ua, _("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db));
334 for (i=1; i<ua->argc; i++) {
335 if ((strcasecmp(ua->argk[i], NT_("client")) == 0 ||
336 strcasecmp(ua->argk[i], NT_("fd")) == 0) && ua->argv[i]) {
337 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
340 bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
341 if (!db_get_client_record(ua->jcr, ua->db, cr)) {
342 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
343 db_strerror(ua->db));
350 if (!select_client_dbr(ua, cr)) { /* try once more by proposing a list */
357 * Select a Client record from the catalog
358 * Returns 1 on success
361 int select_client_dbr(UAContext *ua, CLIENT_DBR *cr)
364 char name[MAX_NAME_LENGTH];
370 if (!db_get_client_ids(ua->jcr, ua->db, &num_clients, &ids)) {
371 bsendmsg(ua, _("Error obtaining client ids. ERR=%s\n"), db_strerror(ua->db));
374 if (num_clients <= 0) {
375 bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n"));
379 start_prompt(ua, _("Defined Clients:\n"));
380 for (i=0; i < num_clients; i++) {
381 ocr.ClientId = ids[i];
382 if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
383 !acl_access_ok(ua, Client_ACL, ocr.Name)) {
386 add_prompt(ua, ocr.Name);
389 if (do_prompt(ua, _("Client"), _("Select the Client"), name, sizeof(name)) < 0) {
392 memset(&ocr, 0, sizeof(ocr));
393 bstrncpy(ocr.Name, name, sizeof(ocr.Name));
395 if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
396 bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), name, db_strerror(ua->db));
399 memcpy(cr, &ocr, sizeof(ocr));
405 /* Scan what the user has entered looking for:
409 * if error or not found, put up a list of pool DBRs
412 * returns: false on error
413 * true on success and fills in POOL_DBR
415 bool get_pool_dbr(UAContext *ua, POOL_DBR *pr)
417 if (pr->Name[0]) { /* If name already supplied */
418 if (db_get_pool_record(ua->jcr, ua->db, pr) &&
419 acl_access_ok(ua, Pool_ACL, pr->Name)) {
422 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
424 if (!select_pool_dbr(ua, pr)) { /* try once more */
431 * Select a Pool record from the catalog
433 bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
436 char name[MAX_NAME_LENGTH];
440 for (i=1; i<ua->argc; i++) {
441 if (strcasecmp(ua->argk[i], NT_("pool")) == 0 && ua->argv[i] &&
442 acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
443 bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
444 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
445 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
446 db_strerror(ua->db));
455 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
456 bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
459 if (num_pools <= 0) {
460 bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
464 start_prompt(ua, _("Defined Pools:\n"));
465 for (i=0; i < num_pools; i++) {
467 if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
468 !acl_access_ok(ua, Pool_ACL, opr.Name)) {
471 add_prompt(ua, opr.Name);
474 if (do_prompt(ua, _("Pool"), _("Select the Pool"), name, sizeof(name)) < 0) {
477 memset(&opr, 0, sizeof(opr));
478 bstrncpy(opr.Name, name, sizeof(opr.Name));
480 if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
481 bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
484 memcpy(pr, &opr, sizeof(opr));
489 * Select a Pool and a Media (Volume) record from the database
491 int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
494 if (!select_media_dbr(ua, mr)) {
497 memset(pr, 0, sizeof(POOL_DBR));
498 pr->PoolId = mr->PoolId;
499 if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
500 bsendmsg(ua, "%s", db_strerror(ua->db));
503 if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
504 bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
510 /* Select a Media (Volume) record from the database */
511 int select_media_dbr(UAContext *ua, MEDIA_DBR *mr)
515 memset(mr, 0, sizeof(MEDIA_DBR));
517 i = find_arg_with_value(ua, "volume");
519 bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
521 if (mr->VolumeName[0] == 0) {
523 memset(&pr, 0, sizeof(pr));
524 /* Get the pool from pool=<pool-name> */
525 if (!get_pool_dbr(ua, &pr)) {
528 mr->PoolId = pr.PoolId;
529 db_list_media_records(ua->jcr, ua->db, mr, prtit, ua, HORZ_LIST);
530 if (!get_cmd(ua, _("Enter MediaId or Volume name: "))) {
533 if (is_a_number(ua->cmd)) {
534 mr->MediaId = str_to_int64(ua->cmd);
536 bstrncpy(mr->VolumeName, ua->cmd, sizeof(mr->VolumeName));
540 if (!db_get_media_record(ua->jcr, ua->db, mr)) {
541 bsendmsg(ua, "%s", db_strerror(ua->db));
549 * Select a pool resource from prompt list
551 POOL *select_pool_resource(UAContext *ua)
553 char name[MAX_NAME_LENGTH];
556 start_prompt(ua, _("The defined Pool resources are:\n"));
558 foreach_res(pool, R_POOL) {
559 if (acl_access_ok(ua, Pool_ACL, pool->hdr.name)) {
560 add_prompt(ua, pool->hdr.name);
564 if (do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) {
567 pool = (POOL *)GetResWithName(R_POOL, name);
573 * If you are thinking about using it, you
574 * probably want to use select_pool_dbr()
575 * or get_pool_dbr() above.
577 POOL *get_pool_resource(UAContext *ua)
582 i = find_arg_with_value(ua, "pool");
583 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
584 pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
588 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
590 return select_pool_resource(ua);
594 * List all jobs and ask user to select one
596 int select_job_dbr(UAContext *ua, JOB_DBR *jr)
598 db_list_job_records(ua->jcr, ua->db, jr, prtit, ua, HORZ_LIST);
599 if (!get_pint(ua, _("Enter the JobId to select: "))) {
602 jr->JobId = ua->int64_val;
603 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
604 bsendmsg(ua, "%s", db_strerror(ua->db));
612 /* Scan what the user has entered looking for:
616 * if error or not found, put up a list of Jobs
619 * returns: 0 on error
620 * JobId on success and fills in JOB_DBR
622 int get_job_dbr(UAContext *ua, JOB_DBR *jr)
626 for (i=1; i<ua->argc; i++) {
627 if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
629 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
630 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && ua->argv[i]) {
631 jr->JobId = str_to_int64(ua->argv[i]);
636 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
637 bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
638 db_strerror(ua->db));
648 for (i=1; i<ua->argc; i++) {
649 if ((strcasecmp(ua->argk[i], NT_("jobname")) == 0 ||
650 strcasecmp(ua->argk[i], NT_("job")) == 0) && ua->argv[i]) {
652 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
656 if (!select_job_dbr(ua, jr)) { /* try once more */
663 * Implement unique set of prompts
665 void start_prompt(UAContext *ua, const char *msg)
667 if (ua->max_prompts == 0) {
668 ua->max_prompts = 10;
669 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
672 ua->prompt[0] = bstrdup(msg);
676 * Add to prompts -- keeping them unique
678 void add_prompt(UAContext *ua, const char *prompt)
681 if (ua->num_prompts == ua->max_prompts) {
682 ua->max_prompts *= 2;
683 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
686 for (i=1; i < ua->num_prompts; i++) {
687 if (strcmp(ua->prompt[i], prompt) == 0) {
691 ua->prompt[ua->num_prompts++] = bstrdup(prompt);
695 * Display prompts and get user's choice
697 * Returns: -1 on error
698 * index base 0 on success, and choice
699 * is copied to prompt if not NULL
700 * prompt is set to the chosen prompt item string
702 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
705 char pmsg[MAXSTRING];
710 if (ua->num_prompts == 2) {
713 bstrncpy(prompt, ua->prompt[1], max_prompt);
715 bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
718 /* If running non-interactive, bail out */
720 bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
724 // bnet_sig(ua->UA_sock, BNET_START_SELECT);
725 bsendmsg(ua, ua->prompt[0]);
726 for (i=1; i < ua->num_prompts; i++) {
727 bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
729 // bnet_sig(ua->UA_sock, BNET_END_SELECT);
732 /* First item is the prompt string, not the items */
733 if (ua->num_prompts == 1) {
734 bsendmsg(ua, _("Selection is empty!\n"));
735 item = -1; /* list is empty ! */
738 if (ua->num_prompts == 2) {
740 bsendmsg(ua, _("Item 1 selected automatically.\n"));
742 bstrncpy(prompt, ua->prompt[1], max_prompt);
746 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
748 /* Either a . or an @ will get you out of the loop */
749 if (!get_pint(ua, pmsg)) {
750 item = -1; /* error */
751 bsendmsg(ua, _("Selection aborted, nothing done.\n"));
754 item = ua->pint32_val;
755 if (item < 1 || item >= ua->num_prompts) {
756 bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
760 bstrncpy(prompt, ua->prompt[item], max_prompt);
766 for (i=0; i < ua->num_prompts; i++) {
770 return item>0 ? item-1 : item;
775 * We scan what the user has entered looking for
776 * storage=<storage-resource>
779 * ? (prompt him with storage list)
780 * <some-error> (prompt him with storage list)
782 * If use_default is set, we assume that any keyword without a value
783 * is the name of the Storage resource wanted.
785 STORE *get_storage_resource(UAContext *ua, bool use_default)
787 char *store_name = NULL;
794 for (i=1; i<ua->argc; i++) {
795 if (use_default && !ua->argv[i]) {
796 /* Ignore slots, scan and barcode(s) keywords */
797 if (strcasecmp("scan", ua->argk[i]) == 0 ||
798 strcasecmp("barcode", ua->argk[i]) == 0 ||
799 strcasecmp("barcodes", ua->argk[i]) == 0 ||
800 strcasecmp("slots", ua->argk[i]) == 0) {
803 /* Default argument is storage */
805 bsendmsg(ua, _("Storage name given twice.\n"));
808 store_name = ua->argk[i];
809 if (*store_name == '?') {
814 if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
815 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
816 store_name = ua->argv[i];
819 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
820 jobid = str_to_int64(ua->argv[i]);
822 bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
825 if (!(jcr=get_jcr_by_id(jobid))) {
826 bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
833 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
834 strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
836 bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
839 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
840 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
846 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
848 bsendmsg(ua, _("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
851 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
852 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
861 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
865 if (!store && store_name && store_name[0] != 0) {
866 store = (STORE *)GetResWithName(R_STORAGE, store_name);
868 bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
871 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
874 /* No keywords found, so present a selection list */
876 store = select_storage_resource(ua);
881 /* Get drive that we are working with for this storage */
882 int get_storage_drive(UAContext *ua, STORE *store)
885 /* Get drive for autochanger if possible */
886 i = find_arg_with_value(ua, "drive");
888 drive = atoi(ua->argv[i]);
889 } else if (store && store->autochanger) {
890 /* If our structure is not set ask SD for # drives */
891 if (store->drives == 0) {
892 store->drives = get_num_drives_from_SD(ua);
894 /* If only one drive, default = 0 */
895 if (store->drives == 1) {
898 /* Ask user to enter drive number */
900 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
901 drive = -1; /* None */
903 drive = atoi(ua->cmd);
912 * Scan looking for mediatype=
914 * if not found or error, put up selection list
916 * Returns: 0 on error
917 * 1 on success, MediaType is set
919 int get_media_type(UAContext *ua, char *MediaType, int max_media)
924 i = find_arg_with_value(ua, "mediatype");
926 bstrncpy(MediaType, ua->argv[i], max_media);
930 start_prompt(ua, _("Media Types defined in conf file:\n"));
932 foreach_res(store, R_STORAGE) {
933 add_prompt(ua, store->media_type);
936 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
939 bool get_level_from_name(JCR *jcr, const char *level_name)
941 /* Look up level name and pull code */
943 for (int i=0; joblevels[i].level_name; i++) {
944 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
945 jcr->JobLevel = joblevels[i].level;