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 run_cmd(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,
106 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
107 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
108 static int get_date(UAContext *ua, char *date, int date_len);
114 int restore_cmd(UAContext *ua, char *cmd)
116 RESTORE_CTX rx; /* restore context */
120 memset(&rx, 0, sizeof(rx));
122 rx.path = get_pool_memory(PM_FNAME);
123 rx.fname = get_pool_memory(PM_FNAME);
124 rx.JobIds = get_pool_memory(PM_FNAME);
125 rx.query = get_pool_memory(PM_FNAME);
128 i = find_arg_with_value(ua, "where");
130 rx.where = ua->argv[i];
138 /* Ensure there is at least one Restore Job */
140 while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
141 if (job->JobType == JT_RESTORE) {
142 if (!rx.restore_job) {
143 rx.restore_job = job;
149 if (!rx.restore_jobs) {
151 "No Restore Job Resource found. You must create at least\n"
152 "one before running this command.\n"));
158 * Request user to select JobIds or files by various different methods
159 * last 20 jobs, where File saved, most recent backup, ...
160 * In the end, a list of files are pumped into
163 switch (user_select_jobids_or_files(ua, &rx)) {
166 return 0; /* error */
167 case 1: /* select by jobid */
168 build_directory_tree(ua, &rx);
170 case 2: /* select by filename, no tree needed */
175 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
176 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
180 write_bsr_file(ua, rx.bsr);
181 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
182 rx.selected_files==1?"":"s");
184 bsendmsg(ua, _("No files selected to be restored.\n"));
189 if (rx.restore_jobs == 1) {
190 job = rx.restore_job;
192 job = select_restore_job_resource(ua);
195 bsendmsg(ua, _("No Restore Job resource found!\n"));
200 get_client_name(ua, &rx);
202 /* Build run command */
205 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
207 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
208 working_directory, rx.where);
211 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
212 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
215 if (find_arg(ua, _("run")) >= 0) {
216 pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */
218 Dmsg1(400, "Submitting: %s\n", ua->cmd);
220 run_cmd(ua, ua->cmd);
222 bsendmsg(ua, _("Restore command done.\n"));
227 static void free_rx(RESTORE_CTX *rx)
232 free_pool_memory(rx->JobIds);
236 free_pool_memory(rx->fname);
240 free_pool_memory(rx->path);
244 free_pool_memory(rx->query);
247 free_name_list(&rx->name_list);
250 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
252 /* If no client name specified yet, get it now */
253 if (!rx->ClientName[0]) {
255 /* try command line argument */
256 int i = find_arg_with_value(ua, _("client"));
258 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
261 memset(&cr, 0, sizeof(cr));
262 if (!get_client_dbr(ua, &cr)) {
266 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
272 * The first step in the restore process is for the user to
273 * select a list of JobIds from which he will subsequently
274 * select which files are to be restored.
276 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
279 char date[MAX_TIME_LENGTH];
280 bool have_date = false;
286 "List last 20 Jobs run",
287 "List Jobs where a given File is saved",
288 "Enter list of JobIds to select",
289 "Enter SQL list command",
290 "Select the most recent backup for a client",
291 "Select backup for a client before a specified time",
292 "Enter a list of files to restore",
293 "Enter a list of files to restore before a specified time",
307 switch (find_arg_keyword(ua, kw)) {
310 i = find_arg_with_value(ua, _("jobid"));
314 pm_strcpy(&rx->JobIds, ua->argv[i]);
315 ua->argk[i][0] = 0; /* "consume" jobid= */
319 case 1: /* current */
320 bstrutime(date, sizeof(date), time(NULL));
324 i = find_arg_with_value(ua, _("before"));
328 if (str_to_utime(ua->argv[i]) == 0) {
329 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
332 bstrncpy(date, ua->argv[i], sizeof(date));
337 bstrutime(date, sizeof(date), time(NULL));
339 if (!get_client_name(ua, rx)) {
343 i = find_arg_with_value(ua, _("file"));
347 pm_strcpy(&ua->cmd, ua->argv[i]);
348 insert_one_file(ua, rx, date);
349 ua->argk[i][0] = 0; /* "consume" the file= */
351 /* Check MediaType and select storage that corresponds */
352 get_storage_from_mediatype(ua, &rx->name_list, rx);
356 bstrutime(date, sizeof(date), time(NULL));
358 if (!select_backups_before_date(ua, rx, date)) {
368 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
369 "to be restored. You will be presented several methods\n"
370 "of specifying the JobIds. Then you will be allowed to\n"
371 "select which files from those JobIds are to be restored.\n\n"));
374 /* If choice not already made above, prompt */
379 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
380 for (int i=0; list[i]; i++) {
381 add_prompt(ua, list[i]);
384 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
387 case 0: /* list last 20 Jobs run */
388 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
391 case 1: /* list where a file is saved */
392 if (!get_cmd(ua, _("Enter Filename: "))) {
395 len = strlen(ua->cmd);
396 fname = (char *)malloc(len * 2 + 1);
397 db_escape_string(fname, ua->cmd, len);
398 Mmsg(&rx->query, uar_file, fname);
400 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
403 case 2: /* enter a list of JobIds */
404 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
407 pm_strcpy(&rx->JobIds, ua->cmd);
409 case 3: /* Enter an SQL list command */
410 if (!get_cmd(ua, _("Enter SQL list command: "))) {
413 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
416 case 4: /* Select the most recent backups */
417 bstrutime(date, sizeof(date), time(NULL));
418 if (!select_backups_before_date(ua, rx, date)) {
422 case 5: /* select backup at specified time */
423 if (!get_date(ua, date, sizeof(date))) {
426 if (!select_backups_before_date(ua, rx, date)) {
430 case 6: /* Enter files */
431 bstrutime(date, sizeof(date), time(NULL));
432 if (!get_client_name(ua, rx)) {
435 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
436 "containg a list of file names, and terminate\n"
437 "them with a blank line.\n"));
439 if (!get_cmd(ua, _("Enter filename: "))) {
442 len = strlen(ua->cmd);
446 insert_one_file(ua, rx, date);
448 /* Check MediaType and select storage that corresponds */
449 get_storage_from_mediatype(ua, &rx->name_list, rx);
451 case 7: /* enter files backed up before specified time */
452 if (!get_date(ua, date, sizeof(date))) {
455 if (!get_client_name(ua, rx)) {
458 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
459 "containg a list of file names, and terminate\n"
460 "them with a blank line.\n"));
462 if (!get_cmd(ua, _("Enter filename: "))) {
465 len = strlen(ua->cmd);
469 insert_one_file(ua, rx, date);
471 /* Check MediaType and select storage that corresponds */
472 get_storage_from_mediatype(ua, &rx->name_list, rx);
476 case 8: /* Cancel or quit */
481 if (*rx->JobIds == 0) {
482 bsendmsg(ua, _("No Jobs selected.\n"));
485 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
486 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
488 memset(&jr, 0, sizeof(JOB_DBR));
491 for (p=rx->JobIds; ; ) {
492 int stat = next_jobid_from_list(&p, &JobId);
494 bsendmsg(ua, _("Invalid JobId in list.\n"));
500 if (jr.JobId == JobId) {
501 continue; /* duplicate of last JobId */
504 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
505 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
508 rx->TotalFiles += jr.JobFiles;
513 static int get_date(UAContext *ua, char *date, int date_len)
515 bsendmsg(ua, _("The restored files will the most current backup\n"
516 "BEFORE the date you specify below.\n\n"));
518 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
521 if (str_to_utime(ua->cmd) != 0) {
524 bsendmsg(ua, _("Improper date format.\n"));
526 bstrncpy(date, ua->cmd, date_len);
530 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
540 if ((ffd = fopen(p, "r")) == NULL) {
541 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
545 while (fgets(file, sizeof(file), ffd)) {
547 if (!insert_file_into_findex_list(ua, rx, file, date)) {
548 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
554 insert_file_into_findex_list(ua, rx, ua->cmd, date);
560 * For a given file (path+filename), split into path and file, then
561 * lookup the most recent backup in the catalog to get the JobId
562 * and FileIndex, then insert them into the findex list.
564 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
567 strip_trailing_junk(file);
568 split_path_and_filename(rx, file);
569 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
571 /* Find and insert jobid and File Index */
572 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
573 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
574 rx->query, db_strerror(ua->db));
577 bsendmsg(ua, _("No database record found for: %s\n"), file);
580 rx->selected_files++;
582 * Find the FileSets for this JobId and add to the name_list
584 Mmsg(&rx->query, uar_mediatype, rx->JobId);
585 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
586 bsendmsg(ua, "%s", db_strerror(ua->db));
592 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
596 /* Find path without the filename.
597 * I.e. everything after the last / is a "filename".
598 * OK, maybe it is a directory name, but we treat it like
599 * a filename. If we don't find a / then the whole name
600 * must be a path name (e.g. c:).
602 for (p=f=name; *p; p++) {
604 f = p; /* set pos of last slash */
607 if (*f == '/') { /* did we find a slash? */
608 f++; /* yes, point to filename */
609 } else { /* no, whole thing must be path name */
613 /* If filename doesn't exist (i.e. root directory), we
614 * simply create a blank name consisting of a single
615 * space. This makes handling zero length filenames
620 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
621 memcpy(rx->fname, f, rx->fnl); /* copy filename */
622 rx->fname[rx->fnl] = 0;
624 rx->fname[0] = ' '; /* blank filename */
631 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
632 memcpy(rx->path, name, rx->pnl);
633 rx->path[rx->pnl] = 0;
640 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
643 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
646 JobId_t JobId, last_JobId;
650 memset(&tree, 0, sizeof(TREE_CTX));
652 * Build the directory tree containing JobIds user selected
654 tree.root = new_tree(rx->TotalFiles);
655 tree.root->fname = nofname;
659 * For display purposes, the same JobId, with different volumes may
660 * appear more than once, however, we only insert it once.
663 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
665 if (JobId == last_JobId) {
666 continue; /* eliminate duplicate JobIds */
669 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
672 * Find files for this JobId and insert them in the tree
674 Mmsg(&rx->query, uar_sel_files, JobId);
675 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
676 bsendmsg(ua, "%s", db_strerror(ua->db));
679 * Find the FileSets for this JobId and add to the name_list
681 Mmsg(&rx->query, uar_mediatype, JobId);
682 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
683 bsendmsg(ua, "%s", db_strerror(ua->db));
686 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
687 items, items==1?"":"s");
689 /* Check MediaType and select storage that corresponds */
690 get_storage_from_mediatype(ua, &rx->name_list, rx);
692 if (find_arg(ua, _("all")) < 0) {
693 /* Let the user select which files to restore */
694 user_select_files_from_tree(&tree);
698 * Walk down through the tree finding all files marked to be
699 * extracted making a bootstrap file.
701 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
702 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
704 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
705 add_findex(rx->bsr, node->JobId, node->FileIndex);
706 rx->selected_files++;
710 free_tree(tree.root); /* free the directory tree */
715 * This routine is used to get the current backup or a backup
716 * before the specified date.
718 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
723 char fileset_name[MAX_NAME_LENGTH];
727 /* Create temp tables */
728 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
729 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
730 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
731 bsendmsg(ua, "%s\n", db_strerror(ua->db));
733 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
734 bsendmsg(ua, "%s\n", db_strerror(ua->db));
737 * Select Client from the Catalog
739 memset(&cr, 0, sizeof(cr));
740 if (!get_client_dbr(ua, &cr)) {
743 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
748 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
749 start_prompt(ua, _("The defined FileSet resources are:\n"));
750 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
751 bsendmsg(ua, "%s\n", db_strerror(ua->db));
753 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
754 fileset_name, sizeof(fileset_name)) < 0) {
757 fsr.FileSetId = atoi(fileset_name); /* Id is first part of name */
758 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
759 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
760 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
761 "Continuing anyway.\n"));
765 /* Find JobId of last Full backup for this client, fileset */
766 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSetId);
767 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
768 bsendmsg(ua, "%s\n", db_strerror(ua->db));
772 /* Find all Volumes used by that JobId */
773 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
774 bsendmsg(ua, "%s\n", db_strerror(ua->db));
777 /* Note, this is needed because I don't seem to get the callback
778 * from the call just above.
781 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
782 bsendmsg(ua, "%s\n", db_strerror(ua->db));
784 if (rx->JobTDate == 0) {
785 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
789 /* Now find all Incremental/Decremental Jobs after Full save */
790 Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
791 cr.ClientId, fsr.FileSetId);
792 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
793 bsendmsg(ua, "%s\n", db_strerror(ua->db));
796 /* Get the JobIds from that list */
798 rx->last_jobid[0] = 0;
799 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
800 bsendmsg(ua, "%s\n", db_strerror(ua->db));
803 if (rx->JobIds[0] != 0) {
804 /* Display a list of Jobs selected for this restore */
805 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
807 bsendmsg(ua, _("No jobs found.\n"));
813 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
814 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
818 /* Return next JobId from comma separated list */
819 static int next_jobid_from_list(char **p, uint32_t *JobId)
825 for (int i=0; i<(int)sizeof(jobid); i++) {
826 if (*q == ',' || *q == 0) {
833 if (jobid[0] == 0 || !is_a_number(jobid)) {
837 *JobId = strtoul(jobid, NULL, 10);
842 * Callback handler to get JobId and FileIndex for files
844 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
846 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
847 rx->JobId = atoi(row[0]);
848 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
854 * Callback handler make list of JobIds
856 static int jobid_handler(void *ctx, int num_fields, char **row)
858 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
860 if (strcmp(rx->last_jobid, row[0]) == 0) {
861 return 0; /* duplicate id */
863 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
864 if (rx->JobIds[0] != 0) {
865 pm_strcat(&rx->JobIds, ",");
867 pm_strcat(&rx->JobIds, row[0]);
873 * Callback handler to pickup last Full backup JobTDate
875 static int last_full_handler(void *ctx, int num_fields, char **row)
877 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
879 rx->JobTDate = strtoll(row[1], NULL, 10);
885 * Callback handler build fileset prompt list
887 static int fileset_handler(void *ctx, int num_fields, char **row)
889 char prompt[MAX_NAME_LENGTH+200];
891 snprintf(prompt, sizeof(prompt), "%s %s %s", row[0], row[1], row[2]);
892 add_prompt((UAContext *)ctx, prompt);
897 * Called here with each name to be added to the list. The name is
898 * added to the list if it is not already in the list.
900 * Used to make unique list of FileSets and MediaTypes
902 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
904 NAME_LIST *name = (NAME_LIST *)ctx;
906 if (name->num_ids == MAX_ID_LIST_LEN) {
909 if (name->num_ids == name->max_ids) {
910 if (name->max_ids == 0) {
911 name->max_ids = 1000;
912 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
914 name->max_ids = (name->max_ids * 3) / 2;
915 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
918 for (int i=0; i<name->num_ids; i++) {
919 if (strcmp(name->name[i], row[0]) == 0) {
920 return 0; /* already in list, return */
923 /* Add new name to list */
924 name->name[name->num_ids++] = bstrdup(row[0]);
930 * Print names in the list
932 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
934 for (int i=0; i < name_list->num_ids; i++) {
935 bsendmsg(ua, "%s\n", name_list->name[i]);
941 * Free names in the list
943 static void free_name_list(NAME_LIST *name_list)
945 for (int i=0; i < name_list->num_ids; i++) {
946 free(name_list->name[i]);
948 if (name_list->name) {
949 free(name_list->name);
950 name_list->name = NULL;
952 name_list->max_ids = 0;
953 name_list->num_ids = 0;
956 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
958 char name[MAX_NAME_LENGTH];
961 if (name_list->num_ids > 1) {
962 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
963 "Restore is not possible. The MediaTypes used are:\n"));
964 print_name_list(ua, name_list);
965 rx->store = select_storage_resource(ua);
969 if (name_list->num_ids == 0) {
970 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
971 rx->store = select_storage_resource(ua);
975 start_prompt(ua, _("The defined Storage resources are:\n"));
977 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
978 if (strcmp(store->media_type, name_list->name[0]) == 0) {
979 add_prompt(ua, store->hdr.name);
983 do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name));
984 rx->store = (STORE *)GetResWithName(R_STORAGE, name);
986 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
987 "MediaType %s, needed by the Jobs you selected.\n"
988 "You will be allowed to select a Storage device later.\n"),