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], 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]);
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], N_("jobname")) == 0 && ua->argv[i]) {
653 bstrncpy(jr->Name, ua->argv[i], sizeof(jr->Name));
657 if (!select_job_dbr(ua, jr)) { /* try once more */
664 * Implement unique set of prompts
666 void start_prompt(UAContext *ua, const char *msg)
668 if (ua->max_prompts == 0) {
669 ua->max_prompts = 10;
670 ua->prompt = (char **)bmalloc(sizeof(char *) * ua->max_prompts);
673 ua->prompt[0] = bstrdup(msg);
677 * Add to prompts -- keeping them unique
679 void add_prompt(UAContext *ua, const char *prompt)
682 if (ua->num_prompts == ua->max_prompts) {
683 ua->max_prompts *= 2;
684 ua->prompt = (char **)brealloc(ua->prompt, sizeof(char *) *
687 for (i=1; i < ua->num_prompts; i++) {
688 if (strcmp(ua->prompt[i], prompt) == 0) {
692 ua->prompt[ua->num_prompts++] = bstrdup(prompt);
696 * Display prompts and get user's choice
698 * Returns: -1 on error
699 * index base 0 on success, and choice
700 * is copied to prompt if not NULL
701 * prompt is set to the chosen prompt item string
703 int do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt)
706 char pmsg[MAXSTRING];
711 if (ua->num_prompts == 2) {
714 bstrncpy(prompt, ua->prompt[1], max_prompt);
716 bsendmsg(ua, _("Automatically selected %s: %s\n"), automsg, ua->prompt[1]);
719 /* If running non-interactive, bail out */
721 bsendmsg(ua, _("Cannot select %s in batch mode.\n"), automsg);
725 // bnet_sig(ua->UA_sock, BNET_START_SELECT);
726 bsendmsg(ua, ua->prompt[0]);
727 for (i=1; i < ua->num_prompts; i++) {
728 bsendmsg(ua, "%6d: %s\n", i, ua->prompt[i]);
730 // bnet_sig(ua->UA_sock, BNET_END_SELECT);
733 /* First item is the prompt string, not the items */
734 if (ua->num_prompts == 1) {
735 bsendmsg(ua, _("Selection is empty!\n"));
736 item = -1; /* list is empty ! */
739 if (ua->num_prompts == 2) {
741 bsendmsg(ua, _("Item 1 selected automatically.\n"));
743 bstrncpy(prompt, ua->prompt[1], max_prompt);
747 sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
749 /* Either a . or an @ will get you out of the loop */
750 if (!get_pint(ua, pmsg)) {
751 item = -1; /* error */
752 bsendmsg(ua, _("Selection aborted, nothing done.\n"));
755 item = ua->pint32_val;
756 if (item < 1 || item >= ua->num_prompts) {
757 bsendmsg(ua, _("Please enter a number between 1 and %d\n"), ua->num_prompts-1);
761 bstrncpy(prompt, ua->prompt[item], max_prompt);
767 for (i=0; i < ua->num_prompts; i++) {
771 return item>0 ? item-1 : item;
776 * We scan what the user has entered looking for
777 * storage=<storage-resource>
780 * ? (prompt him with storage list)
781 * <some-error> (prompt him with storage list)
783 * If use_default is set, we assume that any keyword without a value
784 * is the name of the Storage resource wanted.
786 STORE *get_storage_resource(UAContext *ua, bool use_default)
788 char *store_name = NULL;
795 for (i=1; i<ua->argc; i++) {
796 if (use_default && !ua->argv[i]) {
797 /* Ignore slots, scan and barcode(s) keywords */
798 if (strcasecmp("scan", ua->argk[i]) == 0 ||
799 strcasecmp("barcode", ua->argk[i]) == 0 ||
800 strcasecmp("barcodes", ua->argk[i]) == 0 ||
801 strcasecmp("slots", ua->argk[i]) == 0) {
804 /* Default argument is storage */
806 bsendmsg(ua, _("Storage name given twice.\n"));
809 store_name = ua->argk[i];
810 if (*store_name == '?') {
815 if (strcasecmp(ua->argk[i], N_("storage")) == 0 ||
816 strcasecmp(ua->argk[i], N_("sd")) == 0) {
817 store_name = ua->argv[i];
820 } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
821 jobid = str_to_int64(ua->argv[i]);
823 bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
826 if (!(jcr=get_jcr_by_id(jobid))) {
827 bsendmsg(ua, _("JobId %s is not running.\n"), edit_int64(jobid, ed1));
834 } else if (strcasecmp(ua->argk[i], N_("job")) == 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]);
849 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
853 if (!store && store_name && store_name[0] != 0) {
854 store = (STORE *)GetResWithName(R_STORAGE, store_name);
856 bsendmsg(ua, _("Storage resource \"%s\": not found\n"), store_name);
859 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
862 /* No keywords found, so present a selection list */
864 store = select_storage_resource(ua);
869 /* Get drive that we are working with for this storage */
870 int get_storage_drive(UAContext *ua, STORE *store)
873 /* Get drive for autochanger if possible */
874 i = find_arg_with_value(ua, "drive");
876 drive = atoi(ua->argv[i]);
877 } else if (store && store->autochanger) {
878 /* If our structure is not set ask SD for # drives */
879 if (store->drives == 0) {
880 store->drives = get_num_drives_from_SD(ua);
882 /* If only one drive, default = 0 */
883 if (store->drives == 1) {
886 /* Ask user to enter drive number */
888 if (!get_cmd(ua, _("Enter autochanger drive[0]: "))) {
889 drive = -1; /* None */
891 drive = atoi(ua->cmd);
900 * Scan looking for mediatype=
902 * if not found or error, put up selection list
904 * Returns: 0 on error
905 * 1 on success, MediaType is set
907 int get_media_type(UAContext *ua, char *MediaType, int max_media)
912 i = find_arg_with_value(ua, "mediatype");
914 bstrncpy(MediaType, ua->argv[i], max_media);
918 start_prompt(ua, _("Media Types defined in conf file:\n"));
920 foreach_res(store, R_STORAGE) {
921 add_prompt(ua, store->media_type);
924 return (do_prompt(ua, _("Media Type"), _("Select the Media Type"), MediaType, max_media) < 0) ? 0 : 1;
927 bool get_level_from_name(JCR *jcr, const char *level_name)
929 /* Look up level name and pull code */
931 for (int i=0; joblevels[i].level_name; i++) {
932 if (strcasecmp(level_name, joblevels[i].level_name) == 0) {
933 jcr->JobLevel = joblevels[i].level;