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-2005 Kern Sibbald
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 void print_bsr(UAContext *ua, RBSR *bsr);
43 /* Imported variables */
44 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
45 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
46 extern char *uar_create_temp1, *uar_last_full, *uar_full;
47 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
48 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
49 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
50 extern char *uar_count_files, *uar_jobids_fileindex;
51 extern char *uar_jobid_fileindex_from_dir;
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 bool 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 bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
109 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
111 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
112 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
113 static int get_date(UAContext *ua, char *date, int date_len);
114 static int count_handler(void *ctx, int num_fields, char **row);
120 int restore_cmd(UAContext *ua, const char *cmd)
122 RESTORE_CTX rx; /* restore context */
126 memset(&rx, 0, sizeof(rx));
127 rx.path = get_pool_memory(PM_FNAME);
128 rx.fname = get_pool_memory(PM_FNAME);
129 rx.JobIds = get_pool_memory(PM_FNAME);
130 rx.query = get_pool_memory(PM_FNAME);
133 i = find_arg_with_value(ua, "where");
135 rx.where = ua->argv[i];
142 /* Ensure there is at least one Restore Job */
144 foreach_res(job, R_JOB) {
145 if (job->JobType == JT_RESTORE) {
146 if (!rx.restore_job) {
147 rx.restore_job = job;
153 if (!rx.restore_jobs) {
155 "No Restore Job Resource found in bacula-dir.conf.\n"
156 "You must create at least one before running this command.\n"));
161 * Request user to select JobIds or files by various different methods
162 * last 20 jobs, where File saved, most recent backup, ...
163 * In the end, a list of files are pumped into
166 switch (user_select_jobids_or_files(ua, &rx)) {
169 case 1: /* selected by jobid */
170 if (!build_directory_tree(ua, &rx)) {
171 bsendmsg(ua, _("Restore not done.\n"));
175 case 2: /* selected by filename, no tree needed */
180 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
181 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
184 if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
185 bsendmsg(ua, _("No files selected to be restored.\n"));
188 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
189 rx.selected_files==1?"":"s");
191 bsendmsg(ua, _("No files selected to be restored.\n"));
195 if (rx.restore_jobs == 1) {
196 job = rx.restore_job;
198 job = select_restore_job_resource(ua);
204 get_client_name(ua, &rx);
205 if (!rx.ClientName) {
206 bsendmsg(ua, _("No Restore Job resource found!\n"));
210 /* Build run command */
213 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
214 " where=\"%s\" files=%d catalog=\"%s\"",
215 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
216 working_directory, rx.where, rx.selected_files, ua->catalog->hdr.name);
219 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
220 " files=%d catalog=\"%s\"",
221 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
222 working_directory, rx.selected_files, ua->catalog->hdr.name);
224 if (find_arg(ua, _("yes")) > 0) {
225 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
227 Dmsg1(400, "Submitting: %s\n", ua->cmd);
229 run_cmd(ua, ua->cmd);
239 static void free_rx(RESTORE_CTX *rx)
244 free_pool_memory(rx->JobIds);
248 free_pool_memory(rx->fname);
252 free_pool_memory(rx->path);
256 free_pool_memory(rx->query);
259 free_name_list(&rx->name_list);
262 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
264 /* If no client name specified yet, get it now */
265 if (!rx->ClientName[0]) {
267 /* try command line argument */
268 int i = find_arg_with_value(ua, _("client"));
270 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
273 memset(&cr, 0, sizeof(cr));
274 if (!get_client_dbr(ua, &cr)) {
277 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
283 * The first step in the restore process is for the user to
284 * select a list of JobIds from which he will subsequently
285 * select which files are to be restored.
287 * Returns: 2 if filename list made
288 * 1 if jobid list made
291 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
294 char date[MAX_TIME_LENGTH];
295 bool have_date = false;
300 const char *list[] = {
301 "List last 20 Jobs run",
302 "List Jobs where a given File is saved",
303 "Enter list of comma separated JobIds to select",
304 "Enter SQL list command",
305 "Select the most recent backup for a client",
306 "Select backup for a client before a specified time",
307 "Enter a list of files to restore",
308 "Enter a list of files to restore before a specified time",
309 "Enter a list of directories to restore for a given JobId",
314 /* These keywords are handled in a for loop */
324 /* The keyword below are handled by individual arg lookups */
336 for (i=1; i<ua->argc; i++) { /* loop through arguments */
337 bool found_kw = false;
338 for (j=0; kw[j]; j++) { /* loop through keywords */
339 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
345 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
348 /* Found keyword in kw[] list, process it */
351 if (*rx->JobIds != 0) {
352 pm_strcat(rx->JobIds, ",");
354 pm_strcat(rx->JobIds, ua->argv[i]);
357 case 1: /* current */
358 bstrutime(date, sizeof(date), time(NULL));
362 if (str_to_utime(ua->argv[i]) == 0) {
363 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
366 bstrncpy(date, ua->argv[i], sizeof(date));
372 bstrutime(date, sizeof(date), time(NULL));
374 if (!get_client_name(ua, rx)) {
377 pm_strcpy(ua->cmd, ua->argv[i]);
378 insert_one_file_or_dir(ua, rx, date, j==4);
379 if (rx->name_list.num_ids) {
380 /* Check MediaType and select storage that corresponds */
381 get_storage_from_mediatype(ua, &rx->name_list, rx);
387 bstrutime(date, sizeof(date), time(NULL));
389 if (!select_backups_before_date(ua, rx, date)) {
394 case 6: /* pool specified */
395 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
397 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
400 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
402 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
406 case 7: /* all specified */
410 * All keywords 7 or greater are ignored or handled by a select prompt
416 if (rx->name_list.num_ids) {
417 return 2; /* filename list made */
421 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
422 "to be restored. You will be presented several methods\n"
423 "of specifying the JobIds. Then you will be allowed to\n"
424 "select which files from those JobIds are to be restored.\n\n"));
427 /* If choice not already made above, prompt */
433 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
434 for (int i=0; list[i]; i++) {
435 add_prompt(ua, list[i]);
438 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
441 case 0: /* list last 20 Jobs run */
442 gui_save = ua->jcr->gui;
444 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
445 ua->jcr->gui = gui_save;
448 case 1: /* list where a file is saved */
449 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
452 len = strlen(ua->cmd);
453 fname = (char *)malloc(len * 2 + 1);
454 db_escape_string(fname, ua->cmd, len);
455 Mmsg(rx->query, uar_file, fname);
457 gui_save = ua->jcr->gui;
459 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
460 ua->jcr->gui = gui_save;
463 case 2: /* enter a list of JobIds */
464 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
467 pm_strcpy(rx->JobIds, ua->cmd);
469 case 3: /* Enter an SQL list command */
470 if (!get_cmd(ua, _("Enter SQL list command: "))) {
473 gui_save = ua->jcr->gui;
475 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
476 ua->jcr->gui = gui_save;
479 case 4: /* Select the most recent backups */
480 bstrutime(date, sizeof(date), time(NULL));
481 if (!select_backups_before_date(ua, rx, date)) {
485 case 5: /* select backup at specified time */
486 if (!get_date(ua, date, sizeof(date))) {
489 if (!select_backups_before_date(ua, rx, date)) {
493 case 6: /* Enter files */
494 bstrutime(date, sizeof(date), time(NULL));
495 if (!get_client_name(ua, rx)) {
498 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
499 "containg a list of file names with paths, and terminate\n"
500 "them with a blank line.\n"));
502 if (!get_cmd(ua, _("Enter full filename: "))) {
505 len = strlen(ua->cmd);
509 insert_one_file_or_dir(ua, rx, date, false);
511 /* Check MediaType and select storage that corresponds */
512 if (rx->name_list.num_ids) {
513 get_storage_from_mediatype(ua, &rx->name_list, rx);
516 case 7: /* enter files backed up before specified time */
517 if (!get_date(ua, date, sizeof(date))) {
520 if (!get_client_name(ua, rx)) {
523 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
524 "containg a list of file names with paths, and terminate\n"
525 "them with a blank line.\n"));
527 if (!get_cmd(ua, _("Enter full filename: "))) {
530 len = strlen(ua->cmd);
534 insert_one_file_or_dir(ua, rx, date, false);
536 /* Check MediaType and select storage that corresponds */
537 if (rx->name_list.num_ids) {
538 get_storage_from_mediatype(ua, &rx->name_list, rx);
542 case 8: /* Enter directories */
543 if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
544 if (*rx->JobIds != 0) {
545 pm_strcat(rx->JobIds, ",");
547 pm_strcpy(rx->JobIds, ua->cmd);
549 if (*rx->JobIds != 0) {
552 bstrutime(date, sizeof(date), time(NULL));
553 if (!get_client_name(ua, rx)) {
556 bsendmsg(ua, _("Enter directory names with a trailing /, or < to enter a filename\n"
557 "containg a list of directories and terminate\n"
558 "them with a blank line.\n"));
560 if (!get_cmd(ua, _("Enter full filename: "))) {
563 len = strlen(ua->cmd);
567 insert_one_file_or_dir(ua, rx, date, true);
569 /* Check MediaType and select storage that corresponds */
570 if (rx->name_list.num_ids) {
571 get_storage_from_mediatype(ua, &rx->name_list, rx);
575 case 9: /* Cancel or quit */
580 if (*rx->JobIds == 0) {
581 bsendmsg(ua, _("No Jobs selected.\n"));
584 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
585 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
587 memset(&jr, 0, sizeof(JOB_DBR));
590 for (p=rx->JobIds; ; ) {
591 int stat = get_next_jobid_from_list(&p, &JobId);
593 bsendmsg(ua, _("Invalid JobId in list.\n"));
599 if (jr.JobId == JobId) {
600 continue; /* duplicate of last JobId */
603 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
605 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
606 edit_int64(JobId, ed1), db_strerror(ua->db));
609 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
610 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
614 rx->TotalFiles += jr.JobFiles;
622 static int get_date(UAContext *ua, char *date, int date_len)
624 bsendmsg(ua, _("The restored files will the most current backup\n"
625 "BEFORE the date you specify below.\n\n"));
627 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
630 if (str_to_utime(ua->cmd) != 0) {
633 bsendmsg(ua, _("Improper date format.\n"));
635 bstrncpy(date, ua->cmd, date_len);
640 * Insert a single file, or read a list of files from a file
642 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
652 if ((ffd = fopen(p, "r")) == NULL) {
654 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
658 while (fgets(file, sizeof(file), ffd)) {
661 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
662 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
665 if (!insert_file_into_findex_list(ua, rx, file, date)) {
666 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
674 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
676 insert_file_into_findex_list(ua, rx, ua->cmd, date);
683 * For a given file (path+filename), split into path and file, then
684 * lookup the most recent backup in the catalog to get the JobId
685 * and FileIndex, then insert them into the findex list.
687 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
692 strip_trailing_junk(file);
693 split_path_and_filename(rx, file);
694 if (*rx->JobIds == 0) {
695 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
698 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
699 rx->path, rx->fname, rx->ClientName);
702 /* Find and insert jobid and File Index */
703 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
704 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
705 rx->query, db_strerror(ua->db));
708 bsendmsg(ua, _("No database record found for: %s\n"), file);
712 * Find the MediaTypes for this JobId and add to the name_list
714 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
715 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
716 bsendmsg(ua, "%s", db_strerror(ua->db));
723 * For a given path lookup the most recent backup in the catalog
724 * to get the JobId and FileIndexes of all files in that directory.
726 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
729 strip_trailing_junk(dir);
730 if (*rx->JobIds == 0) {
731 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
734 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
735 dir, rx->ClientName);
738 /* Find and insert jobid and File Index */
739 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
740 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
741 rx->query, db_strerror(ua->db));
744 bsendmsg(ua, _("No database record found for: %s\n"), dir);
748 * Find the MediaTypes for this JobId and add to the name_list
750 Mmsg(rx->query, uar_mediatype, rx->JobId);
751 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
752 bsendmsg(ua, "%s", db_strerror(ua->db));
759 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
763 /* Find path without the filename.
764 * I.e. everything after the last / is a "filename".
765 * OK, maybe it is a directory name, but we treat it like
766 * a filename. If we don't find a / then the whole name
767 * must be a path name (e.g. c:).
769 for (p=f=name; *p; p++) {
771 f = p; /* set pos of last slash */
774 if (*f == '/') { /* did we find a slash? */
775 f++; /* yes, point to filename */
776 } else { /* no, whole thing must be path name */
780 /* If filename doesn't exist (i.e. root directory), we
781 * simply create a blank name consisting of a single
782 * space. This makes handling zero length filenames
787 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
788 memcpy(rx->fname, f, rx->fnl); /* copy filename */
789 rx->fname[rx->fnl] = 0;
797 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
798 memcpy(rx->path, name, rx->pnl);
799 rx->path[rx->pnl] = 0;
805 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
808 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
811 JobId_t JobId, last_JobId;
816 memset(&tree, 0, sizeof(TREE_CTX));
818 * Build the directory tree containing JobIds user selected
820 tree.root = new_tree(rx->TotalFiles);
825 * For display purposes, the same JobId, with different volumes may
826 * appear more than once, however, we only insert it once.
830 tree.FileEstimate = 0;
831 if (get_next_jobid_from_list(&p, &JobId) > 0) {
832 /* Use first JobId as estimate of the number of files to restore */
833 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
834 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
835 bsendmsg(ua, "%s\n", db_strerror(ua->db));
838 /* Add about 25% more than this job for over estimate */
839 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
840 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
843 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
846 if (JobId == last_JobId) {
847 continue; /* eliminate duplicate JobIds */
850 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
851 edit_int64(JobId, ed1));
854 * Find files for this JobId and insert them in the tree
856 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
857 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
858 bsendmsg(ua, "%s", db_strerror(ua->db));
861 * Find the MediaTypes for this JobId and add to the name_list
863 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
864 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
865 bsendmsg(ua, "%s", db_strerror(ua->db));
868 if (tree.FileCount == 0) {
869 bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
870 "is not possible.\n");
871 if (!get_yesno(ua, _("Do you want to restore all the files? (yes|no): "))) {
875 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
876 if (JobId == last_JobId) {
877 continue; /* eliminate duplicate JobIds */
879 add_findex_all(rx->bsr, JobId);
885 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
886 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
887 tree.all?" and marked for extraction":"");
889 /* Check MediaType and select storage that corresponds */
890 get_storage_from_mediatype(ua, &rx->name_list, rx);
892 if (find_arg(ua, _("done")) < 0) {
893 /* Let the user interact in selecting which files to restore */
894 OK = user_select_files_from_tree(&tree);
898 * Walk down through the tree finding all files marked to be
899 * extracted making a bootstrap file.
902 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
903 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
904 if (node->extract || node->extract_dir) {
905 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
906 add_findex(rx->bsr, node->JobId, node->FileIndex);
907 if (node->extract && node->type != TN_NEWDIR) {
908 rx->selected_files++; /* count only saved files */
915 free_tree(tree.root); /* free the directory tree */
921 * This routine is used to get the current backup or a backup
922 * before the specified date.
924 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
929 char fileset_name[MAX_NAME_LENGTH];
930 char ed1[50], ed2[50];
931 char pool_select[MAX_NAME_LENGTH];
935 /* Create temp tables */
936 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
937 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
938 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
939 bsendmsg(ua, "%s\n", db_strerror(ua->db));
941 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
942 bsendmsg(ua, "%s\n", db_strerror(ua->db));
945 * Select Client from the Catalog
947 memset(&cr, 0, sizeof(cr));
948 if (!get_client_dbr(ua, &cr)) {
951 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
956 memset(&fsr, 0, sizeof(fsr));
957 i = find_arg_with_value(ua, "FileSet");
959 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
960 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
961 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
962 db_strerror(ua->db));
966 if (i < 0) { /* fileset not found */
967 edit_int64(cr.ClientId, ed1);
968 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
969 start_prompt(ua, _("The defined FileSet resources are:\n"));
970 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
971 bsendmsg(ua, "%s\n", db_strerror(ua->db));
973 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
974 fileset_name, sizeof(fileset_name)) < 0) {
978 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
979 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
980 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
981 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
982 "Continuing anyway.\n"));
986 /* If Pool specified, add PoolId specification */
990 memset(&pr, 0, sizeof(pr));
991 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
992 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
993 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
994 edit_int64(pr.PoolId, ed1));
996 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1000 /* Find JobId of last Full backup for this client, fileset */
1001 edit_int64(cr.ClientId, ed1);
1002 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1004 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1005 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1009 /* Find all Volumes used by that JobId */
1010 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1011 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1014 /* Note, this is needed because I don't seem to get the callback
1015 * from the call just above.
1018 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1019 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1021 if (rx->JobTDate == 0) {
1022 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1026 /* Now find most recent Differental Job after Full save, if any */
1027 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1028 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1029 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1030 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1032 /* Now update JobTDate to lock onto Differental, if any */
1034 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1035 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1037 if (rx->JobTDate == 0) {
1038 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1042 /* Now find all Incremental Jobs after Full/dif save */
1043 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1044 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1045 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1046 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1049 /* Get the JobIds from that list */
1051 rx->last_jobid[0] = 0;
1052 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1053 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1056 if (rx->JobIds[0] != 0) {
1057 /* Display a list of Jobs selected for this restore */
1058 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1061 bsendmsg(ua, _("No jobs found.\n"));
1065 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1066 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1071 /* Return next JobId from comma separated list */
1072 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1078 for (int i=0; i<(int)sizeof(jobid); i++) {
1081 } else if (*q == ',') {
1088 if (jobid[0] == 0) {
1090 } else if (!is_a_number(jobid)) {
1091 return -1; /* error */
1094 *JobId = str_to_int64(jobid);
1098 static int count_handler(void *ctx, int num_fields, char **row)
1100 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1101 rx->JobId = str_to_int64(row[0]);
1107 * Callback handler to get JobId and FileIndex for files
1108 * can insert more than one depending on the caller.
1110 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1112 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1113 rx->JobId = str_to_int64(row[0]);
1114 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1116 rx->selected_files++;
1121 * Callback handler make list of JobIds
1123 static int jobid_handler(void *ctx, int num_fields, char **row)
1125 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1127 if (strcmp(rx->last_jobid, row[0]) == 0) {
1128 return 0; /* duplicate id */
1130 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1131 if (rx->JobIds[0] != 0) {
1132 pm_strcat(rx->JobIds, ",");
1134 pm_strcat(rx->JobIds, row[0]);
1140 * Callback handler to pickup last Full backup JobTDate
1142 static int last_full_handler(void *ctx, int num_fields, char **row)
1144 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1146 rx->JobTDate = str_to_int64(row[1]);
1151 * Callback handler build FileSet name prompt list
1153 static int fileset_handler(void *ctx, int num_fields, char **row)
1155 /* row[0] = FileSet (name) */
1157 add_prompt((UAContext *)ctx, row[0]);
1163 * Called here with each name to be added to the list. The name is
1164 * added to the list if it is not already in the list.
1166 * Used to make unique list of FileSets and MediaTypes
1168 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1170 NAME_LIST *name = (NAME_LIST *)ctx;
1172 if (name->num_ids == MAX_ID_LIST_LEN) {
1175 if (name->num_ids == name->max_ids) {
1176 if (name->max_ids == 0) {
1177 name->max_ids = 1000;
1178 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1180 name->max_ids = (name->max_ids * 3) / 2;
1181 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1184 for (int i=0; i<name->num_ids; i++) {
1185 if (strcmp(name->name[i], row[0]) == 0) {
1186 return 0; /* already in list, return */
1189 /* Add new name to list */
1190 name->name[name->num_ids++] = bstrdup(row[0]);
1196 * Print names in the list
1198 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1200 for (int i=0; i < name_list->num_ids; i++) {
1201 bsendmsg(ua, "%s\n", name_list->name[i]);
1207 * Free names in the list
1209 static void free_name_list(NAME_LIST *name_list)
1211 for (int i=0; i < name_list->num_ids; i++) {
1212 free(name_list->name[i]);
1214 if (name_list->name) {
1215 free(name_list->name);
1216 name_list->name = NULL;
1218 name_list->max_ids = 0;
1219 name_list->num_ids = 0;
1222 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1226 if (name_list->num_ids > 1) {
1227 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1228 "Restore is not possible. The MediaTypes used are:\n"));
1229 print_name_list(ua, name_list);
1230 rx->store = select_storage_resource(ua);
1234 if (name_list->num_ids == 0) {
1235 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1236 rx->store = select_storage_resource(ua);
1243 * We have a single MediaType, look it up in our Storage resource
1246 foreach_res(store, R_STORAGE) {
1247 if (strcmp(name_list->name[0], store->media_type) == 0) {
1248 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1257 /* Check if an explicit storage resource is given */
1259 int i = find_arg_with_value(ua, "storage");
1261 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1262 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1266 if (store && (store != rx->store)) {
1267 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1274 /* Take command line arg, or ask user if none */
1275 rx->store = get_storage_resource(ua, false /* don't use default */);
1278 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1279 "MediaType \"%s\", needed by the Jobs you selected.\n"
1280 "You will be allowed to select a Storage device later.\n"),
1281 name_list->name[0]);