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);
40 /* Forward referenced functions */
41 static int last_full_handler(void *ctx, int num_fields, char **row);
42 static int jobid_handler(void *ctx, int num_fields, char **row);
43 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
44 static int fileset_handler(void *ctx, int num_fields, char **row);
45 static void free_name_list(NAME_LIST *name_list);
46 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
47 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
48 static void free_rx(RESTORE_CTX *rx);
49 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
50 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
51 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
53 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
55 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
56 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
57 static int get_date(UAContext *ua, char *date, int date_len);
58 static int count_handler(void *ctx, int num_fields, char **row);
59 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
65 int restore_cmd(UAContext *ua, const char *cmd)
67 RESTORE_CTX rx; /* restore context */
72 memset(&rx, 0, sizeof(rx));
73 rx.path = get_pool_memory(PM_FNAME);
74 rx.fname = get_pool_memory(PM_FNAME);
75 rx.JobIds = get_pool_memory(PM_FNAME);
76 rx.query = get_pool_memory(PM_FNAME);
79 i = find_arg_with_value(ua, "where");
81 rx.where = ua->argv[i];
82 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
83 bsendmsg(ua, _("Forbidden \"where\" specified.\n"));
92 /* Ensure there is at least one Restore Job */
94 foreach_res(job, R_JOB) {
95 if (job->JobType == JT_RESTORE) {
96 if (!rx.restore_job) {
103 if (!rx.restore_jobs) {
105 "No Restore Job Resource found in bacula-dir.conf.\n"
106 "You must create at least one before running this command.\n"));
111 * Request user to select JobIds or files by various different methods
112 * last 20 jobs, where File saved, most recent backup, ...
113 * In the end, a list of files are pumped into
116 switch (user_select_jobids_or_files(ua, &rx)) {
119 case 1: /* selected by jobid */
120 if (!build_directory_tree(ua, &rx)) {
121 bsendmsg(ua, _("Restore not done.\n"));
125 case 2: /* selected by filename, no tree needed */
130 uint32_t selected_files;
131 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
132 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
135 if (!(selected_files = write_bsr_file(ua, rx))) {
136 bsendmsg(ua, _("No files selected to be restored.\n"));
139 /* If no count of files, use bsr generated value (often wrong) */
140 if (rx.selected_files == 0) {
141 rx.selected_files = selected_files;
143 if (rx.selected_files==1) {
144 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
147 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
150 bsendmsg(ua, _("No files selected to be restored.\n"));
154 if (rx.restore_jobs == 1) {
155 job = rx.restore_job;
157 job = select_restore_job_resource(ua);
163 get_client_name(ua, &rx);
164 if (!rx.ClientName) {
165 bsendmsg(ua, _("No Restore Job resource found!\n"));
169 /* Build run command */
171 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
172 bsendmsg(ua, _("Forbidden \"where\" specified.\n"));
176 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
177 " where=\"%s\" files=%d catalog=\"%s\"",
178 job->name(), rx.ClientName, rx.store?rx.store->name():"",
179 jcr->RestoreBootstrap, rx.where, rx.selected_files, ua->catalog->name());
182 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
183 " files=%d catalog=\"%s\"",
184 job->name(), rx.ClientName, rx.store?rx.store->name():"",
185 jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name());
187 if (find_arg(ua, NT_("yes")) > 0) {
188 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
190 Dmsg1(200, "Submitting: %s\n", ua->cmd);
192 run_cmd(ua, ua->cmd);
202 static void free_rx(RESTORE_CTX *rx)
207 free_pool_memory(rx->JobIds);
211 free_pool_memory(rx->fname);
215 free_pool_memory(rx->path);
219 free_pool_memory(rx->query);
222 free_name_list(&rx->name_list);
225 static bool has_value(UAContext *ua, int i)
228 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
234 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
236 /* If no client name specified yet, get it now */
237 if (!rx->ClientName[0]) {
239 /* try command line argument */
240 int i = find_arg_with_value(ua, NT_("client"));
242 if (!has_value(ua, i)) {
245 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
248 memset(&cr, 0, sizeof(cr));
249 if (!get_client_dbr(ua, &cr)) {
252 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
259 * The first step in the restore process is for the user to
260 * select a list of JobIds from which he will subsequently
261 * select which files are to be restored.
263 * Returns: 2 if filename list made
264 * 1 if jobid list made
267 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
270 char date[MAX_TIME_LENGTH];
271 bool have_date = false;
273 JOB_DBR jr = { (JobId_t)-1 };
276 const char *list[] = {
277 _("List last 20 Jobs run"),
278 _("List Jobs where a given File is saved"),
279 _("Enter list of comma separated JobIds to select"),
280 _("Enter SQL list command"),
281 _("Select the most recent backup for a client"),
282 _("Select backup for a client before a specified time"),
283 _("Enter a list of files to restore"),
284 _("Enter a list of files to restore before a specified time"),
285 _("Find the JobIds of the most recent backup for a client"),
286 _("Find the JobIds for a backup for a client before a specified time"),
287 _("Enter a list of directories to restore for found JobIds"),
292 /* These keywords are handled in a for loop */
302 /* The keyword below are handled by individual arg lookups */
308 "bootstrap", /* 13 */
315 for (i=1; i<ua->argc; i++) { /* loop through arguments */
316 bool found_kw = false;
317 for (j=0; kw[j]; j++) { /* loop through keywords */
318 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
324 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
327 /* Found keyword in kw[] list, process it */
330 if (!has_value(ua, i)) {
333 if (*rx->JobIds != 0) {
334 pm_strcat(rx->JobIds, ",");
336 pm_strcat(rx->JobIds, ua->argv[i]);
339 case 1: /* current */
340 bstrutime(date, sizeof(date), time(NULL));
344 if (!has_value(ua, i)) {
347 if (str_to_utime(ua->argv[i]) == 0) {
348 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
351 bstrncpy(date, ua->argv[i], sizeof(date));
356 if (!has_value(ua, i)) {
360 bstrutime(date, sizeof(date), time(NULL));
362 if (!get_client_name(ua, rx)) {
365 pm_strcpy(ua->cmd, ua->argv[i]);
366 insert_one_file_or_dir(ua, rx, date, j==4);
370 bstrutime(date, sizeof(date), time(NULL));
372 if (!select_backups_before_date(ua, rx, date)) {
377 case 6: /* pool specified */
378 if (!has_value(ua, i)) {
381 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
383 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
386 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
388 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
392 case 7: /* all specified */
396 * All keywords 7 or greater are ignored or handled by a select prompt
404 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
405 "to be restored. You will be presented several methods\n"
406 "of specifying the JobIds. Then you will be allowed to\n"
407 "select which files from those JobIds are to be restored.\n\n"));
410 /* If choice not already made above, prompt */
416 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
417 for (int i=0; list[i]; i++) {
418 add_prompt(ua, list[i]);
421 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
422 case -1: /* error or cancel */
424 case 0: /* list last 20 Jobs run */
425 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
426 bsendmsg(ua, _("SQL query not authorized.\n"));
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_client_name(ua, rx)) {
439 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
442 len = strlen(ua->cmd);
443 fname = (char *)malloc(len * 2 + 1);
444 db_escape_string(fname, ua->cmd, len);
445 Mmsg(rx->query, uar_file, rx->ClientName, fname);
447 gui_save = ua->jcr->gui;
449 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
450 ua->jcr->gui = gui_save;
453 case 2: /* enter a list of JobIds */
454 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
457 pm_strcpy(rx->JobIds, ua->cmd);
459 case 3: /* Enter an SQL list command */
460 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
461 bsendmsg(ua, _("SQL query not authorized.\n"));
464 if (!get_cmd(ua, _("Enter SQL list command: "))) {
467 gui_save = ua->jcr->gui;
469 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
470 ua->jcr->gui = gui_save;
473 case 4: /* Select the most recent backups */
474 bstrutime(date, sizeof(date), time(NULL));
475 if (!select_backups_before_date(ua, rx, date)) {
479 case 5: /* select backup at specified time */
480 if (!get_date(ua, date, sizeof(date))) {
483 if (!select_backups_before_date(ua, rx, date)) {
487 case 6: /* Enter files */
488 bstrutime(date, sizeof(date), time(NULL));
489 if (!get_client_name(ua, rx)) {
492 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
493 "containing a list of file names with paths, and terminate\n"
494 "them with a blank line.\n"));
496 if (!get_cmd(ua, _("Enter full filename: "))) {
499 len = strlen(ua->cmd);
503 insert_one_file_or_dir(ua, rx, date, false);
506 case 7: /* enter files backed up before specified time */
507 if (!get_date(ua, date, sizeof(date))) {
510 if (!get_client_name(ua, rx)) {
513 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
514 "containing a list of file names with paths, and terminate\n"
515 "them with a blank line.\n"));
517 if (!get_cmd(ua, _("Enter full filename: "))) {
520 len = strlen(ua->cmd);
524 insert_one_file_or_dir(ua, rx, date, false);
528 case 8: /* Find JobIds for current backup */
529 bstrutime(date, sizeof(date), time(NULL));
530 if (!select_backups_before_date(ua, rx, date)) {
536 case 9: /* Find JobIds for give date */
537 if (!get_date(ua, date, sizeof(date))) {
540 if (!select_backups_before_date(ua, rx, date)) {
546 case 10: /* Enter directories */
547 if (*rx->JobIds != 0) {
548 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
550 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
551 if (*rx->JobIds != 0 && *ua->cmd) {
552 pm_strcat(rx->JobIds, ",");
554 pm_strcat(rx->JobIds, ua->cmd);
556 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
557 return 0; /* nothing entered, return */
559 bstrutime(date, sizeof(date), time(NULL));
560 if (!get_client_name(ua, rx)) {
563 bsendmsg(ua, _("Enter full directory names or start the name\n"
564 "with a < to indicate it is a filename containing a list\n"
565 "of directories and terminate them with a blank line.\n"));
567 if (!get_cmd(ua, _("Enter directory name: "))) {
570 len = strlen(ua->cmd);
574 /* Add trailing slash to end of directory names */
575 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
576 strcat(ua->cmd, "/");
578 insert_one_file_or_dir(ua, rx, date, true);
582 case 11: /* Cancel or quit */
587 if (*rx->JobIds == 0) {
588 bsendmsg(ua, _("No Jobs selected.\n"));
591 if (strchr(rx->JobIds,',')) {
592 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
595 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
600 for (p=rx->JobIds; ; ) {
601 int stat = get_next_jobid_from_list(&p, &JobId);
603 bsendmsg(ua, _("Invalid JobId in list.\n"));
609 if (jr.JobId == JobId) {
610 continue; /* duplicate of last JobId */
612 memset(&jr, 0, sizeof(JOB_DBR));
614 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
616 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
617 edit_int64(JobId, ed1), db_strerror(ua->db));
620 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
621 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
625 rx->TotalFiles += jr.JobFiles;
633 static int get_date(UAContext *ua, char *date, int date_len)
635 bsendmsg(ua, _("The restored files will the most current backup\n"
636 "BEFORE the date you specify below.\n\n"));
638 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
641 if (str_to_utime(ua->cmd) != 0) {
644 bsendmsg(ua, _("Improper date format.\n"));
646 bstrncpy(date, ua->cmd, date_len);
651 * Insert a single file, or read a list of files from a file
653 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
663 if ((ffd = fopen(p, "rb")) == NULL) {
665 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
669 while (fgets(file, sizeof(file), ffd)) {
672 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
673 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
676 if (!insert_file_into_findex_list(ua, rx, file, date)) {
677 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
685 insert_table_into_findex_list(ua, rx, p);
689 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
691 insert_file_into_findex_list(ua, rx, ua->cmd, date);
698 * For a given file (path+filename), split into path and file, then
699 * lookup the most recent backup in the catalog to get the JobId
700 * and FileIndex, then insert them into the findex list.
702 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
705 strip_trailing_newline(file);
706 split_path_and_filename(rx, file);
707 if (*rx->JobIds == 0) {
708 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
711 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
712 rx->path, rx->fname, rx->ClientName);
715 /* Find and insert jobid and File Index */
716 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
717 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
718 rx->query, db_strerror(ua->db));
721 bsendmsg(ua, _("No database record found for: %s\n"), file);
728 * For a given path lookup the most recent backup in the catalog
729 * to get the JobId and FileIndexes of all files in that directory.
731 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
734 strip_trailing_junk(dir);
735 if (*rx->JobIds == 0) {
736 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
739 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
740 dir, rx->ClientName);
743 /* Find and insert jobid and File Index */
744 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
745 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
746 rx->query, db_strerror(ua->db));
749 bsendmsg(ua, _("No database record found for: %s\n"), dir);
756 * Get the JobId and FileIndexes of all files in the specified table
758 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
760 strip_trailing_junk(table);
761 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
764 /* Find and insert jobid and File Index */
765 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
766 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
767 rx->query, db_strerror(ua->db));
770 bsendmsg(ua, _("No table found: %s\n"), table);
776 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
780 /* Find path without the filename.
781 * I.e. everything after the last / is a "filename".
782 * OK, maybe it is a directory name, but we treat it like
783 * a filename. If we don't find a / then the whole name
784 * must be a path name (e.g. c:).
786 for (p=f=name; *p; p++) {
788 f = p; /* set pos of last slash */
791 if (*f == '/') { /* did we find a slash? */
792 f++; /* yes, point to filename */
793 } else { /* no, whole thing must be path name */
797 /* If filename doesn't exist (i.e. root directory), we
798 * simply create a blank name consisting of a single
799 * space. This makes handling zero length filenames
804 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
805 memcpy(rx->fname, f, rx->fnl); /* copy filename */
806 rx->fname[rx->fnl] = 0;
814 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
815 memcpy(rx->path, name, rx->pnl);
816 rx->path[rx->pnl] = 0;
822 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
825 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
828 JobId_t JobId, last_JobId;
833 memset(&tree, 0, sizeof(TREE_CTX));
835 * Build the directory tree containing JobIds user selected
837 tree.root = new_tree(rx->TotalFiles);
842 * For display purposes, the same JobId, with different volumes may
843 * appear more than once, however, we only insert it once.
847 tree.FileEstimate = 0;
848 if (get_next_jobid_from_list(&p, &JobId) > 0) {
849 /* Use first JobId as estimate of the number of files to restore */
850 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
851 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
852 bsendmsg(ua, "%s\n", db_strerror(ua->db));
855 /* Add about 25% more than this job for over estimate */
856 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
857 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
860 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
863 if (JobId == last_JobId) {
864 continue; /* eliminate duplicate JobIds */
867 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
868 edit_int64(JobId, ed1));
871 * Find files for this JobId and insert them in the tree
873 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
874 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
875 bsendmsg(ua, "%s", db_strerror(ua->db));
878 if (tree.FileCount == 0) {
879 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
880 "is not possible.Most likely your retention policy pruned the files\n"));
881 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
885 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
886 if (JobId == last_JobId) {
887 continue; /* eliminate duplicate JobIds */
889 add_findex_all(rx->bsr, JobId);
897 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
898 edit_uint64_with_commas(tree.FileCount, ec1));
901 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
902 edit_uint64_with_commas(tree.FileCount, ec1));
907 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
908 items, edit_uint64_with_commas(tree.FileCount, ec1));
911 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
912 items, edit_uint64_with_commas(tree.FileCount, ec1));
916 if (find_arg(ua, NT_("done")) < 0) {
917 /* Let the user interact in selecting which files to restore */
918 OK = user_select_files_from_tree(&tree);
922 * Walk down through the tree finding all files marked to be
923 * extracted making a bootstrap file.
926 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
927 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
928 if (node->extract || node->extract_dir) {
929 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
930 add_findex(rx->bsr, node->JobId, node->FileIndex);
931 if (node->extract && node->type != TN_NEWDIR) {
932 rx->selected_files++; /* count only saved files */
939 free_tree(tree.root); /* free the directory tree */
945 * This routine is used to get the current backup or a backup
946 * before the specified date.
948 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
953 char fileset_name[MAX_NAME_LENGTH];
954 char ed1[50], ed2[50];
955 char pool_select[MAX_NAME_LENGTH];
959 /* Create temp tables */
960 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
961 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
962 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
963 bsendmsg(ua, "%s\n", db_strerror(ua->db));
965 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
966 bsendmsg(ua, "%s\n", db_strerror(ua->db));
969 * Select Client from the Catalog
971 memset(&cr, 0, sizeof(cr));
972 if (!get_client_dbr(ua, &cr)) {
975 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
980 memset(&fsr, 0, sizeof(fsr));
981 i = find_arg_with_value(ua, "FileSet");
983 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
984 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
985 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
986 db_strerror(ua->db));
990 if (i < 0) { /* fileset not found */
991 edit_int64(cr.ClientId, ed1);
992 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
993 start_prompt(ua, _("The defined FileSet resources are:\n"));
994 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
995 bsendmsg(ua, "%s\n", db_strerror(ua->db));
997 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
998 fileset_name, sizeof(fileset_name)) < 0) {
1002 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1003 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1004 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1005 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1006 "Continuing anyway.\n"));
1010 /* If Pool specified, add PoolId specification */
1014 memset(&pr, 0, sizeof(pr));
1015 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1016 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1017 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1018 edit_int64(pr.PoolId, ed1));
1020 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1024 /* Find JobId of last Full backup for this client, fileset */
1025 edit_int64(cr.ClientId, ed1);
1026 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1028 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1029 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1033 /* Find all Volumes used by that JobId */
1034 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1035 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1038 /* Note, this is needed because I don't seem to get the callback
1039 * from the call just above.
1042 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1043 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1045 if (rx->JobTDate == 0) {
1046 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1050 /* Now find most recent Differental Job after Full save, if any */
1051 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1052 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1053 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1054 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1056 /* Now update JobTDate to lock onto Differental, if any */
1058 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1059 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1061 if (rx->JobTDate == 0) {
1062 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1066 /* Now find all Incremental Jobs after Full/dif save */
1067 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1068 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1069 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1070 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1073 /* Get the JobIds from that list */
1075 rx->last_jobid[0] = 0;
1076 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1077 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1080 if (rx->JobIds[0] != 0) {
1081 /* Display a list of Jobs selected for this restore */
1082 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1085 bsendmsg(ua, _("No jobs found.\n"));
1089 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1090 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1096 * Return next JobId from comma separated list
1099 * 1 if next JobId returned
1100 * 0 if no more JobIds are in list
1101 * -1 there is an error
1103 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1109 for (int i=0; i<(int)sizeof(jobid); i++) {
1112 } else if (*q == ',') {
1119 if (jobid[0] == 0) {
1121 } else if (!is_a_number(jobid)) {
1122 return -1; /* error */
1125 *JobId = str_to_int64(jobid);
1129 static int count_handler(void *ctx, int num_fields, char **row)
1131 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1132 rx->JobId = str_to_int64(row[0]);
1138 * Callback handler to get JobId and FileIndex for files
1139 * can insert more than one depending on the caller.
1141 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1143 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1144 rx->JobId = str_to_int64(row[0]);
1145 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1147 rx->selected_files++;
1152 * Callback handler make list of JobIds
1154 static int jobid_handler(void *ctx, int num_fields, char **row)
1156 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1158 if (strcmp(rx->last_jobid, row[0]) == 0) {
1159 return 0; /* duplicate id */
1161 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1162 if (rx->JobIds[0] != 0) {
1163 pm_strcat(rx->JobIds, ",");
1165 pm_strcat(rx->JobIds, row[0]);
1171 * Callback handler to pickup last Full backup JobTDate
1173 static int last_full_handler(void *ctx, int num_fields, char **row)
1175 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1177 rx->JobTDate = str_to_int64(row[1]);
1182 * Callback handler build FileSet name prompt list
1184 static int fileset_handler(void *ctx, int num_fields, char **row)
1186 /* row[0] = FileSet (name) */
1188 add_prompt((UAContext *)ctx, row[0]);
1194 * Free names in the list
1196 static void free_name_list(NAME_LIST *name_list)
1198 for (int i=0; i < name_list->num_ids; i++) {
1199 free(name_list->name[i]);
1201 if (name_list->name) {
1202 free(name_list->name);
1203 name_list->name = NULL;
1205 name_list->max_ids = 0;
1206 name_list->num_ids = 0;
1209 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1214 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1218 * Try looking up Storage by name
1221 foreach_res(store, R_STORAGE) {
1222 if (strcmp(Storage, store->name()) == 0) {
1223 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1232 /* Check if an explicit storage resource is given */
1234 int i = find_arg_with_value(ua, "storage");
1236 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1237 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1241 if (store && (store != rx.store)) {
1242 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1245 Dmsg1(200, "Set store=%s\n", rx.store->name());
1250 /* If no storage resource, try to find one from MediaType */
1253 foreach_res(store, R_STORAGE) {
1254 if (strcmp(MediaType, store->media_type) == 0) {
1255 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1257 Dmsg1(200, "Set store=%s\n", rx.store->name());
1258 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1259 Storage, store->name(), MediaType);
1266 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1267 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1270 /* Take command line arg, or ask user if none */
1271 rx.store = get_storage_resource(ua, false /* don't use default */);
1272 Dmsg1(200, "Set store=%s\n", rx.store->name());