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
16 Copyright (C) 2002-2005 Kern Sibbald
18 This program is free software; you can redistribute it and/or
19 modify it under the terms of the GNU General Public License
20 version 2 as ammended with additional clauses defined in the
21 file LICENSE in the main source directory.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 the file LICENSE for additional details.
35 /* Imported functions */
36 extern void print_bsr(UAContext *ua, RBSR *bsr);
38 /* Imported variables */
39 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
40 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
41 extern char *uar_create_temp1, *uar_last_full, *uar_full;
42 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
43 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
44 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
45 extern char *uar_count_files, *uar_jobids_fileindex;
46 extern char *uar_jobid_fileindex_from_dir;
50 char **name; /* list of names */
51 int num_ids; /* ids stored */
52 int max_ids; /* size of array */
53 int num_del; /* number deleted */
54 int tot_ids; /* total to process */
58 /* Main structure for obtaining JobIds or Files to be restored */
63 char ClientName[MAX_NAME_LENGTH];
65 POOLMEM *JobIds; /* User entered string of JobIds */
70 uint32_t selected_files;
73 POOLMEM *fname; /* filename only */
74 POOLMEM *path; /* path only */
76 int fnl; /* filename length */
77 int pnl; /* path length */
79 bool all; /* mark all as default */
84 #define MAX_ID_LIST_LEN 1000000
87 /* Forward referenced functions */
88 static int last_full_handler(void *ctx, int num_fields, char **row);
89 static int jobid_handler(void *ctx, int num_fields, char **row);
90 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
91 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
92 static int fileset_handler(void *ctx, int num_fields, char **row);
93 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
94 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
95 static void free_name_list(NAME_LIST *name_list);
96 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
97 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
98 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
99 static void free_rx(RESTORE_CTX *rx);
100 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
101 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
102 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
104 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
106 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
107 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
108 static int get_date(UAContext *ua, char *date, int date_len);
109 static int count_handler(void *ctx, int num_fields, char **row);
115 int restore_cmd(UAContext *ua, const 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 in bacula-dir.conf.\n"
151 "You must create at least 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: /* selected by jobid */
165 if (!build_directory_tree(ua, &rx)) {
166 bsendmsg(ua, _("Restore not done.\n"));
170 case 2: /* selected 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 if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
180 bsendmsg(ua, _("No files selected to be restored.\n"));
183 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
184 rx.selected_files==1?"":"s");
186 bsendmsg(ua, _("No files selected to be restored.\n"));
190 if (rx.restore_jobs == 1) {
191 job = rx.restore_job;
193 job = select_restore_job_resource(ua);
199 get_client_name(ua, &rx);
200 if (!rx.ClientName) {
201 bsendmsg(ua, _("No Restore Job resource found!\n"));
205 /* Build run command */
206 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
207 make_unique_restore_filename(ua, &fname);
210 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
211 " where=\"%s\" files=%d catalog=\"%s\"",
212 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
213 fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
216 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
217 " files=%d catalog=\"%s\"",
218 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
219 fname, rx.selected_files, ua->catalog->hdr.name);
221 free_pool_memory(fname);
222 if (find_arg(ua, _("yes")) > 0) {
223 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
225 Dmsg1(400, "Submitting: %s\n", ua->cmd);
227 run_cmd(ua, ua->cmd);
237 static void free_rx(RESTORE_CTX *rx)
242 free_pool_memory(rx->JobIds);
246 free_pool_memory(rx->fname);
250 free_pool_memory(rx->path);
254 free_pool_memory(rx->query);
257 free_name_list(&rx->name_list);
260 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
262 /* If no client name specified yet, get it now */
263 if (!rx->ClientName[0]) {
265 /* try command line argument */
266 int i = find_arg_with_value(ua, _("client"));
268 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
271 memset(&cr, 0, sizeof(cr));
272 if (!get_client_dbr(ua, &cr)) {
275 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
281 * The first step in the restore process is for the user to
282 * select a list of JobIds from which he will subsequently
283 * select which files are to be restored.
285 * Returns: 2 if filename list made
286 * 1 if jobid list made
289 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
292 char date[MAX_TIME_LENGTH];
293 bool have_date = false;
298 const char *list[] = {
299 "List last 20 Jobs run",
300 "List Jobs where a given File is saved",
301 "Enter list of comma separated JobIds to select",
302 "Enter SQL list command",
303 "Select the most recent backup for a client",
304 "Select backup for a client before a specified time",
305 "Enter a list of files to restore",
306 "Enter a list of files to restore before a specified time",
307 "Find the JobIds of the most recent backup for a client",
308 "Find the JobIds for a backup for a client before a specified time",
309 "Enter a list of directories to restore for given JobIds",
314 /* These keywords are handled in a for loop */
324 /* The keyword below are handled by individual arg lookups */
330 "bootstrap", /* 13 */
337 for (i=1; i<ua->argc; i++) { /* loop through arguments */
338 bool found_kw = false;
339 for (j=0; kw[j]; j++) { /* loop through keywords */
340 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
346 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
349 /* Found keyword in kw[] list, process it */
352 if (*rx->JobIds != 0) {
353 pm_strcat(rx->JobIds, ",");
355 pm_strcat(rx->JobIds, ua->argv[i]);
358 case 1: /* current */
359 bstrutime(date, sizeof(date), time(NULL));
363 if (str_to_utime(ua->argv[i]) == 0) {
364 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
367 bstrncpy(date, ua->argv[i], sizeof(date));
373 bstrutime(date, sizeof(date), time(NULL));
375 if (!get_client_name(ua, rx)) {
378 pm_strcpy(ua->cmd, ua->argv[i]);
379 insert_one_file_or_dir(ua, rx, date, j==4);
380 if (rx->name_list.num_ids) {
381 /* Check MediaType and select storage that corresponds */
382 get_storage_from_mediatype(ua, &rx->name_list, rx);
388 bstrutime(date, sizeof(date), time(NULL));
390 if (!select_backups_before_date(ua, rx, date)) {
395 case 6: /* pool specified */
396 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
398 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
401 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
403 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
407 case 7: /* all specified */
411 * All keywords 7 or greater are ignored or handled by a select prompt
417 if (rx->name_list.num_ids) {
418 return 2; /* filename list made */
422 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
423 "to be restored. You will be presented several methods\n"
424 "of specifying the JobIds. Then you will be allowed to\n"
425 "select which files from those JobIds are to be restored.\n\n"));
428 /* If choice not already made above, prompt */
434 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
435 for (int i=0; list[i]; i++) {
436 add_prompt(ua, list[i]);
439 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
442 case 0: /* list last 20 Jobs run */
443 gui_save = ua->jcr->gui;
445 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
446 ua->jcr->gui = gui_save;
449 case 1: /* list where a file is saved */
450 if (!get_client_name(ua, rx)) {
453 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
456 len = strlen(ua->cmd);
457 fname = (char *)malloc(len * 2 + 1);
458 db_escape_string(fname, ua->cmd, len);
459 Mmsg(rx->query, uar_file, rx->ClientName, fname);
461 gui_save = ua->jcr->gui;
463 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
464 ua->jcr->gui = gui_save;
467 case 2: /* enter a list of JobIds */
468 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
471 pm_strcpy(rx->JobIds, ua->cmd);
473 case 3: /* Enter an SQL list command */
474 if (!get_cmd(ua, _("Enter SQL list command: "))) {
477 gui_save = ua->jcr->gui;
479 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
480 ua->jcr->gui = gui_save;
483 case 4: /* Select the most recent backups */
484 bstrutime(date, sizeof(date), time(NULL));
485 if (!select_backups_before_date(ua, rx, date)) {
489 case 5: /* select backup at specified time */
490 if (!get_date(ua, date, sizeof(date))) {
493 if (!select_backups_before_date(ua, rx, date)) {
497 case 6: /* Enter files */
498 bstrutime(date, sizeof(date), time(NULL));
499 if (!get_client_name(ua, rx)) {
502 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
503 "containg a list of file names with paths, and terminate\n"
504 "them with a blank line.\n"));
506 if (!get_cmd(ua, _("Enter full filename: "))) {
509 len = strlen(ua->cmd);
513 insert_one_file_or_dir(ua, rx, date, false);
515 /* Check MediaType and select storage that corresponds */
516 if (rx->name_list.num_ids) {
517 get_storage_from_mediatype(ua, &rx->name_list, rx);
520 case 7: /* enter files backed up before specified time */
521 if (!get_date(ua, date, sizeof(date))) {
524 if (!get_client_name(ua, rx)) {
527 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
528 "containg a list of file names with paths, and terminate\n"
529 "them with a blank line.\n"));
531 if (!get_cmd(ua, _("Enter full filename: "))) {
534 len = strlen(ua->cmd);
538 insert_one_file_or_dir(ua, rx, date, false);
540 /* Check MediaType and select storage that corresponds */
541 if (rx->name_list.num_ids) {
542 get_storage_from_mediatype(ua, &rx->name_list, rx);
546 case 8: /* Find JobIds for current backup */
547 bstrutime(date, sizeof(date), time(NULL));
548 if (!select_backups_before_date(ua, rx, date)) {
554 case 9: /* Find JobIds for give date */
555 if (!get_date(ua, date, sizeof(date))) {
558 if (!select_backups_before_date(ua, rx, date)) {
564 case 10: /* Enter directories */
565 if (*rx->JobIds != 0) {
566 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
568 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
569 if (*rx->JobIds != 0 && *ua->cmd) {
570 pm_strcat(rx->JobIds, ",");
572 pm_strcat(rx->JobIds, ua->cmd);
574 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
575 return 0; /* nothing entered, return */
577 bstrutime(date, sizeof(date), time(NULL));
578 if (!get_client_name(ua, rx)) {
581 bsendmsg(ua, _("Enter directory names with a trailing /, or < to enter a filename\n"
582 "containg a list of directories and terminate\n"
583 "them with a blank line.\n"));
585 if (!get_cmd(ua, _("Enter directory name: "))) {
588 len = strlen(ua->cmd);
592 if (ua->cmd[len-1] != '/') {
593 strcat(ua->cmd, "/");
595 insert_one_file_or_dir(ua, rx, date, true);
597 /* Check MediaType and select storage that corresponds */
598 if (rx->name_list.num_ids) {
599 get_storage_from_mediatype(ua, &rx->name_list, rx);
603 case 11: /* Cancel or quit */
608 if (*rx->JobIds == 0) {
609 bsendmsg(ua, _("No Jobs selected.\n"));
612 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
613 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
615 memset(&jr, 0, sizeof(JOB_DBR));
618 for (p=rx->JobIds; ; ) {
619 int stat = get_next_jobid_from_list(&p, &JobId);
621 bsendmsg(ua, _("Invalid JobId in list.\n"));
627 if (jr.JobId == JobId) {
628 continue; /* duplicate of last JobId */
631 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
633 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
634 edit_int64(JobId, ed1), db_strerror(ua->db));
637 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
638 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
642 rx->TotalFiles += jr.JobFiles;
650 static int get_date(UAContext *ua, char *date, int date_len)
652 bsendmsg(ua, _("The restored files will the most current backup\n"
653 "BEFORE the date you specify below.\n\n"));
655 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
658 if (str_to_utime(ua->cmd) != 0) {
661 bsendmsg(ua, _("Improper date format.\n"));
663 bstrncpy(date, ua->cmd, date_len);
668 * Insert a single file, or read a list of files from a file
670 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
680 if ((ffd = fopen(p, "r")) == NULL) {
682 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
686 while (fgets(file, sizeof(file), ffd)) {
689 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
690 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
693 if (!insert_file_into_findex_list(ua, rx, file, date)) {
694 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
702 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
704 insert_file_into_findex_list(ua, rx, ua->cmd, date);
711 * For a given file (path+filename), split into path and file, then
712 * lookup the most recent backup in the catalog to get the JobId
713 * and FileIndex, then insert them into the findex list.
715 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
720 strip_trailing_junk(file);
721 split_path_and_filename(rx, file);
722 if (*rx->JobIds == 0) {
723 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
726 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
727 rx->path, rx->fname, rx->ClientName);
730 /* Find and insert jobid and File Index */
731 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
732 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
733 rx->query, db_strerror(ua->db));
736 bsendmsg(ua, _("No database record found for: %s\n"), file);
740 * Find the MediaTypes for this JobId and add to the name_list
742 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
743 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
744 bsendmsg(ua, "%s", db_strerror(ua->db));
751 * For a given path lookup the most recent backup in the catalog
752 * to get the JobId and FileIndexes of all files in that directory.
754 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
759 strip_trailing_junk(dir);
760 if (*rx->JobIds == 0) {
761 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
764 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
765 dir, rx->ClientName);
768 /* Find and insert jobid and File Index */
769 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
770 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
771 rx->query, db_strerror(ua->db));
774 bsendmsg(ua, _("No database record found for: %s\n"), dir);
778 * Find the MediaTypes for this JobId and add to the name_list
780 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
781 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
782 bsendmsg(ua, "%s", db_strerror(ua->db));
789 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
793 /* Find path without the filename.
794 * I.e. everything after the last / is a "filename".
795 * OK, maybe it is a directory name, but we treat it like
796 * a filename. If we don't find a / then the whole name
797 * must be a path name (e.g. c:).
799 for (p=f=name; *p; p++) {
801 f = p; /* set pos of last slash */
804 if (*f == '/') { /* did we find a slash? */
805 f++; /* yes, point to filename */
806 } else { /* no, whole thing must be path name */
810 /* If filename doesn't exist (i.e. root directory), we
811 * simply create a blank name consisting of a single
812 * space. This makes handling zero length filenames
817 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
818 memcpy(rx->fname, f, rx->fnl); /* copy filename */
819 rx->fname[rx->fnl] = 0;
827 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
828 memcpy(rx->path, name, rx->pnl);
829 rx->path[rx->pnl] = 0;
835 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
838 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
841 JobId_t JobId, last_JobId;
846 memset(&tree, 0, sizeof(TREE_CTX));
848 * Build the directory tree containing JobIds user selected
850 tree.root = new_tree(rx->TotalFiles);
855 * For display purposes, the same JobId, with different volumes may
856 * appear more than once, however, we only insert it once.
860 tree.FileEstimate = 0;
861 if (get_next_jobid_from_list(&p, &JobId) > 0) {
862 /* Use first JobId as estimate of the number of files to restore */
863 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
864 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
865 bsendmsg(ua, "%s\n", db_strerror(ua->db));
868 /* Add about 25% more than this job for over estimate */
869 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
870 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
873 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
876 if (JobId == last_JobId) {
877 continue; /* eliminate duplicate JobIds */
880 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
881 edit_int64(JobId, ed1));
884 * Find files for this JobId and insert them in the tree
886 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
887 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
888 bsendmsg(ua, "%s", db_strerror(ua->db));
891 * Find the MediaTypes for this JobId and add to the name_list
893 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
894 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
895 bsendmsg(ua, "%s", db_strerror(ua->db));
898 if (tree.FileCount == 0) {
899 bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
900 "is not possible.Most likely your retention policy pruned the files\n");
901 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
905 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
906 if (JobId == last_JobId) {
907 continue; /* eliminate duplicate JobIds */
909 add_findex_all(rx->bsr, JobId);
915 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
916 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
917 tree.all?" and marked for extraction":"");
919 /* Check MediaType and select storage that corresponds */
920 get_storage_from_mediatype(ua, &rx->name_list, rx);
922 if (find_arg(ua, _("done")) < 0) {
923 /* Let the user interact in selecting which files to restore */
924 OK = user_select_files_from_tree(&tree);
928 * Walk down through the tree finding all files marked to be
929 * extracted making a bootstrap file.
932 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
933 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
934 if (node->extract || node->extract_dir) {
935 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
936 add_findex(rx->bsr, node->JobId, node->FileIndex);
937 if (node->extract && node->type != TN_NEWDIR) {
938 rx->selected_files++; /* count only saved files */
945 free_tree(tree.root); /* free the directory tree */
951 * This routine is used to get the current backup or a backup
952 * before the specified date.
954 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
959 char fileset_name[MAX_NAME_LENGTH];
960 char ed1[50], ed2[50];
961 char pool_select[MAX_NAME_LENGTH];
965 /* Create temp tables */
966 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
967 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
968 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
969 bsendmsg(ua, "%s\n", db_strerror(ua->db));
971 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
972 bsendmsg(ua, "%s\n", db_strerror(ua->db));
975 * Select Client from the Catalog
977 memset(&cr, 0, sizeof(cr));
978 if (!get_client_dbr(ua, &cr)) {
981 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
986 memset(&fsr, 0, sizeof(fsr));
987 i = find_arg_with_value(ua, "FileSet");
989 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
990 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
991 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
992 db_strerror(ua->db));
996 if (i < 0) { /* fileset not found */
997 edit_int64(cr.ClientId, ed1);
998 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
999 start_prompt(ua, _("The defined FileSet resources are:\n"));
1000 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1001 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1003 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1004 fileset_name, sizeof(fileset_name)) < 0) {
1008 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1009 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1010 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1011 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1012 "Continuing anyway.\n"));
1016 /* If Pool specified, add PoolId specification */
1020 memset(&pr, 0, sizeof(pr));
1021 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1022 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1023 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1024 edit_int64(pr.PoolId, ed1));
1026 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1030 /* Find JobId of last Full backup for this client, fileset */
1031 edit_int64(cr.ClientId, ed1);
1032 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1034 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1035 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1039 /* Find all Volumes used by that JobId */
1040 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1041 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1044 /* Note, this is needed because I don't seem to get the callback
1045 * from the call just above.
1048 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1049 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1051 if (rx->JobTDate == 0) {
1052 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1056 /* Now find most recent Differental Job after Full save, if any */
1057 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1058 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1059 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1060 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1062 /* Now update JobTDate to lock onto Differental, if any */
1064 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1065 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1067 if (rx->JobTDate == 0) {
1068 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1072 /* Now find all Incremental Jobs after Full/dif save */
1073 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1074 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1075 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1076 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1079 /* Get the JobIds from that list */
1081 rx->last_jobid[0] = 0;
1082 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1083 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1086 if (rx->JobIds[0] != 0) {
1087 /* Display a list of Jobs selected for this restore */
1088 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1091 bsendmsg(ua, _("No jobs found.\n"));
1095 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1096 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1101 /* Return next JobId from comma separated list */
1102 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1108 for (int i=0; i<(int)sizeof(jobid); i++) {
1111 } else if (*q == ',') {
1118 if (jobid[0] == 0) {
1120 } else if (!is_a_number(jobid)) {
1121 return -1; /* error */
1124 *JobId = str_to_int64(jobid);
1128 static int count_handler(void *ctx, int num_fields, char **row)
1130 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1131 rx->JobId = str_to_int64(row[0]);
1137 * Callback handler to get JobId and FileIndex for files
1138 * can insert more than one depending on the caller.
1140 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1142 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1143 rx->JobId = str_to_int64(row[0]);
1144 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1146 rx->selected_files++;
1151 * Callback handler make list of JobIds
1153 static int jobid_handler(void *ctx, int num_fields, char **row)
1155 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1157 if (strcmp(rx->last_jobid, row[0]) == 0) {
1158 return 0; /* duplicate id */
1160 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1161 if (rx->JobIds[0] != 0) {
1162 pm_strcat(rx->JobIds, ",");
1164 pm_strcat(rx->JobIds, row[0]);
1170 * Callback handler to pickup last Full backup JobTDate
1172 static int last_full_handler(void *ctx, int num_fields, char **row)
1174 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1176 rx->JobTDate = str_to_int64(row[1]);
1181 * Callback handler build FileSet name prompt list
1183 static int fileset_handler(void *ctx, int num_fields, char **row)
1185 /* row[0] = FileSet (name) */
1187 add_prompt((UAContext *)ctx, row[0]);
1193 * Called here with each name to be added to the list. The name is
1194 * added to the list if it is not already in the list.
1196 * Used to make unique list of FileSets and MediaTypes
1198 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1200 NAME_LIST *name = (NAME_LIST *)ctx;
1202 if (name->num_ids == MAX_ID_LIST_LEN) {
1205 if (name->num_ids == name->max_ids) {
1206 if (name->max_ids == 0) {
1207 name->max_ids = 1000;
1208 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1210 name->max_ids = (name->max_ids * 3) / 2;
1211 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1214 for (int i=0; i<name->num_ids; i++) {
1215 if (strcmp(name->name[i], row[0]) == 0) {
1216 return 0; /* already in list, return */
1219 /* Add new name to list */
1220 name->name[name->num_ids++] = bstrdup(row[0]);
1226 * Print names in the list
1228 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1230 for (int i=0; i < name_list->num_ids; i++) {
1231 bsendmsg(ua, "%s\n", name_list->name[i]);
1237 * Free names in the list
1239 static void free_name_list(NAME_LIST *name_list)
1241 for (int i=0; i < name_list->num_ids; i++) {
1242 free(name_list->name[i]);
1244 if (name_list->name) {
1245 free(name_list->name);
1246 name_list->name = NULL;
1248 name_list->max_ids = 0;
1249 name_list->num_ids = 0;
1252 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1256 if (name_list->num_ids > 1) {
1257 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1258 "Restore is not possible. The MediaTypes used are:\n"));
1259 print_name_list(ua, name_list);
1260 rx->store = select_storage_resource(ua);
1264 if (name_list->num_ids == 0) {
1265 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1266 rx->store = select_storage_resource(ua);
1273 * We have a single MediaType, look it up in our Storage resource
1276 foreach_res(store, R_STORAGE) {
1277 if (strcmp(name_list->name[0], store->media_type) == 0) {
1278 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1287 /* Check if an explicit storage resource is given */
1289 int i = find_arg_with_value(ua, "storage");
1291 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1292 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1296 if (store && (store != rx->store)) {
1297 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1304 /* Take command line arg, or ask user if none */
1305 rx->store = get_storage_resource(ua, false /* don't use default */);
1308 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1309 "MediaType \"%s\", needed by the Jobs you selected.\n"
1310 "You will be allowed to select a Storage device later.\n"),
1311 name_list->name[0]);