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];
88 /* Ensure there is at least one Restore Job */
90 foreach_res(job, R_JOB) {
91 if (job->JobType == JT_RESTORE) {
92 if (!rx.restore_job) {
99 if (!rx.restore_jobs) {
101 "No Restore Job Resource found in bacula-dir.conf.\n"
102 "You must create at least one before running this command.\n"));
107 * Request user to select JobIds or files by various different methods
108 * last 20 jobs, where File saved, most recent backup, ...
109 * In the end, a list of files are pumped into
112 switch (user_select_jobids_or_files(ua, &rx)) {
115 case 1: /* selected by jobid */
116 if (!build_directory_tree(ua, &rx)) {
117 bsendmsg(ua, _("Restore not done.\n"));
121 case 2: /* selected by filename, no tree needed */
126 uint32_t selected_files;
127 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
128 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
131 if (!(selected_files = write_bsr_file(ua, rx))) {
132 bsendmsg(ua, _("No files selected to be restored.\n"));
135 /* If no count of files, use bsr generated value (often wrong) */
136 if (rx.selected_files == 0) {
137 rx.selected_files = selected_files;
139 if (rx.selected_files==1) {
140 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
143 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
146 bsendmsg(ua, _("No files selected to be restored.\n"));
150 if (rx.restore_jobs == 1) {
151 job = rx.restore_job;
153 job = select_restore_job_resource(ua);
159 get_client_name(ua, &rx);
160 if (!rx.ClientName) {
161 bsendmsg(ua, _("No Restore Job resource found!\n"));
165 /* Build run command */
168 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
169 " where=\"%s\" files=%d catalog=\"%s\"",
170 job->name(), rx.ClientName, rx.store?rx.store->name():"",
171 jcr->RestoreBootstrap, rx.where, rx.selected_files, ua->catalog->name());
174 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
175 " files=%d catalog=\"%s\"",
176 job->name(), rx.ClientName, rx.store?rx.store->name():"",
177 jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name());
179 if (find_arg(ua, NT_("yes")) > 0) {
180 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
182 Dmsg1(200, "Submitting: %s\n", ua->cmd);
184 run_cmd(ua, ua->cmd);
194 static void free_rx(RESTORE_CTX *rx)
199 free_pool_memory(rx->JobIds);
203 free_pool_memory(rx->fname);
207 free_pool_memory(rx->path);
211 free_pool_memory(rx->query);
214 free_name_list(&rx->name_list);
217 static bool has_value(UAContext *ua, int i)
220 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
226 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
228 /* If no client name specified yet, get it now */
229 if (!rx->ClientName[0]) {
231 /* try command line argument */
232 int i = find_arg_with_value(ua, NT_("client"));
234 if (!has_value(ua, i)) {
237 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
240 memset(&cr, 0, sizeof(cr));
241 if (!get_client_dbr(ua, &cr)) {
244 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
251 * The first step in the restore process is for the user to
252 * select a list of JobIds from which he will subsequently
253 * select which files are to be restored.
255 * Returns: 2 if filename list made
256 * 1 if jobid list made
259 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
262 char date[MAX_TIME_LENGTH];
263 bool have_date = false;
265 JOB_DBR jr = { (JobId_t)-1 };
268 const char *list[] = {
269 _("List last 20 Jobs run"),
270 _("List Jobs where a given File is saved"),
271 _("Enter list of comma separated JobIds to select"),
272 _("Enter SQL list command"),
273 _("Select the most recent backup for a client"),
274 _("Select backup for a client before a specified time"),
275 _("Enter a list of files to restore"),
276 _("Enter a list of files to restore before a specified time"),
277 _("Find the JobIds of the most recent backup for a client"),
278 _("Find the JobIds for a backup for a client before a specified time"),
279 _("Enter a list of directories to restore for found JobIds"),
284 /* These keywords are handled in a for loop */
294 /* The keyword below are handled by individual arg lookups */
300 "bootstrap", /* 13 */
307 for (i=1; i<ua->argc; i++) { /* loop through arguments */
308 bool found_kw = false;
309 for (j=0; kw[j]; j++) { /* loop through keywords */
310 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
316 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
319 /* Found keyword in kw[] list, process it */
322 if (!has_value(ua, i)) {
325 if (*rx->JobIds != 0) {
326 pm_strcat(rx->JobIds, ",");
328 pm_strcat(rx->JobIds, ua->argv[i]);
331 case 1: /* current */
332 bstrutime(date, sizeof(date), time(NULL));
336 if (!has_value(ua, i)) {
339 if (str_to_utime(ua->argv[i]) == 0) {
340 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
343 bstrncpy(date, ua->argv[i], sizeof(date));
348 if (!has_value(ua, i)) {
352 bstrutime(date, sizeof(date), time(NULL));
354 if (!get_client_name(ua, rx)) {
357 pm_strcpy(ua->cmd, ua->argv[i]);
358 insert_one_file_or_dir(ua, rx, date, j==4);
362 bstrutime(date, sizeof(date), time(NULL));
364 if (!select_backups_before_date(ua, rx, date)) {
369 case 6: /* pool specified */
370 if (!has_value(ua, i)) {
373 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
375 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
378 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
380 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
384 case 7: /* all specified */
388 * All keywords 7 or greater are ignored or handled by a select prompt
396 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
397 "to be restored. You will be presented several methods\n"
398 "of specifying the JobIds. Then you will be allowed to\n"
399 "select which files from those JobIds are to be restored.\n\n"));
402 /* If choice not already made above, prompt */
408 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
409 for (int i=0; list[i]; i++) {
410 add_prompt(ua, list[i]);
413 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
414 case -1: /* error or cancel */
416 case 0: /* list last 20 Jobs run */
417 /* ***FIXME*** restrict clients on restricted console */
418 gui_save = ua->jcr->gui;
420 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
421 ua->jcr->gui = gui_save;
424 case 1: /* list where a file is saved */
425 if (!get_client_name(ua, rx)) {
428 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
431 len = strlen(ua->cmd);
432 fname = (char *)malloc(len * 2 + 1);
433 db_escape_string(fname, ua->cmd, len);
434 Mmsg(rx->query, uar_file, rx->ClientName, fname);
436 gui_save = ua->jcr->gui;
438 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
439 ua->jcr->gui = gui_save;
442 case 2: /* enter a list of JobIds */
443 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
446 pm_strcpy(rx->JobIds, ua->cmd);
448 case 3: /* Enter an SQL list command */
449 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
452 if (!get_cmd(ua, _("Enter SQL list command: "))) {
455 gui_save = ua->jcr->gui;
457 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
458 ua->jcr->gui = gui_save;
461 case 4: /* Select the most recent backups */
462 bstrutime(date, sizeof(date), time(NULL));
463 if (!select_backups_before_date(ua, rx, date)) {
467 case 5: /* select backup at specified time */
468 if (!get_date(ua, date, sizeof(date))) {
471 if (!select_backups_before_date(ua, rx, date)) {
475 case 6: /* Enter files */
476 bstrutime(date, sizeof(date), time(NULL));
477 if (!get_client_name(ua, rx)) {
480 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
481 "containing a list of file names with paths, and terminate\n"
482 "them with a blank line.\n"));
484 if (!get_cmd(ua, _("Enter full filename: "))) {
487 len = strlen(ua->cmd);
491 insert_one_file_or_dir(ua, rx, date, false);
494 case 7: /* enter files backed up before specified time */
495 if (!get_date(ua, date, sizeof(date))) {
498 if (!get_client_name(ua, rx)) {
501 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
502 "containing a list of file names with paths, and terminate\n"
503 "them with a blank line.\n"));
505 if (!get_cmd(ua, _("Enter full filename: "))) {
508 len = strlen(ua->cmd);
512 insert_one_file_or_dir(ua, rx, date, false);
516 case 8: /* Find JobIds for current backup */
517 bstrutime(date, sizeof(date), time(NULL));
518 if (!select_backups_before_date(ua, rx, date)) {
524 case 9: /* Find JobIds for give date */
525 if (!get_date(ua, date, sizeof(date))) {
528 if (!select_backups_before_date(ua, rx, date)) {
534 case 10: /* Enter directories */
535 if (*rx->JobIds != 0) {
536 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
538 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
539 if (*rx->JobIds != 0 && *ua->cmd) {
540 pm_strcat(rx->JobIds, ",");
542 pm_strcat(rx->JobIds, ua->cmd);
544 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
545 return 0; /* nothing entered, return */
547 bstrutime(date, sizeof(date), time(NULL));
548 if (!get_client_name(ua, rx)) {
551 bsendmsg(ua, _("Enter full directory names or start the name\n"
552 "with a < to indicate it is a filename containing a list\n"
553 "of directories and terminate them with a blank line.\n"));
555 if (!get_cmd(ua, _("Enter directory name: "))) {
558 len = strlen(ua->cmd);
562 /* Add trailing slash to end of directory names */
563 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
564 strcat(ua->cmd, "/");
566 insert_one_file_or_dir(ua, rx, date, true);
570 case 11: /* Cancel or quit */
575 if (*rx->JobIds == 0) {
576 bsendmsg(ua, _("No Jobs selected.\n"));
579 if (strchr(rx->JobIds,',')) {
580 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
583 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
588 for (p=rx->JobIds; ; ) {
589 int stat = get_next_jobid_from_list(&p, &JobId);
591 bsendmsg(ua, _("Invalid JobId in list.\n"));
597 if (jr.JobId == JobId) {
598 continue; /* duplicate of last JobId */
600 memset(&jr, 0, sizeof(JOB_DBR));
602 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
604 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
605 edit_int64(JobId, ed1), db_strerror(ua->db));
608 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
609 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
613 rx->TotalFiles += jr.JobFiles;
621 static int get_date(UAContext *ua, char *date, int date_len)
623 bsendmsg(ua, _("The restored files will the most current backup\n"
624 "BEFORE the date you specify below.\n\n"));
626 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
629 if (str_to_utime(ua->cmd) != 0) {
632 bsendmsg(ua, _("Improper date format.\n"));
634 bstrncpy(date, ua->cmd, date_len);
639 * Insert a single file, or read a list of files from a file
641 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
651 if ((ffd = fopen(p, "rb")) == NULL) {
653 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
657 while (fgets(file, sizeof(file), ffd)) {
660 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
661 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
664 if (!insert_file_into_findex_list(ua, rx, file, date)) {
665 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
673 insert_table_into_findex_list(ua, rx, p);
677 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
679 insert_file_into_findex_list(ua, rx, ua->cmd, date);
686 * For a given file (path+filename), split into path and file, then
687 * lookup the most recent backup in the catalog to get the JobId
688 * and FileIndex, then insert them into the findex list.
690 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
693 strip_trailing_newline(file);
694 split_path_and_filename(rx, file);
695 if (*rx->JobIds == 0) {
696 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
699 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
700 rx->path, rx->fname, rx->ClientName);
703 /* Find and insert jobid and File Index */
704 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
705 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
706 rx->query, db_strerror(ua->db));
709 bsendmsg(ua, _("No database record found for: %s\n"), file);
716 * For a given path lookup the most recent backup in the catalog
717 * to get the JobId and FileIndexes of all files in that directory.
719 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
722 strip_trailing_junk(dir);
723 if (*rx->JobIds == 0) {
724 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
727 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
728 dir, rx->ClientName);
731 /* Find and insert jobid and File Index */
732 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
733 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
734 rx->query, db_strerror(ua->db));
737 bsendmsg(ua, _("No database record found for: %s\n"), dir);
744 * Get the JobId and FileIndexes of all files in the specified table
746 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
748 strip_trailing_junk(table);
749 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
752 /* Find and insert jobid and File Index */
753 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
754 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
755 rx->query, db_strerror(ua->db));
758 bsendmsg(ua, _("No table found: %s\n"), table);
764 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
768 /* Find path without the filename.
769 * I.e. everything after the last / is a "filename".
770 * OK, maybe it is a directory name, but we treat it like
771 * a filename. If we don't find a / then the whole name
772 * must be a path name (e.g. c:).
774 for (p=f=name; *p; p++) {
776 f = p; /* set pos of last slash */
779 if (*f == '/') { /* did we find a slash? */
780 f++; /* yes, point to filename */
781 } else { /* no, whole thing must be path name */
785 /* If filename doesn't exist (i.e. root directory), we
786 * simply create a blank name consisting of a single
787 * space. This makes handling zero length filenames
792 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
793 memcpy(rx->fname, f, rx->fnl); /* copy filename */
794 rx->fname[rx->fnl] = 0;
802 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
803 memcpy(rx->path, name, rx->pnl);
804 rx->path[rx->pnl] = 0;
810 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
813 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
816 JobId_t JobId, last_JobId;
821 memset(&tree, 0, sizeof(TREE_CTX));
823 * Build the directory tree containing JobIds user selected
825 tree.root = new_tree(rx->TotalFiles);
830 * For display purposes, the same JobId, with different volumes may
831 * appear more than once, however, we only insert it once.
835 tree.FileEstimate = 0;
836 if (get_next_jobid_from_list(&p, &JobId) > 0) {
837 /* Use first JobId as estimate of the number of files to restore */
838 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
839 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
840 bsendmsg(ua, "%s\n", db_strerror(ua->db));
843 /* Add about 25% more than this job for over estimate */
844 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
845 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
848 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
851 if (JobId == last_JobId) {
852 continue; /* eliminate duplicate JobIds */
855 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
856 edit_int64(JobId, ed1));
859 * Find files for this JobId and insert them in the tree
861 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
862 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
863 bsendmsg(ua, "%s", db_strerror(ua->db));
866 if (tree.FileCount == 0) {
867 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
868 "is not possible.Most likely your retention policy pruned the files\n"));
869 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
873 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
874 if (JobId == last_JobId) {
875 continue; /* eliminate duplicate JobIds */
877 add_findex_all(rx->bsr, JobId);
885 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
886 edit_uint64_with_commas(tree.FileCount, ec1));
889 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
890 edit_uint64_with_commas(tree.FileCount, ec1));
895 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
896 items, edit_uint64_with_commas(tree.FileCount, ec1));
899 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
900 items, edit_uint64_with_commas(tree.FileCount, ec1));
904 if (find_arg(ua, NT_("done")) < 0) {
905 /* Let the user interact in selecting which files to restore */
906 OK = user_select_files_from_tree(&tree);
910 * Walk down through the tree finding all files marked to be
911 * extracted making a bootstrap file.
914 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
915 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
916 if (node->extract || node->extract_dir) {
917 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
918 add_findex(rx->bsr, node->JobId, node->FileIndex);
919 if (node->extract && node->type != TN_NEWDIR) {
920 rx->selected_files++; /* count only saved files */
927 free_tree(tree.root); /* free the directory tree */
933 * This routine is used to get the current backup or a backup
934 * before the specified date.
936 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
941 char fileset_name[MAX_NAME_LENGTH];
942 char ed1[50], ed2[50];
943 char pool_select[MAX_NAME_LENGTH];
947 /* Create temp tables */
948 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
949 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
950 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
951 bsendmsg(ua, "%s\n", db_strerror(ua->db));
953 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
954 bsendmsg(ua, "%s\n", db_strerror(ua->db));
957 * Select Client from the Catalog
959 memset(&cr, 0, sizeof(cr));
960 if (!get_client_dbr(ua, &cr)) {
963 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
968 memset(&fsr, 0, sizeof(fsr));
969 i = find_arg_with_value(ua, "FileSet");
971 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
972 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
973 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
974 db_strerror(ua->db));
978 if (i < 0) { /* fileset not found */
979 edit_int64(cr.ClientId, ed1);
980 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
981 start_prompt(ua, _("The defined FileSet resources are:\n"));
982 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
983 bsendmsg(ua, "%s\n", db_strerror(ua->db));
985 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
986 fileset_name, sizeof(fileset_name)) < 0) {
990 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
991 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
992 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
993 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
994 "Continuing anyway.\n"));
998 /* If Pool specified, add PoolId specification */
1002 memset(&pr, 0, sizeof(pr));
1003 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1004 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1005 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1006 edit_int64(pr.PoolId, ed1));
1008 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1012 /* Find JobId of last Full backup for this client, fileset */
1013 edit_int64(cr.ClientId, ed1);
1014 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1016 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1017 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1021 /* Find all Volumes used by that JobId */
1022 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1023 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1026 /* Note, this is needed because I don't seem to get the callback
1027 * from the call just above.
1030 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1031 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1033 if (rx->JobTDate == 0) {
1034 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1038 /* Now find most recent Differental Job after Full save, if any */
1039 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1040 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1041 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1042 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1044 /* Now update JobTDate to lock onto Differental, if any */
1046 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1047 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1049 if (rx->JobTDate == 0) {
1050 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1054 /* Now find all Incremental Jobs after Full/dif save */
1055 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1056 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1057 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1058 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1061 /* Get the JobIds from that list */
1063 rx->last_jobid[0] = 0;
1064 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1065 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1068 if (rx->JobIds[0] != 0) {
1069 /* Display a list of Jobs selected for this restore */
1070 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1073 bsendmsg(ua, _("No jobs found.\n"));
1077 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1078 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1084 * Return next JobId from comma separated list
1087 * 1 if next JobId returned
1088 * 0 if no more JobIds are in list
1089 * -1 there is an error
1091 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1097 for (int i=0; i<(int)sizeof(jobid); i++) {
1100 } else if (*q == ',') {
1107 if (jobid[0] == 0) {
1109 } else if (!is_a_number(jobid)) {
1110 return -1; /* error */
1113 *JobId = str_to_int64(jobid);
1117 static int count_handler(void *ctx, int num_fields, char **row)
1119 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1120 rx->JobId = str_to_int64(row[0]);
1126 * Callback handler to get JobId and FileIndex for files
1127 * can insert more than one depending on the caller.
1129 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1131 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1132 rx->JobId = str_to_int64(row[0]);
1133 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1135 rx->selected_files++;
1140 * Callback handler make list of JobIds
1142 static int jobid_handler(void *ctx, int num_fields, char **row)
1144 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1146 if (strcmp(rx->last_jobid, row[0]) == 0) {
1147 return 0; /* duplicate id */
1149 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1150 if (rx->JobIds[0] != 0) {
1151 pm_strcat(rx->JobIds, ",");
1153 pm_strcat(rx->JobIds, row[0]);
1159 * Callback handler to pickup last Full backup JobTDate
1161 static int last_full_handler(void *ctx, int num_fields, char **row)
1163 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1165 rx->JobTDate = str_to_int64(row[1]);
1170 * Callback handler build FileSet name prompt list
1172 static int fileset_handler(void *ctx, int num_fields, char **row)
1174 /* row[0] = FileSet (name) */
1176 add_prompt((UAContext *)ctx, row[0]);
1182 * Free names in the list
1184 static void free_name_list(NAME_LIST *name_list)
1186 for (int i=0; i < name_list->num_ids; i++) {
1187 free(name_list->name[i]);
1189 if (name_list->name) {
1190 free(name_list->name);
1191 name_list->name = NULL;
1193 name_list->max_ids = 0;
1194 name_list->num_ids = 0;
1197 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1202 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1206 * Try looking up Storage by name
1209 foreach_res(store, R_STORAGE) {
1210 if (strcmp(Storage, store->name()) == 0) {
1211 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1220 /* Check if an explicit storage resource is given */
1222 int i = find_arg_with_value(ua, "storage");
1224 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1225 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1229 if (store && (store != rx.store)) {
1230 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1233 Dmsg1(200, "Set store=%s\n", rx.store->name());
1238 /* If no storage resource, try to find one from MediaType */
1241 foreach_res(store, R_STORAGE) {
1242 if (strcmp(MediaType, store->media_type) == 0) {
1243 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1245 Dmsg1(200, "Set store=%s\n", rx.store->name());
1246 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1247 Storage, store->name(), MediaType);
1254 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1255 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1258 /* Take command line arg, or ask user if none */
1259 rx.store = get_storage_resource(ua, false /* don't use default */);
1260 Dmsg1(200, "Set store=%s\n", rx.store->name());