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];
137 /* Ensure there is at least one Restore Job */
139 while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
140 if (job->JobType == JT_RESTORE) {
141 if (!rx.restore_job) {
142 rx.restore_job = job;
148 if (!rx.restore_jobs) {
150 "No Restore Job Resource found. You must create at least\n"
151 "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 case 1: /* select by jobid */
165 build_directory_tree(ua, &rx);
167 case 2: /* select by filename, no tree needed */
172 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
173 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
176 write_bsr_file(ua, rx.bsr);
177 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
178 rx.selected_files==1?"":"s");
180 bsendmsg(ua, _("No files selected to be restored.\n"));
184 if (rx.restore_jobs == 1) {
185 job = rx.restore_job;
187 job = select_restore_job_resource(ua);
193 get_client_name(ua, &rx);
194 if (!rx.ClientName) {
195 bsendmsg(ua, _("No Restore Job resource found!\n"));
199 /* Build run command */
202 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
204 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
205 working_directory, rx.where);
208 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
209 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
212 if (find_arg(ua, _("run")) >= 0) {
213 pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */
215 Dmsg1(400, "Submitting: %s\n", ua->cmd);
217 run_cmd(ua, ua->cmd);
219 bsendmsg(ua, _("Restore command done.\n"));
229 static void free_rx(RESTORE_CTX *rx)
234 free_pool_memory(rx->JobIds);
238 free_pool_memory(rx->fname);
242 free_pool_memory(rx->path);
246 free_pool_memory(rx->query);
249 free_name_list(&rx->name_list);
252 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
254 /* If no client name specified yet, get it now */
255 if (!rx->ClientName[0]) {
257 /* try command line argument */
258 int i = find_arg_with_value(ua, _("client"));
260 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
263 memset(&cr, 0, sizeof(cr));
264 if (!get_client_dbr(ua, &cr)) {
267 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
273 * The first step in the restore process is for the user to
274 * select a list of JobIds from which he will subsequently
275 * select which files are to be restored.
277 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
280 char date[MAX_TIME_LENGTH];
281 bool have_date = false;
287 "List last 20 Jobs run",
288 "List Jobs where a given File is saved",
289 "Enter list of JobIds to select",
290 "Enter SQL list command",
291 "Select the most recent backup for a client",
292 "Select backup for a client before a specified time",
293 "Enter a list of files to restore",
294 "Enter a list of files to restore before a specified time",
308 switch (find_arg_keyword(ua, kw)) {
311 i = find_arg_with_value(ua, _("jobid"));
315 pm_strcpy(&rx->JobIds, ua->argv[i]);
316 ua->argk[i][0] = 0; /* "consume" jobid= */
320 case 1: /* current */
321 bstrutime(date, sizeof(date), time(NULL));
325 i = find_arg_with_value(ua, _("before"));
329 if (str_to_utime(ua->argv[i]) == 0) {
330 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
333 bstrncpy(date, ua->argv[i], sizeof(date));
338 bstrutime(date, sizeof(date), time(NULL));
340 if (!get_client_name(ua, rx)) {
344 i = find_arg_with_value(ua, _("file"));
348 pm_strcpy(&ua->cmd, ua->argv[i]);
349 insert_one_file(ua, rx, date);
350 ua->argk[i][0] = 0; /* "consume" the file= */
352 /* Check MediaType and select storage that corresponds */
353 get_storage_from_mediatype(ua, &rx->name_list, rx);
357 bstrutime(date, sizeof(date), time(NULL));
359 if (!select_backups_before_date(ua, rx, date)) {
369 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
370 "to be restored. You will be presented several methods\n"
371 "of specifying the JobIds. Then you will be allowed to\n"
372 "select which files from those JobIds are to be restored.\n\n"));
375 /* If choice not already made above, prompt */
380 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
381 for (int i=0; list[i]; i++) {
382 add_prompt(ua, list[i]);
385 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
388 case 0: /* list last 20 Jobs run */
389 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
392 case 1: /* list where a file is saved */
393 if (!get_cmd(ua, _("Enter Filename: "))) {
396 len = strlen(ua->cmd);
397 fname = (char *)malloc(len * 2 + 1);
398 db_escape_string(fname, ua->cmd, len);
399 Mmsg(&rx->query, uar_file, fname);
401 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
404 case 2: /* enter a list of JobIds */
405 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
408 pm_strcpy(&rx->JobIds, ua->cmd);
410 case 3: /* Enter an SQL list command */
411 if (!get_cmd(ua, _("Enter SQL list command: "))) {
414 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
417 case 4: /* Select the most recent backups */
418 bstrutime(date, sizeof(date), time(NULL));
419 if (!select_backups_before_date(ua, rx, date)) {
423 case 5: /* select backup at specified time */
424 if (!get_date(ua, date, sizeof(date))) {
427 if (!select_backups_before_date(ua, rx, date)) {
431 case 6: /* Enter files */
432 bstrutime(date, sizeof(date), time(NULL));
433 if (!get_client_name(ua, rx)) {
436 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
437 "containg a list of file names, and terminate\n"
438 "them with a blank line.\n"));
440 if (!get_cmd(ua, _("Enter filename: "))) {
443 len = strlen(ua->cmd);
447 insert_one_file(ua, rx, date);
449 /* Check MediaType and select storage that corresponds */
450 get_storage_from_mediatype(ua, &rx->name_list, rx);
452 case 7: /* enter files backed up before specified time */
453 if (!get_date(ua, date, sizeof(date))) {
456 if (!get_client_name(ua, rx)) {
459 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
460 "containg a list of file names, and terminate\n"
461 "them with a blank line.\n"));
463 if (!get_cmd(ua, _("Enter filename: "))) {
466 len = strlen(ua->cmd);
470 insert_one_file(ua, rx, date);
472 /* Check MediaType and select storage that corresponds */
473 get_storage_from_mediatype(ua, &rx->name_list, rx);
477 case 8: /* Cancel or quit */
482 if (*rx->JobIds == 0) {
483 bsendmsg(ua, _("No Jobs selected.\n"));
486 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
487 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
489 memset(&jr, 0, sizeof(JOB_DBR));
492 for (p=rx->JobIds; ; ) {
493 int stat = next_jobid_from_list(&p, &JobId);
495 bsendmsg(ua, _("Invalid JobId in list.\n"));
501 if (jr.JobId == JobId) {
502 continue; /* duplicate of last JobId */
505 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
506 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
509 rx->TotalFiles += jr.JobFiles;
514 static int get_date(UAContext *ua, char *date, int date_len)
516 bsendmsg(ua, _("The restored files will the most current backup\n"
517 "BEFORE the date you specify below.\n\n"));
519 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
522 if (str_to_utime(ua->cmd) != 0) {
525 bsendmsg(ua, _("Improper date format.\n"));
527 bstrncpy(date, ua->cmd, date_len);
531 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
541 if ((ffd = fopen(p, "r")) == NULL) {
542 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
546 while (fgets(file, sizeof(file), ffd)) {
548 if (!insert_file_into_findex_list(ua, rx, file, date)) {
549 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
555 insert_file_into_findex_list(ua, rx, ua->cmd, date);
561 * For a given file (path+filename), split into path and file, then
562 * lookup the most recent backup in the catalog to get the JobId
563 * and FileIndex, then insert them into the findex list.
565 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
568 strip_trailing_junk(file);
569 split_path_and_filename(rx, file);
570 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
572 /* Find and insert jobid and File Index */
573 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
574 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
575 rx->query, db_strerror(ua->db));
578 bsendmsg(ua, _("No database record found for: %s\n"), file);
581 rx->selected_files++;
583 * Find the MediaTypes for this JobId and add to the name_list
585 Mmsg(&rx->query, uar_mediatype, rx->JobId);
586 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
587 bsendmsg(ua, "%s", db_strerror(ua->db));
593 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
597 /* Find path without the filename.
598 * I.e. everything after the last / is a "filename".
599 * OK, maybe it is a directory name, but we treat it like
600 * a filename. If we don't find a / then the whole name
601 * must be a path name (e.g. c:).
603 for (p=f=name; *p; p++) {
605 f = p; /* set pos of last slash */
608 if (*f == '/') { /* did we find a slash? */
609 f++; /* yes, point to filename */
610 } else { /* no, whole thing must be path name */
614 /* If filename doesn't exist (i.e. root directory), we
615 * simply create a blank name consisting of a single
616 * space. This makes handling zero length filenames
621 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
622 memcpy(rx->fname, f, rx->fnl); /* copy filename */
623 rx->fname[rx->fnl] = 0;
625 rx->fname[0] = ' '; /* blank filename */
632 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
633 memcpy(rx->path, name, rx->pnl);
634 rx->path[rx->pnl] = 0;
641 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
644 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
647 JobId_t JobId, last_JobId;
651 memset(&tree, 0, sizeof(TREE_CTX));
653 * Build the directory tree containing JobIds user selected
655 tree.root = new_tree(rx->TotalFiles);
656 tree.root->fname = nofname;
660 * For display purposes, the same JobId, with different volumes may
661 * appear more than once, however, we only insert it once.
664 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
666 if (JobId == last_JobId) {
667 continue; /* eliminate duplicate JobIds */
670 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
673 * Find files for this JobId and insert them in the tree
675 Mmsg(&rx->query, uar_sel_files, JobId);
676 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
677 bsendmsg(ua, "%s", db_strerror(ua->db));
680 * Find the MediaTypes for this JobId and add to the name_list
682 Mmsg(&rx->query, uar_mediatype, JobId);
683 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
684 bsendmsg(ua, "%s", db_strerror(ua->db));
687 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
688 items, items==1?"":"s");
690 /* Check MediaType and select storage that corresponds */
691 get_storage_from_mediatype(ua, &rx->name_list, rx);
693 if (find_arg(ua, _("all")) < 0) {
694 /* Let the user select which files to restore */
695 user_select_files_from_tree(&tree);
699 * Walk down through the tree finding all files marked to be
700 * extracted making a bootstrap file.
702 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
703 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
705 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
706 add_findex(rx->bsr, node->JobId, node->FileIndex);
707 rx->selected_files++;
711 free_tree(tree.root); /* free the directory tree */
716 * This routine is used to get the current backup or a backup
717 * before the specified date.
719 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
724 char fileset_name[MAX_NAME_LENGTH];
729 /* Create temp tables */
730 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
731 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
732 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
733 bsendmsg(ua, "%s\n", db_strerror(ua->db));
735 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
736 bsendmsg(ua, "%s\n", db_strerror(ua->db));
739 * Select Client from the Catalog
741 memset(&cr, 0, sizeof(cr));
742 if (!get_client_dbr(ua, &cr)) {
745 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
750 memset(&fsr, 0, sizeof(fsr));
751 i = find_arg_with_value(ua, "FileSet");
753 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
754 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
755 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
756 db_strerror(ua->db));
760 if (i < 0) { /* fileset not found */
761 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
762 start_prompt(ua, _("The defined FileSet resources are:\n"));
763 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
764 bsendmsg(ua, "%s\n", db_strerror(ua->db));
766 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
767 fileset_name, sizeof(fileset_name)) < 0) {
771 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
772 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
773 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
774 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
775 "Continuing anyway.\n"));
780 /* Find JobId of last Full backup for this client, fileset */
781 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet);
782 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
783 bsendmsg(ua, "%s\n", db_strerror(ua->db));
787 /* Find all Volumes used by that JobId */
788 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
789 bsendmsg(ua, "%s\n", db_strerror(ua->db));
792 /* Note, this is needed because I don't seem to get the callback
793 * from the call just above.
796 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
797 bsendmsg(ua, "%s\n", db_strerror(ua->db));
799 if (rx->JobTDate == 0) {
800 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
804 /* Now find all Incremental/Decremental Jobs after Full save */
805 Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
806 cr.ClientId, fsr.FileSet);
807 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
808 bsendmsg(ua, "%s\n", db_strerror(ua->db));
811 /* Get the JobIds from that list */
813 rx->last_jobid[0] = 0;
814 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
815 bsendmsg(ua, "%s\n", db_strerror(ua->db));
818 if (rx->JobIds[0] != 0) {
819 /* Display a list of Jobs selected for this restore */
820 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
822 bsendmsg(ua, _("No jobs found.\n"));
828 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
829 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
834 /* Return next JobId from comma separated list */
835 static int next_jobid_from_list(char **p, uint32_t *JobId)
841 for (int i=0; i<(int)sizeof(jobid); i++) {
842 if (*q == ',' || *q == 0) {
849 if (jobid[0] == 0 || !is_a_number(jobid)) {
853 *JobId = strtoul(jobid, NULL, 10);
858 * Callback handler to get JobId and FileIndex for files
860 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
862 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
863 rx->JobId = atoi(row[0]);
864 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
870 * Callback handler make list of JobIds
872 static int jobid_handler(void *ctx, int num_fields, char **row)
874 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
876 if (strcmp(rx->last_jobid, row[0]) == 0) {
877 return 0; /* duplicate id */
879 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
880 if (rx->JobIds[0] != 0) {
881 pm_strcat(&rx->JobIds, ",");
883 pm_strcat(&rx->JobIds, row[0]);
889 * Callback handler to pickup last Full backup JobTDate
891 static int last_full_handler(void *ctx, int num_fields, char **row)
893 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
895 rx->JobTDate = strtoll(row[1], NULL, 10);
901 * Callback handler build FileSet name prompt list
903 static int fileset_handler(void *ctx, int num_fields, char **row)
906 /* row[0] = FileSet (name) */
908 add_prompt((UAContext *)ctx, row[0]);
914 * Called here with each name to be added to the list. The name is
915 * added to the list if it is not already in the list.
917 * Used to make unique list of FileSets and MediaTypes
919 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
921 NAME_LIST *name = (NAME_LIST *)ctx;
923 if (name->num_ids == MAX_ID_LIST_LEN) {
926 if (name->num_ids == name->max_ids) {
927 if (name->max_ids == 0) {
928 name->max_ids = 1000;
929 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
931 name->max_ids = (name->max_ids * 3) / 2;
932 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
935 for (int i=0; i<name->num_ids; i++) {
936 if (strcmp(name->name[i], row[0]) == 0) {
937 return 0; /* already in list, return */
940 /* Add new name to list */
941 name->name[name->num_ids++] = bstrdup(row[0]);
947 * Print names in the list
949 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
951 for (int i=0; i < name_list->num_ids; i++) {
952 bsendmsg(ua, "%s\n", name_list->name[i]);
958 * Free names in the list
960 static void free_name_list(NAME_LIST *name_list)
962 for (int i=0; i < name_list->num_ids; i++) {
963 free(name_list->name[i]);
965 if (name_list->name) {
966 free(name_list->name);
967 name_list->name = NULL;
969 name_list->max_ids = 0;
970 name_list->num_ids = 0;
973 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
975 if (name_list->num_ids > 1) {
976 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
977 "Restore is not possible. The MediaTypes used are:\n"));
978 print_name_list(ua, name_list);
979 rx->store = select_storage_resource(ua);
983 if (name_list->num_ids == 0) {
984 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
985 rx->store = select_storage_resource(ua);
989 rx->store = get_storage_resource(ua, false /* don't use default */);
992 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
993 "MediaType %s, needed by the Jobs you selected.\n"
994 "You will be allowed to select a Storage device later.\n"),