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 gui_save = ua->jcr->gui;
419 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
420 ua->jcr->gui = gui_save;
423 case 1: /* list where a file is saved */
424 if (!get_client_name(ua, rx)) {
427 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
430 len = strlen(ua->cmd);
431 fname = (char *)malloc(len * 2 + 1);
432 db_escape_string(fname, ua->cmd, len);
433 Mmsg(rx->query, uar_file, rx->ClientName, fname);
435 gui_save = ua->jcr->gui;
437 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
438 ua->jcr->gui = gui_save;
441 case 2: /* enter a list of JobIds */
442 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
445 pm_strcpy(rx->JobIds, ua->cmd);
447 case 3: /* Enter an SQL list command */
448 if (!get_cmd(ua, _("Enter SQL list command: "))) {
451 gui_save = ua->jcr->gui;
453 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
454 ua->jcr->gui = gui_save;
457 case 4: /* Select the most recent backups */
458 bstrutime(date, sizeof(date), time(NULL));
459 if (!select_backups_before_date(ua, rx, date)) {
463 case 5: /* select backup at specified time */
464 if (!get_date(ua, date, sizeof(date))) {
467 if (!select_backups_before_date(ua, rx, date)) {
471 case 6: /* Enter files */
472 bstrutime(date, sizeof(date), time(NULL));
473 if (!get_client_name(ua, rx)) {
476 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
477 "containing a list of file names with paths, and terminate\n"
478 "them with a blank line.\n"));
480 if (!get_cmd(ua, _("Enter full filename: "))) {
483 len = strlen(ua->cmd);
487 insert_one_file_or_dir(ua, rx, date, false);
490 case 7: /* enter files backed up before specified time */
491 if (!get_date(ua, date, sizeof(date))) {
494 if (!get_client_name(ua, rx)) {
497 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
498 "containing a list of file names with paths, and terminate\n"
499 "them with a blank line.\n"));
501 if (!get_cmd(ua, _("Enter full filename: "))) {
504 len = strlen(ua->cmd);
508 insert_one_file_or_dir(ua, rx, date, false);
512 case 8: /* Find JobIds for current backup */
513 bstrutime(date, sizeof(date), time(NULL));
514 if (!select_backups_before_date(ua, rx, date)) {
520 case 9: /* Find JobIds for give date */
521 if (!get_date(ua, date, sizeof(date))) {
524 if (!select_backups_before_date(ua, rx, date)) {
530 case 10: /* Enter directories */
531 if (*rx->JobIds != 0) {
532 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
534 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
535 if (*rx->JobIds != 0 && *ua->cmd) {
536 pm_strcat(rx->JobIds, ",");
538 pm_strcat(rx->JobIds, ua->cmd);
540 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
541 return 0; /* nothing entered, return */
543 bstrutime(date, sizeof(date), time(NULL));
544 if (!get_client_name(ua, rx)) {
547 bsendmsg(ua, _("Enter full directory names or start the name\n"
548 "with a < to indicate it is a filename containing a list\n"
549 "of directories and terminate them with a blank line.\n"));
551 if (!get_cmd(ua, _("Enter directory name: "))) {
554 len = strlen(ua->cmd);
558 /* Add trailing slash to end of directory names */
559 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
560 strcat(ua->cmd, "/");
562 insert_one_file_or_dir(ua, rx, date, true);
566 case 11: /* Cancel or quit */
571 if (*rx->JobIds == 0) {
572 bsendmsg(ua, _("No Jobs selected.\n"));
575 if (strchr(rx->JobIds,',')) {
576 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
579 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
584 for (p=rx->JobIds; ; ) {
585 int stat = get_next_jobid_from_list(&p, &JobId);
587 bsendmsg(ua, _("Invalid JobId in list.\n"));
593 if (jr.JobId == JobId) {
594 continue; /* duplicate of last JobId */
596 memset(&jr, 0, sizeof(JOB_DBR));
598 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
600 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
601 edit_int64(JobId, ed1), db_strerror(ua->db));
604 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
605 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
609 rx->TotalFiles += jr.JobFiles;
617 static int get_date(UAContext *ua, char *date, int date_len)
619 bsendmsg(ua, _("The restored files will the most current backup\n"
620 "BEFORE the date you specify below.\n\n"));
622 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
625 if (str_to_utime(ua->cmd) != 0) {
628 bsendmsg(ua, _("Improper date format.\n"));
630 bstrncpy(date, ua->cmd, date_len);
635 * Insert a single file, or read a list of files from a file
637 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
647 if ((ffd = fopen(p, "rb")) == NULL) {
649 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
653 while (fgets(file, sizeof(file), ffd)) {
656 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
657 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
660 if (!insert_file_into_findex_list(ua, rx, file, date)) {
661 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
669 insert_table_into_findex_list(ua, rx, p);
673 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
675 insert_file_into_findex_list(ua, rx, ua->cmd, date);
682 * For a given file (path+filename), split into path and file, then
683 * lookup the most recent backup in the catalog to get the JobId
684 * and FileIndex, then insert them into the findex list.
686 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
689 strip_trailing_newline(file);
690 split_path_and_filename(rx, file);
691 if (*rx->JobIds == 0) {
692 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
695 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
696 rx->path, rx->fname, rx->ClientName);
699 /* Find and insert jobid and File Index */
700 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
701 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
702 rx->query, db_strerror(ua->db));
705 bsendmsg(ua, _("No database record found for: %s\n"), file);
712 * For a given path lookup the most recent backup in the catalog
713 * to get the JobId and FileIndexes of all files in that directory.
715 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
718 strip_trailing_junk(dir);
719 if (*rx->JobIds == 0) {
720 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
723 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
724 dir, rx->ClientName);
727 /* Find and insert jobid and File Index */
728 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
729 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
730 rx->query, db_strerror(ua->db));
733 bsendmsg(ua, _("No database record found for: %s\n"), dir);
740 * Get the JobId and FileIndexes of all files in the specified table
742 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
744 strip_trailing_junk(table);
745 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
748 /* Find and insert jobid and File Index */
749 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
750 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
751 rx->query, db_strerror(ua->db));
754 bsendmsg(ua, _("No table found: %s\n"), table);
760 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
764 /* Find path without the filename.
765 * I.e. everything after the last / is a "filename".
766 * OK, maybe it is a directory name, but we treat it like
767 * a filename. If we don't find a / then the whole name
768 * must be a path name (e.g. c:).
770 for (p=f=name; *p; p++) {
772 f = p; /* set pos of last slash */
775 if (*f == '/') { /* did we find a slash? */
776 f++; /* yes, point to filename */
777 } else { /* no, whole thing must be path name */
781 /* If filename doesn't exist (i.e. root directory), we
782 * simply create a blank name consisting of a single
783 * space. This makes handling zero length filenames
788 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
789 memcpy(rx->fname, f, rx->fnl); /* copy filename */
790 rx->fname[rx->fnl] = 0;
798 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
799 memcpy(rx->path, name, rx->pnl);
800 rx->path[rx->pnl] = 0;
806 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
809 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
812 JobId_t JobId, last_JobId;
817 memset(&tree, 0, sizeof(TREE_CTX));
819 * Build the directory tree containing JobIds user selected
821 tree.root = new_tree(rx->TotalFiles);
826 * For display purposes, the same JobId, with different volumes may
827 * appear more than once, however, we only insert it once.
831 tree.FileEstimate = 0;
832 if (get_next_jobid_from_list(&p, &JobId) > 0) {
833 /* Use first JobId as estimate of the number of files to restore */
834 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
835 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
836 bsendmsg(ua, "%s\n", db_strerror(ua->db));
839 /* Add about 25% more than this job for over estimate */
840 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
841 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
844 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
847 if (JobId == last_JobId) {
848 continue; /* eliminate duplicate JobIds */
851 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
852 edit_int64(JobId, ed1));
855 * Find files for this JobId and insert them in the tree
857 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
858 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
859 bsendmsg(ua, "%s", db_strerror(ua->db));
862 if (tree.FileCount == 0) {
863 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
864 "is not possible.Most likely your retention policy pruned the files\n"));
865 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
869 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
870 if (JobId == last_JobId) {
871 continue; /* eliminate duplicate JobIds */
873 add_findex_all(rx->bsr, JobId);
881 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
882 edit_uint64_with_commas(tree.FileCount, ec1));
885 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
886 edit_uint64_with_commas(tree.FileCount, ec1));
891 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
892 items, edit_uint64_with_commas(tree.FileCount, ec1));
895 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
896 items, edit_uint64_with_commas(tree.FileCount, ec1));
900 if (find_arg(ua, NT_("done")) < 0) {
901 /* Let the user interact in selecting which files to restore */
902 OK = user_select_files_from_tree(&tree);
906 * Walk down through the tree finding all files marked to be
907 * extracted making a bootstrap file.
910 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
911 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
912 if (node->extract || node->extract_dir) {
913 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
914 add_findex(rx->bsr, node->JobId, node->FileIndex);
915 if (node->extract && node->type != TN_NEWDIR) {
916 rx->selected_files++; /* count only saved files */
923 free_tree(tree.root); /* free the directory tree */
929 * This routine is used to get the current backup or a backup
930 * before the specified date.
932 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
937 char fileset_name[MAX_NAME_LENGTH];
938 char ed1[50], ed2[50];
939 char pool_select[MAX_NAME_LENGTH];
943 /* Create temp tables */
944 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
945 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
946 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
947 bsendmsg(ua, "%s\n", db_strerror(ua->db));
949 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
950 bsendmsg(ua, "%s\n", db_strerror(ua->db));
953 * Select Client from the Catalog
955 memset(&cr, 0, sizeof(cr));
956 if (!get_client_dbr(ua, &cr)) {
959 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
964 memset(&fsr, 0, sizeof(fsr));
965 i = find_arg_with_value(ua, "FileSet");
967 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
968 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
969 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
970 db_strerror(ua->db));
974 if (i < 0) { /* fileset not found */
975 edit_int64(cr.ClientId, ed1);
976 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
977 start_prompt(ua, _("The defined FileSet resources are:\n"));
978 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
979 bsendmsg(ua, "%s\n", db_strerror(ua->db));
981 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
982 fileset_name, sizeof(fileset_name)) < 0) {
986 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
987 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
988 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
989 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
990 "Continuing anyway.\n"));
994 /* If Pool specified, add PoolId specification */
998 memset(&pr, 0, sizeof(pr));
999 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1000 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1001 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1002 edit_int64(pr.PoolId, ed1));
1004 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1008 /* Find JobId of last Full backup for this client, fileset */
1009 edit_int64(cr.ClientId, ed1);
1010 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1012 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1013 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1017 /* Find all Volumes used by that JobId */
1018 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1019 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1022 /* Note, this is needed because I don't seem to get the callback
1023 * from the call just above.
1026 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1027 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1029 if (rx->JobTDate == 0) {
1030 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1034 /* Now find most recent Differental Job after Full save, if any */
1035 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1036 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1037 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1038 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1040 /* Now update JobTDate to lock onto Differental, if any */
1042 if (!db_sql_query(ua->db, uar_sel_all_temp, 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 all Incremental Jobs after Full/dif save */
1051 Mmsg(rx->query, uar_inc, 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));
1057 /* Get the JobIds from that list */
1059 rx->last_jobid[0] = 0;
1060 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1061 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1064 if (rx->JobIds[0] != 0) {
1065 /* Display a list of Jobs selected for this restore */
1066 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1069 bsendmsg(ua, _("No jobs found.\n"));
1073 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1074 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1080 * Return next JobId from comma separated list
1083 * 1 if next JobId returned
1084 * 0 if no more JobIds are in list
1085 * -1 there is an error
1087 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1093 for (int i=0; i<(int)sizeof(jobid); i++) {
1096 } else if (*q == ',') {
1103 if (jobid[0] == 0) {
1105 } else if (!is_a_number(jobid)) {
1106 return -1; /* error */
1109 *JobId = str_to_int64(jobid);
1113 static int count_handler(void *ctx, int num_fields, char **row)
1115 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1116 rx->JobId = str_to_int64(row[0]);
1122 * Callback handler to get JobId and FileIndex for files
1123 * can insert more than one depending on the caller.
1125 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1127 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1128 rx->JobId = str_to_int64(row[0]);
1129 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1131 rx->selected_files++;
1136 * Callback handler make list of JobIds
1138 static int jobid_handler(void *ctx, int num_fields, char **row)
1140 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1142 if (strcmp(rx->last_jobid, row[0]) == 0) {
1143 return 0; /* duplicate id */
1145 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1146 if (rx->JobIds[0] != 0) {
1147 pm_strcat(rx->JobIds, ",");
1149 pm_strcat(rx->JobIds, row[0]);
1155 * Callback handler to pickup last Full backup JobTDate
1157 static int last_full_handler(void *ctx, int num_fields, char **row)
1159 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1161 rx->JobTDate = str_to_int64(row[1]);
1166 * Callback handler build FileSet name prompt list
1168 static int fileset_handler(void *ctx, int num_fields, char **row)
1170 /* row[0] = FileSet (name) */
1172 add_prompt((UAContext *)ctx, row[0]);
1178 * Free names in the list
1180 static void free_name_list(NAME_LIST *name_list)
1182 for (int i=0; i < name_list->num_ids; i++) {
1183 free(name_list->name[i]);
1185 if (name_list->name) {
1186 free(name_list->name);
1187 name_list->name = NULL;
1189 name_list->max_ids = 0;
1190 name_list->num_ids = 0;
1193 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1198 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1202 * Try looking up Storage by name
1205 foreach_res(store, R_STORAGE) {
1206 if (strcmp(Storage, store->name()) == 0) {
1207 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1216 /* Check if an explicit storage resource is given */
1218 int i = find_arg_with_value(ua, "storage");
1220 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1221 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1225 if (store && (store != rx.store)) {
1226 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1229 Dmsg1(200, "Set store=%s\n", rx.store->name());
1234 /* If no storage resource, try to find one from MediaType */
1237 foreach_res(store, R_STORAGE) {
1238 if (strcmp(MediaType, store->media_type) == 0) {
1239 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1241 Dmsg1(200, "Set store=%s\n", rx.store->name());
1242 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1243 Storage, store->name(), MediaType);
1250 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1251 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1254 /* Take command line arg, or ask user if none */
1255 rx.store = get_storage_resource(ua, false /* don't use default */);
1256 Dmsg1(200, "Set store=%s\n", rx.store->name());