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-2006 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 amended 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 const char *uar_list_jobs, *uar_file, *uar_sel_files;
40 extern const char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
41 extern const char *uar_create_temp1, *uar_last_full, *uar_full;
42 extern const char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
43 extern const char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
44 extern const char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
45 extern const char *uar_count_files, *uar_jobids_fileindex;
46 extern const char *uar_jobid_fileindex_from_dir;
47 extern const char *uar_jobid_fileindex_from_table;
51 /* Forward referenced functions */
52 static int last_full_handler(void *ctx, int num_fields, char **row);
53 static int jobid_handler(void *ctx, int num_fields, char **row);
54 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
55 static int fileset_handler(void *ctx, int num_fields, char **row);
56 static void free_name_list(NAME_LIST *name_list);
57 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
58 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
59 static void free_rx(RESTORE_CTX *rx);
60 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
61 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
62 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
64 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
66 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
67 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
68 static int get_date(UAContext *ua, char *date, int date_len);
69 static int count_handler(void *ctx, int num_fields, char **row);
70 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
76 int restore_cmd(UAContext *ua, const char *cmd)
78 RESTORE_CTX rx; /* restore context */
83 memset(&rx, 0, sizeof(rx));
84 rx.path = get_pool_memory(PM_FNAME);
85 rx.fname = get_pool_memory(PM_FNAME);
86 rx.JobIds = get_pool_memory(PM_FNAME);
87 rx.query = get_pool_memory(PM_FNAME);
90 i = find_arg_with_value(ua, "where");
92 rx.where = ua->argv[i];
99 /* Ensure there is at least one Restore Job */
101 foreach_res(job, R_JOB) {
102 if (job->JobType == JT_RESTORE) {
103 if (!rx.restore_job) {
104 rx.restore_job = job;
110 if (!rx.restore_jobs) {
112 "No Restore Job Resource found in bacula-dir.conf.\n"
113 "You must create at least one before running this command.\n"));
118 * Request user to select JobIds or files by various different methods
119 * last 20 jobs, where File saved, most recent backup, ...
120 * In the end, a list of files are pumped into
123 switch (user_select_jobids_or_files(ua, &rx)) {
126 case 1: /* selected by jobid */
127 if (!build_directory_tree(ua, &rx)) {
128 bsendmsg(ua, _("Restore not done.\n"));
132 case 2: /* selected by filename, no tree needed */
137 uint32_t selected_files;
138 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
139 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
142 if (!(selected_files = write_bsr_file(ua, rx))) {
143 bsendmsg(ua, _("No files selected to be restored.\n"));
146 /* If no count of files, use bsr generated value (often wrong) */
147 if (rx.selected_files == 0) {
148 rx.selected_files = selected_files;
150 if (rx.selected_files==1) {
151 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
154 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
157 bsendmsg(ua, _("No files selected to be restored.\n"));
161 if (rx.restore_jobs == 1) {
162 job = rx.restore_job;
164 job = select_restore_job_resource(ua);
170 get_client_name(ua, &rx);
171 if (!rx.ClientName) {
172 bsendmsg(ua, _("No Restore Job resource found!\n"));
176 /* Build run command */
179 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
180 " where=\"%s\" files=%d catalog=\"%s\"",
181 job->name(), rx.ClientName, rx.store?rx.store->name():"",
182 jcr->RestoreBootstrap, rx.where, rx.selected_files, ua->catalog->name());
185 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
186 " files=%d catalog=\"%s\"",
187 job->name(), rx.ClientName, rx.store?rx.store->name():"",
188 jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name());
190 if (find_arg(ua, NT_("yes")) > 0) {
191 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
193 Dmsg1(200, "Submitting: %s\n", ua->cmd);
195 run_cmd(ua, ua->cmd);
205 static void free_rx(RESTORE_CTX *rx)
210 free_pool_memory(rx->JobIds);
214 free_pool_memory(rx->fname);
218 free_pool_memory(rx->path);
222 free_pool_memory(rx->query);
225 free_name_list(&rx->name_list);
228 static bool has_value(UAContext *ua, int i)
231 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
237 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
239 /* If no client name specified yet, get it now */
240 if (!rx->ClientName[0]) {
242 /* try command line argument */
243 int i = find_arg_with_value(ua, NT_("client"));
245 if (!has_value(ua, i)) {
248 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
251 memset(&cr, 0, sizeof(cr));
252 if (!get_client_dbr(ua, &cr)) {
255 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
262 * The first step in the restore process is for the user to
263 * select a list of JobIds from which he will subsequently
264 * select which files are to be restored.
266 * Returns: 2 if filename list made
267 * 1 if jobid list made
270 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
273 char date[MAX_TIME_LENGTH];
274 bool have_date = false;
276 JOB_DBR jr = { (JobId_t)-1 };
279 const char *list[] = {
280 _("List last 20 Jobs run"),
281 _("List Jobs where a given File is saved"),
282 _("Enter list of comma separated JobIds to select"),
283 _("Enter SQL list command"),
284 _("Select the most recent backup for a client"),
285 _("Select backup for a client before a specified time"),
286 _("Enter a list of files to restore"),
287 _("Enter a list of files to restore before a specified time"),
288 _("Find the JobIds of the most recent backup for a client"),
289 _("Find the JobIds for a backup for a client before a specified time"),
290 _("Enter a list of directories to restore for found JobIds"),
295 /* These keywords are handled in a for loop */
305 /* The keyword below are handled by individual arg lookups */
311 "bootstrap", /* 13 */
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 (!has_value(ua, i)) {
336 if (*rx->JobIds != 0) {
337 pm_strcat(rx->JobIds, ",");
339 pm_strcat(rx->JobIds, ua->argv[i]);
342 case 1: /* current */
343 bstrutime(date, sizeof(date), time(NULL));
347 if (!has_value(ua, i)) {
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 if (!has_value(ua, i)) {
363 bstrutime(date, sizeof(date), time(NULL));
365 if (!get_client_name(ua, rx)) {
368 pm_strcpy(ua->cmd, ua->argv[i]);
369 insert_one_file_or_dir(ua, rx, date, j==4);
373 bstrutime(date, sizeof(date), time(NULL));
375 if (!select_backups_before_date(ua, rx, date)) {
380 case 6: /* pool specified */
381 if (!has_value(ua, i)) {
384 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
386 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
389 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
391 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
395 case 7: /* all specified */
399 * All keywords 7 or greater are ignored or handled by a select prompt
407 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
408 "to be restored. You will be presented several methods\n"
409 "of specifying the JobIds. Then you will be allowed to\n"
410 "select which files from those JobIds are to be restored.\n\n"));
413 /* If choice not already made above, prompt */
419 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
420 for (int i=0; list[i]; i++) {
421 add_prompt(ua, list[i]);
424 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
425 case -1: /* error or cancel */
427 case 0: /* list last 20 Jobs run */
428 gui_save = ua->jcr->gui;
430 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
431 ua->jcr->gui = gui_save;
434 case 1: /* list where a file is saved */
435 if (!get_client_name(ua, rx)) {
438 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
441 len = strlen(ua->cmd);
442 fname = (char *)malloc(len * 2 + 1);
443 db_escape_string(fname, ua->cmd, len);
444 Mmsg(rx->query, uar_file, rx->ClientName, fname);
446 gui_save = ua->jcr->gui;
448 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
449 ua->jcr->gui = gui_save;
452 case 2: /* enter a list of JobIds */
453 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
456 pm_strcpy(rx->JobIds, ua->cmd);
458 case 3: /* Enter an SQL list command */
459 if (!get_cmd(ua, _("Enter SQL list command: "))) {
462 gui_save = ua->jcr->gui;
464 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
465 ua->jcr->gui = gui_save;
468 case 4: /* Select the most recent backups */
469 bstrutime(date, sizeof(date), time(NULL));
470 if (!select_backups_before_date(ua, rx, date)) {
474 case 5: /* select backup at specified time */
475 if (!get_date(ua, date, sizeof(date))) {
478 if (!select_backups_before_date(ua, rx, date)) {
482 case 6: /* Enter files */
483 bstrutime(date, sizeof(date), time(NULL));
484 if (!get_client_name(ua, rx)) {
487 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
488 "containing a list of file names with paths, and terminate\n"
489 "them with a blank line.\n"));
491 if (!get_cmd(ua, _("Enter full filename: "))) {
494 len = strlen(ua->cmd);
498 insert_one_file_or_dir(ua, rx, date, false);
501 case 7: /* enter files backed up before specified time */
502 if (!get_date(ua, date, sizeof(date))) {
505 if (!get_client_name(ua, rx)) {
508 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
509 "containing a list of file names with paths, and terminate\n"
510 "them with a blank line.\n"));
512 if (!get_cmd(ua, _("Enter full filename: "))) {
515 len = strlen(ua->cmd);
519 insert_one_file_or_dir(ua, rx, date, false);
523 case 8: /* Find JobIds for current backup */
524 bstrutime(date, sizeof(date), time(NULL));
525 if (!select_backups_before_date(ua, rx, date)) {
531 case 9: /* Find JobIds for give date */
532 if (!get_date(ua, date, sizeof(date))) {
535 if (!select_backups_before_date(ua, rx, date)) {
541 case 10: /* Enter directories */
542 if (*rx->JobIds != 0) {
543 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
545 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
546 if (*rx->JobIds != 0 && *ua->cmd) {
547 pm_strcat(rx->JobIds, ",");
549 pm_strcat(rx->JobIds, ua->cmd);
551 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
552 return 0; /* nothing entered, return */
554 bstrutime(date, sizeof(date), time(NULL));
555 if (!get_client_name(ua, rx)) {
558 bsendmsg(ua, _("Enter full directory names or start the name\n"
559 "with a < to indicate it is a filename containing a list\n"
560 "of directories and terminate them with a blank line.\n"));
562 if (!get_cmd(ua, _("Enter directory name: "))) {
565 len = strlen(ua->cmd);
569 /* Add trailing slash to end of directory names */
570 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
571 strcat(ua->cmd, "/");
573 insert_one_file_or_dir(ua, rx, date, true);
577 case 11: /* Cancel or quit */
582 if (*rx->JobIds == 0) {
583 bsendmsg(ua, _("No Jobs selected.\n"));
586 if (strchr(rx->JobIds,',')) {
587 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
590 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
595 for (p=rx->JobIds; ; ) {
596 int stat = get_next_jobid_from_list(&p, &JobId);
598 bsendmsg(ua, _("Invalid JobId in list.\n"));
604 if (jr.JobId == JobId) {
605 continue; /* duplicate of last JobId */
607 memset(&jr, 0, sizeof(JOB_DBR));
609 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
611 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
612 edit_int64(JobId, ed1), db_strerror(ua->db));
615 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
616 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
620 rx->TotalFiles += jr.JobFiles;
628 static int get_date(UAContext *ua, char *date, int date_len)
630 bsendmsg(ua, _("The restored files will the most current backup\n"
631 "BEFORE the date you specify below.\n\n"));
633 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
636 if (str_to_utime(ua->cmd) != 0) {
639 bsendmsg(ua, _("Improper date format.\n"));
641 bstrncpy(date, ua->cmd, date_len);
646 * Insert a single file, or read a list of files from a file
648 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
658 if ((ffd = fopen(p, "rb")) == NULL) {
660 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
664 while (fgets(file, sizeof(file), ffd)) {
667 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
668 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
671 if (!insert_file_into_findex_list(ua, rx, file, date)) {
672 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
680 insert_table_into_findex_list(ua, rx, p);
684 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
686 insert_file_into_findex_list(ua, rx, ua->cmd, date);
693 * For a given file (path+filename), split into path and file, then
694 * lookup the most recent backup in the catalog to get the JobId
695 * and FileIndex, then insert them into the findex list.
697 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
700 strip_trailing_newline(file);
701 split_path_and_filename(rx, file);
702 if (*rx->JobIds == 0) {
703 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
706 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
707 rx->path, rx->fname, rx->ClientName);
710 /* Find and insert jobid and File Index */
711 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
712 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
713 rx->query, db_strerror(ua->db));
716 bsendmsg(ua, _("No database record found for: %s\n"), file);
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);
751 * Get the JobId and FileIndexes of all files in the specified table
753 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
755 strip_trailing_junk(table);
756 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
759 /* Find and insert jobid and File Index */
760 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
761 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
762 rx->query, db_strerror(ua->db));
765 bsendmsg(ua, _("No table found: %s\n"), table);
771 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
775 /* Find path without the filename.
776 * I.e. everything after the last / is a "filename".
777 * OK, maybe it is a directory name, but we treat it like
778 * a filename. If we don't find a / then the whole name
779 * must be a path name (e.g. c:).
781 for (p=f=name; *p; p++) {
783 f = p; /* set pos of last slash */
786 if (*f == '/') { /* did we find a slash? */
787 f++; /* yes, point to filename */
788 } else { /* no, whole thing must be path name */
792 /* If filename doesn't exist (i.e. root directory), we
793 * simply create a blank name consisting of a single
794 * space. This makes handling zero length filenames
799 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
800 memcpy(rx->fname, f, rx->fnl); /* copy filename */
801 rx->fname[rx->fnl] = 0;
809 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
810 memcpy(rx->path, name, rx->pnl);
811 rx->path[rx->pnl] = 0;
817 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
820 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
823 JobId_t JobId, last_JobId;
828 memset(&tree, 0, sizeof(TREE_CTX));
830 * Build the directory tree containing JobIds user selected
832 tree.root = new_tree(rx->TotalFiles);
837 * For display purposes, the same JobId, with different volumes may
838 * appear more than once, however, we only insert it once.
842 tree.FileEstimate = 0;
843 if (get_next_jobid_from_list(&p, &JobId) > 0) {
844 /* Use first JobId as estimate of the number of files to restore */
845 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
846 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
847 bsendmsg(ua, "%s\n", db_strerror(ua->db));
850 /* Add about 25% more than this job for over estimate */
851 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
852 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
855 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
858 if (JobId == last_JobId) {
859 continue; /* eliminate duplicate JobIds */
862 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
863 edit_int64(JobId, ed1));
866 * Find files for this JobId and insert them in the tree
868 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
869 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
870 bsendmsg(ua, "%s", db_strerror(ua->db));
873 if (tree.FileCount == 0) {
874 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
875 "is not possible.Most likely your retention policy pruned the files\n"));
876 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
880 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
881 if (JobId == last_JobId) {
882 continue; /* eliminate duplicate JobIds */
884 add_findex_all(rx->bsr, JobId);
892 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
893 edit_uint64_with_commas(tree.FileCount, ec1));
896 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
897 edit_uint64_with_commas(tree.FileCount, ec1));
902 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
903 items, edit_uint64_with_commas(tree.FileCount, ec1));
906 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
907 items, edit_uint64_with_commas(tree.FileCount, ec1));
911 if (find_arg(ua, NT_("done")) < 0) {
912 /* Let the user interact in selecting which files to restore */
913 OK = user_select_files_from_tree(&tree);
917 * Walk down through the tree finding all files marked to be
918 * extracted making a bootstrap file.
921 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
922 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
923 if (node->extract || node->extract_dir) {
924 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
925 add_findex(rx->bsr, node->JobId, node->FileIndex);
926 if (node->extract && node->type != TN_NEWDIR) {
927 rx->selected_files++; /* count only saved files */
934 free_tree(tree.root); /* free the directory tree */
940 * This routine is used to get the current backup or a backup
941 * before the specified date.
943 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
948 char fileset_name[MAX_NAME_LENGTH];
949 char ed1[50], ed2[50];
950 char pool_select[MAX_NAME_LENGTH];
954 /* Create temp tables */
955 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
956 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
957 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
958 bsendmsg(ua, "%s\n", db_strerror(ua->db));
960 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
961 bsendmsg(ua, "%s\n", db_strerror(ua->db));
964 * Select Client from the Catalog
966 memset(&cr, 0, sizeof(cr));
967 if (!get_client_dbr(ua, &cr)) {
970 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
975 memset(&fsr, 0, sizeof(fsr));
976 i = find_arg_with_value(ua, "FileSet");
978 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
979 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
980 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
981 db_strerror(ua->db));
985 if (i < 0) { /* fileset not found */
986 edit_int64(cr.ClientId, ed1);
987 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
988 start_prompt(ua, _("The defined FileSet resources are:\n"));
989 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
990 bsendmsg(ua, "%s\n", db_strerror(ua->db));
992 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
993 fileset_name, sizeof(fileset_name)) < 0) {
997 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
998 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
999 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1000 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1001 "Continuing anyway.\n"));
1005 /* If Pool specified, add PoolId specification */
1009 memset(&pr, 0, sizeof(pr));
1010 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1011 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1012 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1013 edit_int64(pr.PoolId, ed1));
1015 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1019 /* Find JobId of last Full backup for this client, fileset */
1020 edit_int64(cr.ClientId, ed1);
1021 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1023 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1024 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1028 /* Find all Volumes used by that JobId */
1029 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1030 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1033 /* Note, this is needed because I don't seem to get the callback
1034 * from the call just above.
1037 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1038 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1040 if (rx->JobTDate == 0) {
1041 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1045 /* Now find most recent Differental Job after Full save, if any */
1046 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1047 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1048 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1049 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1051 /* Now update JobTDate to lock onto Differental, if any */
1053 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1054 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1056 if (rx->JobTDate == 0) {
1057 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1061 /* Now find all Incremental Jobs after Full/dif save */
1062 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1063 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1064 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1065 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1068 /* Get the JobIds from that list */
1070 rx->last_jobid[0] = 0;
1071 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1072 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1075 if (rx->JobIds[0] != 0) {
1076 /* Display a list of Jobs selected for this restore */
1077 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1080 bsendmsg(ua, _("No jobs found.\n"));
1084 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1085 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1091 * Return next JobId from comma separated list
1094 * 1 if next JobId returned
1095 * 0 if no more JobIds are in list
1096 * -1 there is an error
1098 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1104 for (int i=0; i<(int)sizeof(jobid); i++) {
1107 } else if (*q == ',') {
1114 if (jobid[0] == 0) {
1116 } else if (!is_a_number(jobid)) {
1117 return -1; /* error */
1120 *JobId = str_to_int64(jobid);
1124 static int count_handler(void *ctx, int num_fields, char **row)
1126 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1127 rx->JobId = str_to_int64(row[0]);
1133 * Callback handler to get JobId and FileIndex for files
1134 * can insert more than one depending on the caller.
1136 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1138 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1139 rx->JobId = str_to_int64(row[0]);
1140 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1142 rx->selected_files++;
1147 * Callback handler make list of JobIds
1149 static int jobid_handler(void *ctx, int num_fields, char **row)
1151 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1153 if (strcmp(rx->last_jobid, row[0]) == 0) {
1154 return 0; /* duplicate id */
1156 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1157 if (rx->JobIds[0] != 0) {
1158 pm_strcat(rx->JobIds, ",");
1160 pm_strcat(rx->JobIds, row[0]);
1166 * Callback handler to pickup last Full backup JobTDate
1168 static int last_full_handler(void *ctx, int num_fields, char **row)
1170 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1172 rx->JobTDate = str_to_int64(row[1]);
1177 * Callback handler build FileSet name prompt list
1179 static int fileset_handler(void *ctx, int num_fields, char **row)
1181 /* row[0] = FileSet (name) */
1183 add_prompt((UAContext *)ctx, row[0]);
1189 * Free names in the list
1191 static void free_name_list(NAME_LIST *name_list)
1193 for (int i=0; i < name_list->num_ids; i++) {
1194 free(name_list->name[i]);
1196 if (name_list->name) {
1197 free(name_list->name);
1198 name_list->name = NULL;
1200 name_list->max_ids = 0;
1201 name_list->num_ids = 0;
1204 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1209 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1213 * Try looking up Storage by name
1216 foreach_res(store, R_STORAGE) {
1217 if (strcmp(Storage, store->name()) == 0) {
1218 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1227 /* Check if an explicit storage resource is given */
1229 int i = find_arg_with_value(ua, "storage");
1231 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1232 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1236 if (store && (store != rx.store)) {
1237 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1240 Dmsg1(200, "Set store=%s\n", rx.store->name());
1245 /* If no storage resource, try to find one from MediaType */
1248 foreach_res(store, R_STORAGE) {
1249 if (strcmp(MediaType, store->media_type) == 0) {
1250 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1252 Dmsg1(200, "Set store=%s\n", rx.store->name());
1253 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1254 Storage, store->name(), MediaType);
1261 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1262 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1265 /* Take command line arg, or ask user if none */
1266 rx.store = get_storage_resource(ua, false /* don't use default */);
1267 Dmsg1(200, "Set store=%s\n", rx.store->name());