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, const 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;
51 extern char *uar_count_files;
55 char **name; /* list of names */
56 int num_ids; /* ids stored */
57 int max_ids; /* size of array */
58 int num_del; /* number deleted */
59 int tot_ids; /* total to process */
63 /* Main structure for obtaining JobIds or Files to be restored */
68 char ClientName[MAX_NAME_LENGTH];
70 POOLMEM *JobIds; /* User entered string of JobIds */
75 uint32_t selected_files;
78 POOLMEM *fname; /* filename only */
79 POOLMEM *path; /* path only */
81 int fnl; /* filename length */
82 int pnl; /* path length */
84 bool all; /* mark all as default */
89 #define MAX_ID_LIST_LEN 1000000
92 /* Forward referenced functions */
93 static int last_full_handler(void *ctx, int num_fields, char **row);
94 static int jobid_handler(void *ctx, int num_fields, char **row);
95 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
96 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
97 static int fileset_handler(void *ctx, int num_fields, char **row);
98 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
99 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
100 static void free_name_list(NAME_LIST *name_list);
101 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
102 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
103 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
104 static void free_rx(RESTORE_CTX *rx);
105 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
106 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
107 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
109 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
110 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
111 static int get_date(UAContext *ua, char *date, int date_len);
112 static int count_handler(void *ctx, int num_fields, char **row);
118 int restore_cmd(UAContext *ua, const char *cmd)
120 RESTORE_CTX rx; /* restore context */
124 memset(&rx, 0, sizeof(rx));
125 rx.path = get_pool_memory(PM_FNAME);
126 rx.fname = get_pool_memory(PM_FNAME);
127 rx.JobIds = get_pool_memory(PM_FNAME);
128 rx.query = get_pool_memory(PM_FNAME);
131 i = find_arg_with_value(ua, "where");
133 rx.where = ua->argv[i];
140 /* Ensure there is at least one Restore Job */
142 foreach_res(job, R_JOB) {
143 if (job->JobType == JT_RESTORE) {
144 if (!rx.restore_job) {
145 rx.restore_job = job;
151 if (!rx.restore_jobs) {
153 "No Restore Job Resource found. You must create at least\n"
154 "one before running this command.\n"));
159 * Request user to select JobIds or files by various different methods
160 * last 20 jobs, where File saved, most recent backup, ...
161 * In the end, a list of files are pumped into
164 switch (user_select_jobids_or_files(ua, &rx)) {
167 case 1: /* select by jobid */
168 if (!build_directory_tree(ua, &rx)) {
169 bsendmsg(ua, _("Restore not done.\n"));
173 case 2: /* select by filename, no tree needed */
178 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
179 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
182 if (!write_bsr_file(ua, rx.bsr)) {
185 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
186 rx.selected_files==1?"":"s");
188 bsendmsg(ua, _("No files selected to be restored.\n"));
192 if (rx.restore_jobs == 1) {
193 job = rx.restore_job;
195 job = select_restore_job_resource(ua);
201 get_client_name(ua, &rx);
202 if (!rx.ClientName) {
203 bsendmsg(ua, _("No Restore Job resource found!\n"));
207 /* Build run command */
210 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
211 " where=\"%s\" files=%d",
212 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
213 working_directory, rx.where, rx.selected_files);
216 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
218 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
219 working_directory, rx.selected_files);
221 if (find_arg(ua, _("yes")) > 0) {
222 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
224 Dmsg1(400, "Submitting: %s\n", ua->cmd);
226 run_cmd(ua, ua->cmd);
236 static void free_rx(RESTORE_CTX *rx)
241 free_pool_memory(rx->JobIds);
245 free_pool_memory(rx->fname);
249 free_pool_memory(rx->path);
253 free_pool_memory(rx->query);
256 free_name_list(&rx->name_list);
259 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
261 /* If no client name specified yet, get it now */
262 if (!rx->ClientName[0]) {
264 /* try command line argument */
265 int i = find_arg_with_value(ua, _("client"));
267 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
270 memset(&cr, 0, sizeof(cr));
271 if (!get_client_dbr(ua, &cr)) {
274 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
280 * The first step in the restore process is for the user to
281 * select a list of JobIds from which he will subsequently
282 * select which files are to be restored.
284 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
287 char date[MAX_TIME_LENGTH];
288 bool have_date = false;
293 const char *list[] = {
294 "List last 20 Jobs run",
295 "List Jobs where a given File is saved",
296 "Enter list of comma separated JobIds to select",
297 "Enter SQL list command",
298 "Select the most recent backup for a client",
299 "Select backup for a client before a specified time",
300 "Enter a list of files to restore",
301 "Enter a list of files to restore before a specified time",
324 for (i=1; i<ua->argc; i++) { /* loop through arguments */
325 bool found_kw = false;
326 for (j=0; kw[j]; j++) { /* loop through keywords */
327 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
333 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
336 /* Found keyword in kw[] list, process it */
339 if (*rx->JobIds != 0) {
340 pm_strcat(rx->JobIds, ",");
342 pm_strcat(rx->JobIds, ua->argv[i]);
345 case 1: /* current */
346 bstrutime(date, sizeof(date), time(NULL));
350 if (str_to_utime(ua->argv[i]) == 0) {
351 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
354 bstrncpy(date, ua->argv[i], sizeof(date));
359 bstrutime(date, sizeof(date), time(NULL));
361 if (!get_client_name(ua, rx)) {
364 pm_strcpy(ua->cmd, ua->argv[i]);
365 insert_one_file(ua, rx, date);
366 if (rx->name_list.num_ids) {
367 /* Check MediaType and select storage that corresponds */
368 get_storage_from_mediatype(ua, &rx->name_list, rx);
374 bstrutime(date, sizeof(date), time(NULL));
376 if (!select_backups_before_date(ua, rx, date)) {
381 case 5: /* pool specified */
382 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
384 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
387 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
389 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
393 case 6: /* all specified */
397 * All keywords 7 or greater are ignored or handled by a select prompt
403 if (rx->name_list.num_ids) {
404 return 2; /* filename list made */
408 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
409 "to be restored. You will be presented several methods\n"
410 "of specifying the JobIds. Then you will be allowed to\n"
411 "select which files from those JobIds are to be restored.\n\n"));
414 /* If choice not already made above, prompt */
420 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
421 for (int i=0; list[i]; i++) {
422 add_prompt(ua, list[i]);
425 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
428 case 0: /* list last 20 Jobs run */
429 gui_save = ua->jcr->gui;
431 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
432 ua->jcr->gui = gui_save;
435 case 1: /* list where a file is saved */
436 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
439 len = strlen(ua->cmd);
440 fname = (char *)malloc(len * 2 + 1);
441 db_escape_string(fname, ua->cmd, len);
442 Mmsg(rx->query, uar_file, fname);
444 gui_save = ua->jcr->gui;
446 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
447 ua->jcr->gui = gui_save;
450 case 2: /* enter a list of JobIds */
451 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
454 pm_strcpy(rx->JobIds, ua->cmd);
456 case 3: /* Enter an SQL list command */
457 if (!get_cmd(ua, _("Enter SQL list command: "))) {
460 gui_save = ua->jcr->gui;
462 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
463 ua->jcr->gui = gui_save;
466 case 4: /* Select the most recent backups */
467 bstrutime(date, sizeof(date), time(NULL));
468 if (!select_backups_before_date(ua, rx, date)) {
472 case 5: /* select backup at specified time */
473 if (!get_date(ua, date, sizeof(date))) {
476 if (!select_backups_before_date(ua, rx, date)) {
480 case 6: /* Enter files */
481 bstrutime(date, sizeof(date), time(NULL));
482 if (!get_client_name(ua, rx)) {
485 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
486 "containg a list of file names with paths, and terminate\n"
487 "them with a blank line.\n"));
489 if (!get_cmd(ua, _("Enter full filename: "))) {
492 len = strlen(ua->cmd);
496 insert_one_file(ua, rx, date);
498 /* Check MediaType and select storage that corresponds */
499 if (rx->name_list.num_ids) {
500 get_storage_from_mediatype(ua, &rx->name_list, rx);
503 case 7: /* enter files backed up before specified time */
504 if (!get_date(ua, date, sizeof(date))) {
507 if (!get_client_name(ua, rx)) {
510 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
511 "containg a list of file names with paths, and terminate\n"
512 "them with a blank line.\n"));
514 if (!get_cmd(ua, _("Enter full filename: "))) {
517 len = strlen(ua->cmd);
521 insert_one_file(ua, rx, date);
523 /* Check MediaType and select storage that corresponds */
524 if (rx->name_list.num_ids) {
525 get_storage_from_mediatype(ua, &rx->name_list, rx);
530 case 8: /* Cancel or quit */
535 if (*rx->JobIds == 0) {
536 bsendmsg(ua, _("No Jobs selected.\n"));
539 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
540 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
542 memset(&jr, 0, sizeof(JOB_DBR));
545 for (p=rx->JobIds; ; ) {
546 int stat = get_next_jobid_from_list(&p, &JobId);
548 bsendmsg(ua, _("Invalid JobId in list.\n"));
554 if (jr.JobId == JobId) {
555 continue; /* duplicate of last JobId */
558 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
559 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
560 JobId, db_strerror(ua->db));
563 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
564 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
568 rx->TotalFiles += jr.JobFiles;
576 static int get_date(UAContext *ua, char *date, int date_len)
578 bsendmsg(ua, _("The restored files will the most current backup\n"
579 "BEFORE the date you specify below.\n\n"));
581 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
584 if (str_to_utime(ua->cmd) != 0) {
587 bsendmsg(ua, _("Improper date format.\n"));
589 bstrncpy(date, ua->cmd, date_len);
594 * Insert a single file, or read a list of files from a file
596 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
606 if ((ffd = fopen(p, "r")) == NULL) {
607 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
611 while (fgets(file, sizeof(file), ffd)) {
613 if (!insert_file_into_findex_list(ua, rx, file, date)) {
614 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
620 insert_file_into_findex_list(ua, rx, ua->cmd, date);
626 * For a given file (path+filename), split into path and file, then
627 * lookup the most recent backup in the catalog to get the JobId
628 * and FileIndex, then insert them into the findex list.
630 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
633 strip_trailing_junk(file);
634 split_path_and_filename(rx, file);
635 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
637 /* Find and insert jobid and File Index */
638 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
639 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
640 rx->query, db_strerror(ua->db));
643 bsendmsg(ua, _("No database record found for: %s\n"), file);
646 rx->selected_files++;
648 * Find the MediaTypes for this JobId and add to the name_list
650 Mmsg(rx->query, uar_mediatype, rx->JobId);
651 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
652 bsendmsg(ua, "%s", db_strerror(ua->db));
658 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
662 /* Find path without the filename.
663 * I.e. everything after the last / is a "filename".
664 * OK, maybe it is a directory name, but we treat it like
665 * a filename. If we don't find a / then the whole name
666 * must be a path name (e.g. c:).
668 for (p=f=name; *p; p++) {
670 f = p; /* set pos of last slash */
673 if (*f == '/') { /* did we find a slash? */
674 f++; /* yes, point to filename */
675 } else { /* no, whole thing must be path name */
679 /* If filename doesn't exist (i.e. root directory), we
680 * simply create a blank name consisting of a single
681 * space. This makes handling zero length filenames
686 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
687 memcpy(rx->fname, f, rx->fnl); /* copy filename */
688 rx->fname[rx->fnl] = 0;
696 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
697 memcpy(rx->path, name, rx->pnl);
698 rx->path[rx->pnl] = 0;
704 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
707 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
710 JobId_t JobId, last_JobId;
714 memset(&tree, 0, sizeof(TREE_CTX));
716 * Build the directory tree containing JobIds user selected
718 tree.root = new_tree(rx->TotalFiles);
723 * For display purposes, the same JobId, with different volumes may
724 * appear more than once, however, we only insert it once.
728 tree.FileEstimate = 0;
729 if (get_next_jobid_from_list(&p, &JobId) > 0) {
730 /* Use first JobId as estimate of the number of files to restore */
731 Mmsg(rx->query, uar_count_files, JobId);
732 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
733 bsendmsg(ua, "%s\n", db_strerror(ua->db));
736 /* Add about 25% more than this job for over estimate */
737 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
738 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
741 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
743 if (JobId == last_JobId) {
744 continue; /* eliminate duplicate JobIds */
747 bsendmsg(ua, _("\nBuilding directory tree for JobId %u ... "), JobId);
750 * Find files for this JobId and insert them in the tree
752 Mmsg(rx->query, uar_sel_files, JobId);
753 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
754 bsendmsg(ua, "%s", db_strerror(ua->db));
757 * Find the MediaTypes for this JobId and add to the name_list
759 Mmsg(rx->query, uar_mediatype, JobId);
760 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
761 bsendmsg(ua, "%s", db_strerror(ua->db));
765 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
766 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
767 tree.all?" and marked for extraction":"");
769 /* Check MediaType and select storage that corresponds */
770 get_storage_from_mediatype(ua, &rx->name_list, rx);
772 if (find_arg(ua, _("done")) < 0) {
773 /* Let the user interact in selecting which files to restore */
774 OK = user_select_files_from_tree(&tree);
778 * Walk down through the tree finding all files marked to be
779 * extracted making a bootstrap file.
782 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
783 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
784 if (node->extract || node->extract_dir) {
785 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
786 add_findex(rx->bsr, node->JobId, node->FileIndex);
787 if (node->extract && node->type != TN_NEWDIR) {
788 rx->selected_files++; /* count only saved files */
794 free_tree(tree.root); /* free the directory tree */
800 * This routine is used to get the current backup or a backup
801 * before the specified date.
803 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
808 char fileset_name[MAX_NAME_LENGTH];
810 char pool_select[MAX_NAME_LENGTH];
814 /* Create temp tables */
815 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
816 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
817 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
818 bsendmsg(ua, "%s\n", db_strerror(ua->db));
820 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
821 bsendmsg(ua, "%s\n", db_strerror(ua->db));
824 * Select Client from the Catalog
826 memset(&cr, 0, sizeof(cr));
827 if (!get_client_dbr(ua, &cr)) {
830 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
835 memset(&fsr, 0, sizeof(fsr));
836 i = find_arg_with_value(ua, "FileSet");
838 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
839 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
840 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
841 db_strerror(ua->db));
845 if (i < 0) { /* fileset not found */
846 Mmsg(rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
847 start_prompt(ua, _("The defined FileSet resources are:\n"));
848 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
849 bsendmsg(ua, "%s\n", db_strerror(ua->db));
851 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
852 fileset_name, sizeof(fileset_name)) < 0) {
856 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
857 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
858 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
859 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
860 "Continuing anyway.\n"));
864 /* If Pool specified, add PoolId specification */
868 memset(&pr, 0, sizeof(pr));
869 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
870 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
871 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
873 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
877 /* Find JobId of last Full backup for this client, fileset */
878 Mmsg(rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
880 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
881 bsendmsg(ua, "%s\n", db_strerror(ua->db));
885 /* Find all Volumes used by that JobId */
886 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
887 bsendmsg(ua, "%s\n", db_strerror(ua->db));
890 /* Note, this is needed because I don't seem to get the callback
891 * from the call just above.
894 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
895 bsendmsg(ua, "%s\n", db_strerror(ua->db));
897 if (rx->JobTDate == 0) {
898 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
902 /* Now find most recent Differental Job after Full save, if any */
903 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
904 cr.ClientId, fsr.FileSet, pool_select);
905 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
906 bsendmsg(ua, "%s\n", db_strerror(ua->db));
908 /* Now update JobTDate to lock onto Differental, if any */
910 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
911 bsendmsg(ua, "%s\n", db_strerror(ua->db));
913 if (rx->JobTDate == 0) {
914 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
918 /* Now find all Incremental Jobs after Full/dif save */
919 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
920 cr.ClientId, fsr.FileSet, pool_select);
921 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
922 bsendmsg(ua, "%s\n", db_strerror(ua->db));
925 /* Get the JobIds from that list */
927 rx->last_jobid[0] = 0;
928 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
929 bsendmsg(ua, "%s\n", db_strerror(ua->db));
932 if (rx->JobIds[0] != 0) {
933 /* Display a list of Jobs selected for this restore */
934 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
936 bsendmsg(ua, _("No jobs found.\n"));
942 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
943 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
948 /* Return next JobId from comma separated list */
949 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
955 for (int i=0; i<(int)sizeof(jobid); i++) {
956 if (*q == ',' || *q == 0) {
963 if (jobid[0] == 0 || !is_a_number(jobid)) {
967 *JobId = strtoul(jobid, NULL, 10);
971 static int count_handler(void *ctx, int num_fields, char **row)
973 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
974 rx->JobId = atoi(row[0]);
980 * Callback handler to get JobId and FileIndex for files
982 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
984 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
985 rx->JobId = atoi(row[0]);
986 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
992 * Callback handler make list of JobIds
994 static int jobid_handler(void *ctx, int num_fields, char **row)
996 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
998 if (strcmp(rx->last_jobid, row[0]) == 0) {
999 return 0; /* duplicate id */
1001 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1002 if (rx->JobIds[0] != 0) {
1003 pm_strcat(rx->JobIds, ",");
1005 pm_strcat(rx->JobIds, row[0]);
1011 * Callback handler to pickup last Full backup JobTDate
1013 static int last_full_handler(void *ctx, int num_fields, char **row)
1015 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1017 rx->JobTDate = str_to_int64(row[1]);
1022 * Callback handler build FileSet name prompt list
1024 static int fileset_handler(void *ctx, int num_fields, char **row)
1026 /* row[0] = FileSet (name) */
1028 add_prompt((UAContext *)ctx, row[0]);
1034 * Called here with each name to be added to the list. The name is
1035 * added to the list if it is not already in the list.
1037 * Used to make unique list of FileSets and MediaTypes
1039 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1041 NAME_LIST *name = (NAME_LIST *)ctx;
1043 if (name->num_ids == MAX_ID_LIST_LEN) {
1046 if (name->num_ids == name->max_ids) {
1047 if (name->max_ids == 0) {
1048 name->max_ids = 1000;
1049 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1051 name->max_ids = (name->max_ids * 3) / 2;
1052 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1055 for (int i=0; i<name->num_ids; i++) {
1056 if (strcmp(name->name[i], row[0]) == 0) {
1057 return 0; /* already in list, return */
1060 /* Add new name to list */
1061 name->name[name->num_ids++] = bstrdup(row[0]);
1067 * Print names in the list
1069 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1071 for (int i=0; i < name_list->num_ids; i++) {
1072 bsendmsg(ua, "%s\n", name_list->name[i]);
1078 * Free names in the list
1080 static void free_name_list(NAME_LIST *name_list)
1082 for (int i=0; i < name_list->num_ids; i++) {
1083 free(name_list->name[i]);
1085 if (name_list->name) {
1086 free(name_list->name);
1087 name_list->name = NULL;
1089 name_list->max_ids = 0;
1090 name_list->num_ids = 0;
1093 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1097 if (name_list->num_ids > 1) {
1098 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1099 "Restore is not possible. The MediaTypes used are:\n"));
1100 print_name_list(ua, name_list);
1101 rx->store = select_storage_resource(ua);
1105 if (name_list->num_ids == 0) {
1106 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1107 rx->store = select_storage_resource(ua);
1114 * We have a single MediaType, look it up in our Storage resource
1117 foreach_res(store, R_STORAGE) {
1118 if (strcmp(name_list->name[0], store->media_type) == 0) {
1119 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1128 /* Check if an explicit storage resource is given */
1130 int i = find_arg_with_value(ua, "storage");
1132 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1133 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1137 if (store && (store != rx->store)) {
1138 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1145 /* Take command line arg, or ask user if none */
1146 rx->store = get_storage_resource(ua, false /* don't use default */);
1149 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1150 "MediaType \"%s\", needed by the Jobs you selected.\n"
1151 "You will be allowed to select a Storage device later.\n"),
1152 name_list->name[0]);