3 * Bacula Director -- User Agent Database restore Command
4 * Creates a bootstrap file for restoring files and
5 * starts the restore job.
7 * Tree handling routines split into ua_tree.c July MMIII.
8 * BSR (bootstrap record) handling routines split into
11 * Kern Sibbald, July MMII
17 Copyright (C) 2002-2003 Kern Sibbald and John Walker
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of
22 the License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public
30 License along with this program; if not, write to the Free
31 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40 /* Imported functions */
41 extern int runcmd(UAContext *ua, char *cmd);
42 extern void print_bsr(UAContext *ua, RBSR *bsr);
44 /* Imported variables */
45 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
46 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
47 extern char *uar_create_temp1, *uar_last_full, *uar_full;
48 extern char *uar_inc_dec, *uar_list_temp, *uar_sel_jobid_temp;
49 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
50 extern char *uar_jobid_fileindex;
54 char **name; /* list of names */
55 int num_ids; /* ids stored */
56 int max_ids; /* size of array */
57 int num_del; /* number deleted */
58 int tot_ids; /* total to process */
62 /* Main structure for obtaining JobIds or Files to be restored */
67 char ClientName[MAX_NAME_LENGTH];
69 POOLMEM *JobIds; /* User entered string of JobIds */
73 uint32_t selected_files;
86 #define MAX_ID_LIST_LEN 1000000
89 /* Forward referenced functions */
90 static int last_full_handler(void *ctx, int num_fields, char **row);
91 static int jobid_handler(void *ctx, int num_fields, char **row);
92 static int next_jobid_from_list(char **p, uint32_t *JobId);
93 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
94 static int fileset_handler(void *ctx, int num_fields, char **row);
95 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
96 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
97 static void free_name_list(NAME_LIST *name_list);
98 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
99 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
100 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
101 static void free_rx(RESTORE_CTX *rx);
102 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
103 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
104 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file);
105 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx);
106 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
112 int restorecmd(UAContext *ua, char *cmd)
114 RESTORE_CTX rx; /* restore context */
118 memset(&rx, 0, sizeof(rx));
120 rx.path = get_pool_memory(PM_FNAME);
121 rx.fname = get_pool_memory(PM_FNAME);
122 rx.JobIds = get_pool_memory(PM_FNAME);
123 rx.query = get_pool_memory(PM_FNAME);
126 i = find_arg_with_value(ua, "where");
128 rx.where = ua->argv[i];
136 /* Ensure there is at least one Restore Job */
138 while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
139 if (job->JobType == JT_RESTORE) {
140 if (!rx.restore_job) {
141 rx.restore_job = job;
147 if (!rx.restore_jobs) {
149 "No Restore Job Resource found. You must create at least\n"
150 "one before running this command.\n"));
156 * Request user to select JobIds or files by various different methods
157 * last 20 jobs, where File saved, most recent backup, ...
158 * In the end, a list of files are pumped into
161 switch (user_select_jobids_or_files(ua, &rx)) {
164 return 0; /* error */
165 case 1: /* select by jobid */
166 build_directory_tree(ua, &rx);
173 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
174 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
178 write_bsr_file(ua, rx.bsr);
179 bsendmsg(ua, _("\n%u file%s selected to restore.\n\n"), rx.selected_files,
180 rx.selected_files==1?"":"s");
182 bsendmsg(ua, _("No files selected to restore.\n"));
187 if (rx.restore_jobs == 1) {
188 job = rx.restore_job;
190 job = select_restore_job_resource(ua);
193 bsendmsg(ua, _("No Restore Job resource found!\n"));
198 get_client_name(ua, &rx);
200 /* Build run command */
203 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
205 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
206 working_directory, rx.where);
209 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
210 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
214 Dmsg1(400, "Submitting: %s\n", ua->cmd);
218 bsendmsg(ua, _("Restore command done.\n"));
223 static void free_rx(RESTORE_CTX *rx)
227 free_pool_memory(rx->JobIds);
231 free_pool_memory(rx->fname);
235 free_pool_memory(rx->path);
239 free_pool_memory(rx->query);
242 free_name_list(&rx->name_list);
245 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
247 /* If no client name specified yet, get it now */
248 if (!rx->ClientName[0]) {
250 /* try command line argument */
251 int i = find_arg_with_value(ua, _("client"));
253 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
256 memset(&cr, 0, sizeof(cr));
257 if (!get_client_dbr(ua, &cr)) {
261 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
267 * The first step in the restore process is for the user to
268 * select a list of JobIds from which he will subsequently
269 * select which files are to be restored.
271 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
274 char date[MAX_TIME_LENGTH];
280 "List last 20 Jobs run",
281 "List Jobs where a given File is saved",
282 "Enter list of JobIds to select",
283 "Enter SQL list command",
284 "Select the most recent backup for a client",
285 "Select backup for a client before a specified time",
286 "Enter a list of files to restore",
298 switch (find_arg_keyword(ua, kw)) {
300 i = find_arg_with_value(ua, _("jobid"));
304 pm_strcpy(&rx->JobIds, ua->argv[i]);
307 case 1: /* current */
308 bstrutime(date, sizeof(date), time(NULL));
309 if (!select_backups_before_date(ua, rx, date)) {
315 i = find_arg_with_value(ua, _("before"));
319 if (str_to_utime(ua->argv[i]) == 0) {
320 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
323 bstrncpy(date, ua->argv[i], sizeof(date));
324 if (!select_backups_before_date(ua, rx, date)) {
330 if (!get_client_name(ua, rx)) {
334 i = find_arg_with_value(ua, _("file"));
338 pm_strcpy(&ua->cmd, ua->argv[i]);
339 insert_one_file(ua, rx);
342 /* Check MediaType and select storage that corresponds */
343 get_storage_from_mediatype(ua, &rx->name_list, rx);
350 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
351 "to be restored. You will be presented several methods\n"
352 "of specifying the JobIds. Then you will be allowed to\n"
353 "select which files from those JobIds are to be restored.\n\n"));
356 /* If choice not already made above, prompt */
361 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
362 for (int i=0; list[i]; i++) {
363 add_prompt(ua, list[i]);
366 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
369 case 0: /* list last 20 Jobs run */
370 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
373 case 1: /* list where a file is saved */
374 if (!get_cmd(ua, _("Enter Filename: "))) {
377 len = strlen(ua->cmd);
378 fname = (char *)malloc(len * 2 + 1);
379 db_escape_string(fname, ua->cmd, len);
380 Mmsg(&rx->query, uar_file, fname);
382 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
385 case 2: /* enter a list of JobIds */
386 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
389 pm_strcpy(&rx->JobIds, ua->cmd);
391 case 3: /* Enter an SQL list command */
392 if (!get_cmd(ua, _("Enter SQL list command: "))) {
395 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
398 case 4: /* Select the most recent backups */
399 bstrutime(date, sizeof(date), time(NULL));
400 if (!select_backups_before_date(ua, rx, date)) {
404 case 5: /* select backup at specified time */
405 bsendmsg(ua, _("The restored files will the most current backup\n"
406 "BEFORE the date you specify below.\n\n"));
408 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
411 if (str_to_utime(ua->cmd) != 0) {
414 bsendmsg(ua, _("Improper date format.\n"));
416 bstrncpy(date, ua->cmd, sizeof(date));
417 if (!select_backups_before_date(ua, rx, date)) {
421 case 6: /* Enter files */
422 if (!get_client_name(ua, rx)) {
425 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
426 "containg a list of file names, and terminate\n"
427 "them with a blank line.\n"));
429 if (!get_cmd(ua, _("Enter filename: "))) {
432 len = strlen(ua->cmd);
436 insert_one_file(ua, rx);
438 /* Check MediaType and select storage that corresponds */
439 get_storage_from_mediatype(ua, &rx->name_list, rx);
442 case 7: /* Cancel or quit */
447 if (*rx->JobIds == 0) {
448 bsendmsg(ua, _("No Jobs selected.\n"));
451 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
452 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
454 memset(&jr, 0, sizeof(JOB_DBR));
457 for (p=rx->JobIds; ; ) {
458 int stat = next_jobid_from_list(&p, &JobId);
460 bsendmsg(ua, _("Invalid JobId in list.\n"));
466 if (jr.JobId == JobId) {
467 continue; /* duplicate of last JobId */
470 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
471 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
474 rx->TotalFiles += jr.JobFiles;
479 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx)
489 if ((ffd = fopen(p, "r")) == NULL) {
490 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
494 while (fgets(file, sizeof(file), ffd)) {
496 if (!insert_file_into_findex_list(ua, rx, file)) {
497 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
503 insert_file_into_findex_list(ua, rx, ua->cmd);
509 * For a given file (path+filename), split into path and file, then
510 * lookup the most recent backup in the catalog to get the JobId
511 * and FileIndex, then insert them into the findex list.
513 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file)
515 strip_trailing_junk(file);
516 split_path_and_filename(rx, file);
517 Mmsg(&rx->query, uar_jobid_fileindex, rx->path, rx->fname, rx->ClientName);
519 /* Find and insert jobid and File Index */
520 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
521 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
522 rx->query, db_strerror(ua->db));
525 bsendmsg(ua, _("No database record found for: %s\n"), file);
528 rx->selected_files++;
530 * Find the FileSets for this JobId and add to the name_list
532 Mmsg(&rx->query, uar_mediatype, rx->JobId);
533 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
534 bsendmsg(ua, "%s", db_strerror(ua->db));
540 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
544 /* Find path without the filename.
545 * I.e. everything after the last / is a "filename".
546 * OK, maybe it is a directory name, but we treat it like
547 * a filename. If we don't find a / then the whole name
548 * must be a path name (e.g. c:).
550 for (p=f=name; *p; p++) {
552 f = p; /* set pos of last slash */
555 if (*f == '/') { /* did we find a slash? */
556 f++; /* yes, point to filename */
557 } else { /* no, whole thing must be path name */
561 /* If filename doesn't exist (i.e. root directory), we
562 * simply create a blank name consisting of a single
563 * space. This makes handling zero length filenames
568 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
569 memcpy(rx->fname, f, rx->fnl); /* copy filename */
570 rx->fname[rx->fnl] = 0;
572 rx->fname[0] = ' '; /* blank filename */
579 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
580 memcpy(rx->path, name, rx->pnl);
581 rx->path[rx->pnl] = 0;
588 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
591 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
594 JobId_t JobId, last_JobId;
598 memset(&tree, 0, sizeof(TREE_CTX));
600 * Build the directory tree containing JobIds user selected
602 tree.root = new_tree(rx->TotalFiles);
603 tree.root->fname = nofname;
607 * For display purposes, the same JobId, with different volumes may
608 * appear more than once, however, we only insert it once.
611 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
613 if (JobId == last_JobId) {
614 continue; /* eliminate duplicate JobIds */
617 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
620 * Find files for this JobId and insert them in the tree
622 Mmsg(&rx->query, uar_sel_files, JobId);
623 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
624 bsendmsg(ua, "%s", db_strerror(ua->db));
627 * Find the FileSets for this JobId and add to the name_list
629 Mmsg(&rx->query, uar_mediatype, JobId);
630 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
631 bsendmsg(ua, "%s", db_strerror(ua->db));
634 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
635 items, items==1?"":"s");
637 /* Check MediaType and select storage that corresponds */
638 get_storage_from_mediatype(ua, &rx->name_list, rx);
640 if (find_arg(ua, _("all")) < 0) {
641 /* Let the user select which files to restore */
642 user_select_files_from_tree(&tree);
646 * Walk down through the tree finding all files marked to be
647 * extracted making a bootstrap file.
649 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
650 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
652 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
653 add_findex(rx->bsr, node->JobId, node->FileIndex);
654 rx->selected_files++;
658 free_tree(tree.root); /* free the directory tree */
663 * This routine is used to get the current backup or a backup
664 * before the specified date.
666 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
671 char fileset_name[MAX_NAME_LENGTH];
675 /* Create temp tables */
676 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
677 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
678 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
679 bsendmsg(ua, "%s\n", db_strerror(ua->db));
681 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
682 bsendmsg(ua, "%s\n", db_strerror(ua->db));
685 * Select Client from the Catalog
687 memset(&cr, 0, sizeof(cr));
688 if (!get_client_dbr(ua, &cr)) {
691 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
696 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
697 start_prompt(ua, _("The defined FileSet resources are:\n"));
698 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
699 bsendmsg(ua, "%s\n", db_strerror(ua->db));
701 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
702 fileset_name, sizeof(fileset_name)) < 0) {
705 fsr.FileSetId = atoi(fileset_name); /* Id is first part of name */
706 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
707 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
708 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
709 "Continuing anyway.\n"));
713 /* Find JobId of last Full backup for this client, fileset */
714 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSetId);
715 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
716 bsendmsg(ua, "%s\n", db_strerror(ua->db));
720 /* Find all Volumes used by that JobId */
721 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
722 bsendmsg(ua, "%s\n", db_strerror(ua->db));
725 /* Note, this is needed as I don't seem to get the callback
726 * from the call just above.
729 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
730 bsendmsg(ua, "%s\n", db_strerror(ua->db));
732 if (rx->JobTDate == 0) {
733 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
737 /* Now find all Incremental/Decremental Jobs after Full save */
738 Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
739 cr.ClientId, fsr.FileSetId);
740 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
741 bsendmsg(ua, "%s\n", db_strerror(ua->db));
744 /* Get the JobIds from that list */
746 rx->last_jobid[0] = 0;
747 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
748 bsendmsg(ua, "%s\n", db_strerror(ua->db));
751 if (rx->JobIds[0] != 0) {
752 /* Display a list of Jobs selected for this restore */
753 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
755 bsendmsg(ua, _("No jobs found.\n"));
761 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
762 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
766 /* Return next JobId from comma separated list */
767 static int next_jobid_from_list(char **p, uint32_t *JobId)
773 for (int i=0; i<(int)sizeof(jobid); i++) {
774 if (*q == ',' || *q == 0) {
781 if (jobid[0] == 0 || !is_a_number(jobid)) {
785 *JobId = strtoul(jobid, NULL, 10);
790 * Callback handler to get JobId and FileIndex for files
792 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
794 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
795 rx->JobId = atoi(row[0]);
796 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
802 * Callback handler make list of JobIds
804 static int jobid_handler(void *ctx, int num_fields, char **row)
806 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
808 if (strcmp(rx->last_jobid, row[0]) == 0) {
809 return 0; /* duplicate id */
811 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
812 if (rx->JobIds[0] != 0) {
813 pm_strcat(&rx->JobIds, ",");
815 pm_strcat(&rx->JobIds, row[0]);
821 * Callback handler to pickup last Full backup JobTDate
823 static int last_full_handler(void *ctx, int num_fields, char **row)
825 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
827 rx->JobTDate = strtoll(row[1], NULL, 10);
833 * Callback handler build fileset prompt list
835 static int fileset_handler(void *ctx, int num_fields, char **row)
837 char prompt[MAX_NAME_LENGTH+200];
839 snprintf(prompt, sizeof(prompt), "%s %s %s", row[0], row[1], row[2]);
840 add_prompt((UAContext *)ctx, prompt);
845 * Called here with each name to be added to the list. The name is
846 * added to the list if it is not already in the list.
848 * Used to make unique list of FileSets and MediaTypes
850 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
852 NAME_LIST *name = (NAME_LIST *)ctx;
854 if (name->num_ids == MAX_ID_LIST_LEN) {
857 if (name->num_ids == name->max_ids) {
858 if (name->max_ids == 0) {
859 name->max_ids = 1000;
860 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
862 name->max_ids = (name->max_ids * 3) / 2;
863 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
866 for (int i=0; i<name->num_ids; i++) {
867 if (strcmp(name->name[i], row[0]) == 0) {
868 return 0; /* already in list, return */
871 /* Add new name to list */
872 name->name[name->num_ids++] = bstrdup(row[0]);
878 * Print names in the list
880 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
882 for (int i=0; i < name_list->num_ids; i++) {
883 bsendmsg(ua, "%s\n", name_list->name[i]);
889 * Free names in the list
891 static void free_name_list(NAME_LIST *name_list)
893 for (int i=0; i < name_list->num_ids; i++) {
894 free(name_list->name[i]);
896 if (name_list->name) {
897 free(name_list->name);
899 name_list->max_ids = 0;
900 name_list->num_ids = 0;
903 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
905 char name[MAX_NAME_LENGTH];
908 if (name_list->num_ids > 1) {
909 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
910 "Restore is not possible. The MediaTypes used are:\n"));
911 print_name_list(ua, name_list);
912 rx->store = select_storage_resource(ua);
916 if (name_list->num_ids == 0) {
917 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
918 rx->store = select_storage_resource(ua);
922 start_prompt(ua, _("The defined Storage resources are:\n"));
924 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
925 if (strcmp(store->media_type, name_list->name[0]) == 0) {
926 add_prompt(ua, store->hdr.name);
930 do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name));
931 rx->store = (STORE *)GetResWithName(R_STORAGE, name);
933 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
934 "MediaType %s, needed by the Jobs you selected.\n"
935 "You will be allowed to select a Storage device later.\n"),