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)
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], NT_("client")) == 0 ||
301 strcasecmp(ua->argk[i], NT_("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], NT_("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], NT_("ujobid")) == 0 && ua->argv[i]) {
631 bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
632 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && ua->argv[i]) {
633 jr->JobId = str_to_int64(ua->argv[i]);
638 if (!db_get_job_record(ua->jcr, ua->db, jr)) {
639 bsendmsg(ua, _("Could not find Job \"%s\": ERR=%s"), ua->argv[i],
640 db_strerror(ua->db));
650 for (i=1; i<ua->argc; i++) {
651 if ((strcasecmp(ua->argk[i], NT_("jobname")) == 0 ||
652 strcasecmp(ua->argk[i], NT_("job")) == 0) && ua->argv[i]) {
654 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
658 if (!select_job_dbr(ua, jr)) { /* try once more */
665 * Implement unique set of prompts
667 void start_prompt(UAContext *ua, const char *msg)
669 if (ua->max_prompts == 0) {
670 ua->max_prompts = 10;
671 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
674 ua->prompt[0] = bstrdup(msg);
678 * Add to prompts -- keeping them unique
680 void add_prompt(UAContext *ua, const char *prompt)
683 if (ua->num_prompts == ua->max_prompts) {
684 ua->max_prompts *= 2;
685 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
688 for (i=1; i < ua->num_prompts; i++) {
689 if (strcmp(ua->prompt[i], prompt) == 0) {
693 ua->prompt[ua->num_prompts++] = bstrdup(prompt);
697 * Display prompts and get user's choice
699 * Returns: -1 on error
700 * index base 0 on success, and choice
701 * is copied to prompt if not NULL
702 * prompt is set to the chosen prompt item string
704 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
707 char pmsg[MAXSTRING];
712 if (ua->num_prompts == 2) {
715 bstrncpy(prompt, ua->prompt[1], max_prompt);
717 bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
720 /* If running non-interactive, bail out */
722 bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
726 // bnet_sig(ua->UA_sock, BNET_START_SELECT);
727 bsendmsg(ua, ua->prompt[0]);
728 for (i=1; i < ua->num_prompts; i++) {
729 bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
731 // bnet_sig(ua->UA_sock, BNET_END_SELECT);
734 /* First item is the prompt string, not the items */
735 if (ua->num_prompts == 1) {
736 bsendmsg(ua, _("Selection is empty!\n"));
737 item = -1; /* list is empty ! */
740 if (ua->num_prompts == 2) {
742 bsendmsg(ua, _("Item 1 selected automatically.\n"));
744 bstrncpy(prompt, ua->prompt[1], max_prompt);
748 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
750 /* Either a . or an @ will get you out of the loop */
751 if (!get_pint(ua, pmsg)) {
752 item = -1; /* error */
753 bsendmsg(ua, _("Selection aborted, nothing done.\n"));
756 item = ua->pint32_val;
757 if (item < 1 || item >= ua->num_prompts) {
758 bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
762 bstrncpy(prompt, ua->prompt[item], max_prompt);
768 for (i=0; i < ua->num_prompts; i++) {
772 return item>0 ? item-1 : item;
777 * We scan what the user has entered looking for
778 * storage=<storage-resource>
781 * ? (prompt him with storage list)
782 * <some-error> (prompt him with storage list)
784 * If use_default is set, we assume that any keyword without a value
785 * is the name of the Storage resource wanted.
787 STORE *get_storage_resource(UAContext *ua, bool use_default)
789 char *store_name = NULL;
796 for (i=1; i<ua->argc; i++) {
797 if (use_default && !ua->argv[i]) {
798 /* Ignore slots, scan and barcode(s) keywords */
799 if (strcasecmp("scan", ua->argk[i]) == 0 ||
800 strcasecmp("barcode", ua->argk[i]) == 0 ||
801 strcasecmp("barcodes", ua->argk[i]) == 0 ||
802 strcasecmp("slots", ua->argk[i]) == 0) {
805 /* Default argument is storage */
807 bsendmsg(ua, _("Storage name given twice.\n"));
810 store_name = ua->argk[i];
811 if (*store_name == '?') {
816 if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
817 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
818 store_name = ua->argv[i];
821 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
822 jobid = str_to_int64(ua->argv[i]);
824 bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
827 if (!(jcr=get_jcr_by_id(jobid))) {
828 bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
835 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
836 strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
838 bsendmsg(ua, _("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
841 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
842 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
848 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
850 bsendmsg(ua, _("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
853 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
854 bsendmsg(ua, _("Job \"%s\" is not running.\n"), ua->argv[i]);
863 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
867 if (!store && store_name && store_name[0] != 0) {
868 store = (STORE *)GetResWithName(R_STORAGE, store_name);
870 bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
873 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
876 /* No keywords found, so present a selection list */
878 store = select_storage_resource(ua);
883 /* Get drive that we are working with for this storage */
884 int get_storage_drive(UAContext *ua, STORE *store)
887 /* Get drive for autochanger if possible */
888 i = find_arg_with_value(ua, "drive");
890 drive = atoi(ua->argv[i]);
891 } else if (store && store->autochanger) {
892 /* If our structure is not set ask SD for # drives */
893 if (store->drives == 0) {
894 store->drives = get_num_drives_from_SD(ua);
896 /* If only one drive, default = 0 */
897 if (store->drives == 1) {
900 /* Ask user to enter drive number */
902 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
903 drive = -1; /* None */
905 drive = atoi(ua->cmd);
914 * Scan looking for mediatype=
916 * if not found or error, put up selection list
918 * Returns: 0 on error
919 * 1 on success, MediaType is set
921 int get_media_type(UAContext *ua, char *MediaType, int max_media)
926 i = find_arg_with_value(ua, "mediatype");
928 bstrncpy(MediaType, ua->argv[i], max_media);
932 start_prompt(ua, _("Media Types defined in conf file:\n"));
934 foreach_res(store, R_STORAGE) {
935 add_prompt(ua, store->media_type);
938 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
941 bool get_level_from_name(JCR *jcr, const char *level_name)
943 /* Look up level name and pull code */
945 for (int i=0; joblevels[i].level_name; i++) {
946 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
947 jcr->JobLevel = joblevels[i].level;