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-2004 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, *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, *uar_dif, *uar_sel_all_temp;
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 */
74 uint32_t selected_files;
77 POOLMEM *fname; /* filename only */
78 POOLMEM *path; /* path only */
80 int fnl; /* filename length */
81 int pnl; /* path length */
87 #define MAX_ID_LIST_LEN 1000000
90 /* Forward referenced functions */
91 static int last_full_handler(void *ctx, int num_fields, char **row);
92 static int jobid_handler(void *ctx, int num_fields, char **row);
93 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
94 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
95 static int fileset_handler(void *ctx, int num_fields, char **row);
96 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
97 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
98 static void free_name_list(NAME_LIST *name_list);
99 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
100 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
101 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
102 static void free_rx(RESTORE_CTX *rx);
103 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
104 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
105 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
107 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
108 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
109 static int get_date(UAContext *ua, char *date, int date_len);
115 int restore_cmd(UAContext *ua, char *cmd)
117 RESTORE_CTX rx; /* restore context */
121 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 foreach_res(job, R_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 if (!build_directory_tree(ua, &rx)) {
166 bsendmsg(ua, _("Restore not done.\n"));
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"));
179 write_bsr_file(ua, rx.bsr);
180 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
181 rx.selected_files==1?"":"s");
183 bsendmsg(ua, _("No files selected to be restored.\n"));
187 if (rx.restore_jobs == 1) {
188 job = rx.restore_job;
190 job = select_restore_job_resource(ua);
196 get_client_name(ua, &rx);
197 if (!rx.ClientName) {
198 bsendmsg(ua, _("No Restore Job resource found!\n"));
202 /* Build run command */
205 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
206 " where=\"%s\" files=%d",
207 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
208 working_directory, rx.where, rx.selected_files);
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, _("yes")) > 0) {
216 pm_strcat(&ua->cmd, " yes"); /* 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"));
232 static void free_rx(RESTORE_CTX *rx)
237 free_pool_memory(rx->JobIds);
241 free_pool_memory(rx->fname);
245 free_pool_memory(rx->path);
249 free_pool_memory(rx->query);
252 free_name_list(&rx->name_list);
255 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
257 /* If no client name specified yet, get it now */
258 if (!rx->ClientName[0]) {
260 /* try command line argument */
261 int i = find_arg_with_value(ua, _("client"));
263 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
266 memset(&cr, 0, sizeof(cr));
267 if (!get_client_dbr(ua, &cr)) {
270 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
276 * The first step in the restore process is for the user to
277 * select a list of JobIds from which he will subsequently
278 * select which files are to be restored.
280 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
283 char date[MAX_TIME_LENGTH];
284 bool have_date = false;
290 "List last 20 Jobs run",
291 "List Jobs where a given File is saved",
292 "Enter list of comma separated JobIds to select",
293 "Enter SQL list command",
294 "Select the most recent backup for a client",
295 "Select backup for a client before a specified time",
296 "Enter a list of files to restore",
297 "Enter a list of files to restore before a specified time",
318 for (i=1; i<ua->argc; i++) { /* loop through arguments */
319 bool found_kw = false;
320 for (j=0; kw[j]; j++) { /* loop through keywords */
321 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
327 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
330 /* Found keyword in kw[] list, process it */
333 if (*rx->JobIds != 0) {
334 pm_strcat(&rx->JobIds, ",");
336 pm_strcat(&rx->JobIds, ua->argv[i]);
339 case 1: /* current */
340 bstrutime(date, sizeof(date), time(NULL));
344 if (str_to_utime(ua->argv[i]) == 0) {
345 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
348 bstrncpy(date, ua->argv[i], sizeof(date));
353 bstrutime(date, sizeof(date), time(NULL));
355 if (!get_client_name(ua, rx)) {
358 pm_strcpy(&ua->cmd, ua->argv[i]);
359 insert_one_file(ua, rx, date);
360 if (rx->name_list.num_ids) {
361 /* Check MediaType and select storage that corresponds */
362 get_storage_from_mediatype(ua, &rx->name_list, rx);
368 bstrutime(date, sizeof(date), time(NULL));
370 if (!select_backups_before_date(ua, rx, date)) {
375 case 5: /* pool specified */
376 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
378 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
381 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
383 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
388 * All keywords 6 or greater are ignored or handled by a select prompt
394 if (rx->name_list.num_ids) {
395 return 2; /* filename list made */
399 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
400 "to be restored. You will be presented several methods\n"
401 "of specifying the JobIds. Then you will be allowed to\n"
402 "select which files from those JobIds are to be restored.\n\n"));
405 /* If choice not already made above, prompt */
410 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
411 for (int i=0; list[i]; i++) {
412 add_prompt(ua, list[i]);
415 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
418 case 0: /* list last 20 Jobs run */
419 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
422 case 1: /* list where a file is saved */
423 if (!get_cmd(ua, _("Enter Filename: "))) {
426 len = strlen(ua->cmd);
427 fname = (char *)malloc(len * 2 + 1);
428 db_escape_string(fname, ua->cmd, len);
429 Mmsg(&rx->query, uar_file, fname);
431 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
434 case 2: /* enter a list of JobIds */
435 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
438 pm_strcpy(&rx->JobIds, ua->cmd);
440 case 3: /* Enter an SQL list command */
441 if (!get_cmd(ua, _("Enter SQL list command: "))) {
444 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
447 case 4: /* Select the most recent backups */
448 bstrutime(date, sizeof(date), time(NULL));
449 if (!select_backups_before_date(ua, rx, date)) {
453 case 5: /* select backup at specified time */
454 if (!get_date(ua, date, sizeof(date))) {
457 if (!select_backups_before_date(ua, rx, date)) {
461 case 6: /* Enter files */
462 bstrutime(date, sizeof(date), time(NULL));
463 if (!get_client_name(ua, rx)) {
466 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
467 "containg a list of file names, and terminate\n"
468 "them with a blank line.\n"));
470 if (!get_cmd(ua, _("Enter filename: "))) {
473 len = strlen(ua->cmd);
477 insert_one_file(ua, rx, date);
479 /* Check MediaType and select storage that corresponds */
480 if (rx->name_list.num_ids) {
481 get_storage_from_mediatype(ua, &rx->name_list, rx);
484 case 7: /* enter files backed up before specified time */
485 if (!get_date(ua, date, sizeof(date))) {
488 if (!get_client_name(ua, rx)) {
491 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
492 "containg a list of file names, and terminate\n"
493 "them with a blank line.\n"));
495 if (!get_cmd(ua, _("Enter filename: "))) {
498 len = strlen(ua->cmd);
502 insert_one_file(ua, rx, date);
504 /* Check MediaType and select storage that corresponds */
505 if (rx->name_list.num_ids) {
506 get_storage_from_mediatype(ua, &rx->name_list, rx);
511 case 8: /* Cancel or quit */
516 if (*rx->JobIds == 0) {
517 bsendmsg(ua, _("No Jobs selected.\n"));
520 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
521 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
523 memset(&jr, 0, sizeof(JOB_DBR));
526 for (p=rx->JobIds; ; ) {
527 int stat = get_next_jobid_from_list(&p, &JobId);
529 bsendmsg(ua, _("Invalid JobId in list.\n"));
535 if (jr.JobId == JobId) {
536 continue; /* duplicate of last JobId */
539 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
540 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
541 JobId, db_strerror(ua->db));
544 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
545 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
549 rx->TotalFiles += jr.JobFiles;
557 static int get_date(UAContext *ua, char *date, int date_len)
559 bsendmsg(ua, _("The restored files will the most current backup\n"
560 "BEFORE the date you specify below.\n\n"));
562 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
565 if (str_to_utime(ua->cmd) != 0) {
568 bsendmsg(ua, _("Improper date format.\n"));
570 bstrncpy(date, ua->cmd, date_len);
575 * Insert a single file, or read a list of files from a file
577 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
587 if ((ffd = fopen(p, "r")) == NULL) {
588 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
592 while (fgets(file, sizeof(file), ffd)) {
594 if (!insert_file_into_findex_list(ua, rx, file, date)) {
595 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
601 insert_file_into_findex_list(ua, rx, ua->cmd, date);
607 * For a given file (path+filename), split into path and file, then
608 * lookup the most recent backup in the catalog to get the JobId
609 * and FileIndex, then insert them into the findex list.
611 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
614 strip_trailing_junk(file);
615 split_path_and_filename(rx, file);
616 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
618 /* Find and insert jobid and File Index */
619 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
620 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
621 rx->query, db_strerror(ua->db));
624 bsendmsg(ua, _("No database record found for: %s\n"), file);
627 rx->selected_files++;
629 * Find the MediaTypes for this JobId and add to the name_list
631 Mmsg(&rx->query, uar_mediatype, rx->JobId);
632 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
633 bsendmsg(ua, "%s", db_strerror(ua->db));
639 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
643 /* Find path without the filename.
644 * I.e. everything after the last / is a "filename".
645 * OK, maybe it is a directory name, but we treat it like
646 * a filename. If we don't find a / then the whole name
647 * must be a path name (e.g. c:).
649 for (p=f=name; *p; p++) {
651 f = p; /* set pos of last slash */
654 if (*f == '/') { /* did we find a slash? */
655 f++; /* yes, point to filename */
656 } else { /* no, whole thing must be path name */
660 /* If filename doesn't exist (i.e. root directory), we
661 * simply create a blank name consisting of a single
662 * space. This makes handling zero length filenames
667 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
668 memcpy(rx->fname, f, rx->fnl); /* copy filename */
669 rx->fname[rx->fnl] = 0;
671 rx->fname[0] = ' '; /* blank filename */
678 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
679 memcpy(rx->path, name, rx->pnl);
680 rx->path[rx->pnl] = 0;
687 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
690 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
693 JobId_t JobId, last_JobId;
698 memset(&tree, 0, sizeof(TREE_CTX));
700 * Build the directory tree containing JobIds user selected
702 tree.root = new_tree(rx->TotalFiles);
703 tree.root->fname = nofname;
707 * For display purposes, the same JobId, with different volumes may
708 * appear more than once, however, we only insert it once.
711 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
713 if (JobId == last_JobId) {
714 continue; /* eliminate duplicate JobIds */
717 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
720 * Find files for this JobId and insert them in the tree
722 Mmsg(&rx->query, uar_sel_files, JobId);
723 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
724 bsendmsg(ua, "%s", db_strerror(ua->db));
727 * Find the MediaTypes for this JobId and add to the name_list
729 Mmsg(&rx->query, uar_mediatype, JobId);
730 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
731 bsendmsg(ua, "%s", db_strerror(ua->db));
734 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
735 items, items==1?"":"s");
737 /* Check MediaType and select storage that corresponds */
738 get_storage_from_mediatype(ua, &rx->name_list, rx);
740 if (find_arg(ua, _("all")) < 0) {
741 /* Let the user interact in selecting which files to restore */
742 OK = user_select_files_from_tree(&tree);
746 * Walk down through the tree finding all files marked to be
747 * extracted making a bootstrap file.
750 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
751 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
752 if (node->extract || node->extract_dir) {
753 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
754 add_findex(rx->bsr, node->JobId, node->FileIndex);
755 rx->selected_files++;
760 free_tree(tree.root); /* free the directory tree */
766 * This routine is used to get the current backup or a backup
767 * before the specified date.
769 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
774 char fileset_name[MAX_NAME_LENGTH];
776 char pool_select[MAX_NAME_LENGTH];
780 /* Create temp tables */
781 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
782 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
783 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
784 bsendmsg(ua, "%s\n", db_strerror(ua->db));
786 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
787 bsendmsg(ua, "%s\n", db_strerror(ua->db));
790 * Select Client from the Catalog
792 memset(&cr, 0, sizeof(cr));
793 if (!get_client_dbr(ua, &cr)) {
796 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
801 memset(&fsr, 0, sizeof(fsr));
802 i = find_arg_with_value(ua, "FileSet");
804 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
805 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
806 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
807 db_strerror(ua->db));
811 if (i < 0) { /* fileset not found */
812 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
813 start_prompt(ua, _("The defined FileSet resources are:\n"));
814 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
815 bsendmsg(ua, "%s\n", db_strerror(ua->db));
817 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
818 fileset_name, sizeof(fileset_name)) < 0) {
822 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
823 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
824 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
825 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
826 "Continuing anyway.\n"));
830 /* If Pool specified, add PoolId specification */
834 memset(&pr, 0, sizeof(pr));
835 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
836 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
837 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
839 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
843 /* Find JobId of last Full backup for this client, fileset */
844 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
846 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
847 bsendmsg(ua, "%s\n", db_strerror(ua->db));
851 /* Find all Volumes used by that JobId */
852 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
853 bsendmsg(ua, "%s\n", db_strerror(ua->db));
856 /* Note, this is needed because I don't seem to get the callback
857 * from the call just above.
860 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
861 bsendmsg(ua, "%s\n", db_strerror(ua->db));
863 if (rx->JobTDate == 0) {
864 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
868 /* Now find most recent Differental Job after Full save, if any */
869 Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
870 cr.ClientId, fsr.FileSet, pool_select);
871 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
872 bsendmsg(ua, "%s\n", db_strerror(ua->db));
874 /* Now update JobTDate to lock onto Differental, if any */
876 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
877 bsendmsg(ua, "%s\n", db_strerror(ua->db));
879 if (rx->JobTDate == 0) {
880 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
884 /* Now find all Incremental Jobs after Full/dif save */
885 Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
886 cr.ClientId, fsr.FileSet, pool_select);
887 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
888 bsendmsg(ua, "%s\n", db_strerror(ua->db));
891 /* Get the JobIds from that list */
893 rx->last_jobid[0] = 0;
894 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
895 bsendmsg(ua, "%s\n", db_strerror(ua->db));
898 if (rx->JobIds[0] != 0) {
899 /* Display a list of Jobs selected for this restore */
900 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
902 bsendmsg(ua, _("No jobs found.\n"));
908 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
909 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
914 /* Return next JobId from comma separated list */
915 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
921 for (int i=0; i<(int)sizeof(jobid); i++) {
922 if (*q == ',' || *q == 0) {
929 if (jobid[0] == 0 || !is_a_number(jobid)) {
933 *JobId = strtoul(jobid, NULL, 10);
938 * Callback handler to get JobId and FileIndex for files
940 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
942 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
943 rx->JobId = atoi(row[0]);
944 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
950 * Callback handler make list of JobIds
952 static int jobid_handler(void *ctx, int num_fields, char **row)
954 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
956 if (strcmp(rx->last_jobid, row[0]) == 0) {
957 return 0; /* duplicate id */
959 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
960 if (rx->JobIds[0] != 0) {
961 pm_strcat(&rx->JobIds, ",");
963 pm_strcat(&rx->JobIds, row[0]);
969 * Callback handler to pickup last Full backup JobTDate
971 static int last_full_handler(void *ctx, int num_fields, char **row)
973 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
975 rx->JobTDate = str_to_int64(row[1]);
980 * Callback handler build FileSet name prompt list
982 static int fileset_handler(void *ctx, int num_fields, char **row)
984 /* row[0] = FileSet (name) */
986 add_prompt((UAContext *)ctx, row[0]);
992 * Called here with each name to be added to the list. The name is
993 * added to the list if it is not already in the list.
995 * Used to make unique list of FileSets and MediaTypes
997 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
999 NAME_LIST *name = (NAME_LIST *)ctx;
1001 if (name->num_ids == MAX_ID_LIST_LEN) {
1004 if (name->num_ids == name->max_ids) {
1005 if (name->max_ids == 0) {
1006 name->max_ids = 1000;
1007 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1009 name->max_ids = (name->max_ids * 3) / 2;
1010 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1013 for (int i=0; i<name->num_ids; i++) {
1014 if (strcmp(name->name[i], row[0]) == 0) {
1015 return 0; /* already in list, return */
1018 /* Add new name to list */
1019 name->name[name->num_ids++] = bstrdup(row[0]);
1025 * Print names in the list
1027 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1029 for (int i=0; i < name_list->num_ids; i++) {
1030 bsendmsg(ua, "%s\n", name_list->name[i]);
1036 * Free names in the list
1038 static void free_name_list(NAME_LIST *name_list)
1040 for (int i=0; i < name_list->num_ids; i++) {
1041 free(name_list->name[i]);
1043 if (name_list->name) {
1044 free(name_list->name);
1045 name_list->name = NULL;
1047 name_list->max_ids = 0;
1048 name_list->num_ids = 0;
1051 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1055 if (name_list->num_ids > 1) {
1056 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1057 "Restore is not possible. The MediaTypes used are:\n"));
1058 print_name_list(ua, name_list);
1059 rx->store = select_storage_resource(ua);
1063 if (name_list->num_ids == 0) {
1064 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1065 rx->store = select_storage_resource(ua);
1072 * We have a single MediaType, look it up in our Storage resource
1075 foreach_res(store, R_STORAGE) {
1076 if (strcmp(name_list->name[0], store->media_type) == 0) {
1084 /* Try asking user */
1085 rx->store = get_storage_resource(ua, false /* don't use default */);
1088 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1089 "MediaType \"%s\", needed by the Jobs you selected.\n"
1090 "You will be allowed to select a Storage device later.\n"),
1091 name_list->name[0]);