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 /* ***FIXME*** restrict clients on restricted console */
426 gui_save = ua->jcr->gui;
428 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
429 ua->jcr->gui = gui_save;
432 case 1: /* list where a file is saved */
433 if (!get_client_name(ua, rx)) {
436 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
439 len = strlen(ua->cmd);
440 fname = (char *)malloc(len * 2 + 1);
441 db_escape_string(fname, ua->cmd, len);
442 Mmsg(rx->query, uar_file, rx->ClientName, fname);
444 gui_save = ua->jcr->gui;
446 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
447 ua->jcr->gui = gui_save;
450 case 2: /* enter a list of JobIds */
451 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
454 pm_strcpy(rx->JobIds, ua->cmd);
456 case 3: /* Enter an SQL list command */
457 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
460 if (!get_cmd(ua, _("Enter SQL list command: "))) {
463 gui_save = ua->jcr->gui;
465 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
466 ua->jcr->gui = gui_save;
469 case 4: /* Select the most recent backups */
470 bstrutime(date, sizeof(date), time(NULL));
471 if (!select_backups_before_date(ua, rx, date)) {
475 case 5: /* select backup at specified time */
476 if (!get_date(ua, date, sizeof(date))) {
479 if (!select_backups_before_date(ua, rx, date)) {
483 case 6: /* Enter files */
484 bstrutime(date, sizeof(date), time(NULL));
485 if (!get_client_name(ua, rx)) {
488 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
489 "containing a list of file names with paths, and terminate\n"
490 "them with a blank line.\n"));
492 if (!get_cmd(ua, _("Enter full filename: "))) {
495 len = strlen(ua->cmd);
499 insert_one_file_or_dir(ua, rx, date, false);
502 case 7: /* enter files backed up before specified time */
503 if (!get_date(ua, date, sizeof(date))) {
506 if (!get_client_name(ua, rx)) {
509 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
510 "containing a list of file names with paths, and terminate\n"
511 "them with a blank line.\n"));
513 if (!get_cmd(ua, _("Enter full filename: "))) {
516 len = strlen(ua->cmd);
520 insert_one_file_or_dir(ua, rx, date, false);
524 case 8: /* Find JobIds for current backup */
525 bstrutime(date, sizeof(date), time(NULL));
526 if (!select_backups_before_date(ua, rx, date)) {
532 case 9: /* Find JobIds for give date */
533 if (!get_date(ua, date, sizeof(date))) {
536 if (!select_backups_before_date(ua, rx, date)) {
542 case 10: /* Enter directories */
543 if (*rx->JobIds != 0) {
544 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
546 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
547 if (*rx->JobIds != 0 && *ua->cmd) {
548 pm_strcat(rx->JobIds, ",");
550 pm_strcat(rx->JobIds, ua->cmd);
552 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
553 return 0; /* nothing entered, return */
555 bstrutime(date, sizeof(date), time(NULL));
556 if (!get_client_name(ua, rx)) {
559 bsendmsg(ua, _("Enter full directory names or start the name\n"
560 "with a < to indicate it is a filename containing a list\n"
561 "of directories and terminate them with a blank line.\n"));
563 if (!get_cmd(ua, _("Enter directory name: "))) {
566 len = strlen(ua->cmd);
570 /* Add trailing slash to end of directory names */
571 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
572 strcat(ua->cmd, "/");
574 insert_one_file_or_dir(ua, rx, date, true);
578 case 11: /* Cancel or quit */
583 if (*rx->JobIds == 0) {
584 bsendmsg(ua, _("No Jobs selected.\n"));
587 if (strchr(rx->JobIds,',')) {
588 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
591 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
596 for (p=rx->JobIds; ; ) {
597 int stat = get_next_jobid_from_list(&p, &JobId);
599 bsendmsg(ua, _("Invalid JobId in list.\n"));
605 if (jr.JobId == JobId) {
606 continue; /* duplicate of last JobId */
608 memset(&jr, 0, sizeof(JOB_DBR));
610 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
612 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
613 edit_int64(JobId, ed1), db_strerror(ua->db));
616 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
617 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
621 rx->TotalFiles += jr.JobFiles;
629 static int get_date(UAContext *ua, char *date, int date_len)
631 bsendmsg(ua, _("The restored files will the most current backup\n"
632 "BEFORE the date you specify below.\n\n"));
634 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
637 if (str_to_utime(ua->cmd) != 0) {
640 bsendmsg(ua, _("Improper date format.\n"));
642 bstrncpy(date, ua->cmd, date_len);
647 * Insert a single file, or read a list of files from a file
649 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
659 if ((ffd = fopen(p, "rb")) == NULL) {
661 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
665 while (fgets(file, sizeof(file), ffd)) {
668 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
669 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
672 if (!insert_file_into_findex_list(ua, rx, file, date)) {
673 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
681 insert_table_into_findex_list(ua, rx, p);
685 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
687 insert_file_into_findex_list(ua, rx, ua->cmd, date);
694 * For a given file (path+filename), split into path and file, then
695 * lookup the most recent backup in the catalog to get the JobId
696 * and FileIndex, then insert them into the findex list.
698 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
701 strip_trailing_newline(file);
702 split_path_and_filename(rx, file);
703 if (*rx->JobIds == 0) {
704 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
707 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
708 rx->path, rx->fname, rx->ClientName);
711 /* Find and insert jobid and File Index */
712 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
713 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
714 rx->query, db_strerror(ua->db));
717 bsendmsg(ua, _("No database record found for: %s\n"), file);
724 * For a given path lookup the most recent backup in the catalog
725 * to get the JobId and FileIndexes of all files in that directory.
727 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
730 strip_trailing_junk(dir);
731 if (*rx->JobIds == 0) {
732 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
735 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
736 dir, rx->ClientName);
739 /* Find and insert jobid and File Index */
740 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
741 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
742 rx->query, db_strerror(ua->db));
745 bsendmsg(ua, _("No database record found for: %s\n"), dir);
752 * Get the JobId and FileIndexes of all files in the specified table
754 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
756 strip_trailing_junk(table);
757 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
760 /* Find and insert jobid and File Index */
761 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
762 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
763 rx->query, db_strerror(ua->db));
766 bsendmsg(ua, _("No table found: %s\n"), table);
772 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
776 /* Find path without the filename.
777 * I.e. everything after the last / is a "filename".
778 * OK, maybe it is a directory name, but we treat it like
779 * a filename. If we don't find a / then the whole name
780 * must be a path name (e.g. c:).
782 for (p=f=name; *p; p++) {
784 f = p; /* set pos of last slash */
787 if (*f == '/') { /* did we find a slash? */
788 f++; /* yes, point to filename */
789 } else { /* no, whole thing must be path name */
793 /* If filename doesn't exist (i.e. root directory), we
794 * simply create a blank name consisting of a single
795 * space. This makes handling zero length filenames
800 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
801 memcpy(rx->fname, f, rx->fnl); /* copy filename */
802 rx->fname[rx->fnl] = 0;
810 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
811 memcpy(rx->path, name, rx->pnl);
812 rx->path[rx->pnl] = 0;
818 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
821 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
824 JobId_t JobId, last_JobId;
829 memset(&tree, 0, sizeof(TREE_CTX));
831 * Build the directory tree containing JobIds user selected
833 tree.root = new_tree(rx->TotalFiles);
838 * For display purposes, the same JobId, with different volumes may
839 * appear more than once, however, we only insert it once.
843 tree.FileEstimate = 0;
844 if (get_next_jobid_from_list(&p, &JobId) > 0) {
845 /* Use first JobId as estimate of the number of files to restore */
846 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
847 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
848 bsendmsg(ua, "%s\n", db_strerror(ua->db));
851 /* Add about 25% more than this job for over estimate */
852 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
853 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
856 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
859 if (JobId == last_JobId) {
860 continue; /* eliminate duplicate JobIds */
863 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
864 edit_int64(JobId, ed1));
867 * Find files for this JobId and insert them in the tree
869 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
870 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
871 bsendmsg(ua, "%s", db_strerror(ua->db));
874 if (tree.FileCount == 0) {
875 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
876 "is not possible.Most likely your retention policy pruned the files\n"));
877 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
881 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
882 if (JobId == last_JobId) {
883 continue; /* eliminate duplicate JobIds */
885 add_findex_all(rx->bsr, JobId);
893 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
894 edit_uint64_with_commas(tree.FileCount, ec1));
897 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
898 edit_uint64_with_commas(tree.FileCount, ec1));
903 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
904 items, edit_uint64_with_commas(tree.FileCount, ec1));
907 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
908 items, edit_uint64_with_commas(tree.FileCount, ec1));
912 if (find_arg(ua, NT_("done")) < 0) {
913 /* Let the user interact in selecting which files to restore */
914 OK = user_select_files_from_tree(&tree);
918 * Walk down through the tree finding all files marked to be
919 * extracted making a bootstrap file.
922 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
923 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
924 if (node->extract || node->extract_dir) {
925 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
926 add_findex(rx->bsr, node->JobId, node->FileIndex);
927 if (node->extract && node->type != TN_NEWDIR) {
928 rx->selected_files++; /* count only saved files */
935 free_tree(tree.root); /* free the directory tree */
941 * This routine is used to get the current backup or a backup
942 * before the specified date.
944 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
949 char fileset_name[MAX_NAME_LENGTH];
950 char ed1[50], ed2[50];
951 char pool_select[MAX_NAME_LENGTH];
955 /* Create temp tables */
956 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
957 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
958 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
959 bsendmsg(ua, "%s\n", db_strerror(ua->db));
961 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
962 bsendmsg(ua, "%s\n", db_strerror(ua->db));
965 * Select Client from the Catalog
967 memset(&cr, 0, sizeof(cr));
968 if (!get_client_dbr(ua, &cr)) {
971 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
976 memset(&fsr, 0, sizeof(fsr));
977 i = find_arg_with_value(ua, "FileSet");
979 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
980 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
981 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
982 db_strerror(ua->db));
986 if (i < 0) { /* fileset not found */
987 edit_int64(cr.ClientId, ed1);
988 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
989 start_prompt(ua, _("The defined FileSet resources are:\n"));
990 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
991 bsendmsg(ua, "%s\n", db_strerror(ua->db));
993 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
994 fileset_name, sizeof(fileset_name)) < 0) {
998 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
999 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1000 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1001 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1002 "Continuing anyway.\n"));
1006 /* If Pool specified, add PoolId specification */
1010 memset(&pr, 0, sizeof(pr));
1011 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1012 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1013 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1014 edit_int64(pr.PoolId, ed1));
1016 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1020 /* Find JobId of last Full backup for this client, fileset */
1021 edit_int64(cr.ClientId, ed1);
1022 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1024 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1025 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1029 /* Find all Volumes used by that JobId */
1030 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1031 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1034 /* Note, this is needed because I don't seem to get the callback
1035 * from the call just above.
1038 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1039 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1041 if (rx->JobTDate == 0) {
1042 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1046 /* Now find most recent Differental Job after Full save, if any */
1047 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1048 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1049 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1050 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1052 /* Now update JobTDate to lock onto Differental, if any */
1054 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1055 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1057 if (rx->JobTDate == 0) {
1058 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1062 /* Now find all Incremental Jobs after Full/dif save */
1063 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1064 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1065 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1066 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1069 /* Get the JobIds from that list */
1071 rx->last_jobid[0] = 0;
1072 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1073 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1076 if (rx->JobIds[0] != 0) {
1077 /* Display a list of Jobs selected for this restore */
1078 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1081 bsendmsg(ua, _("No jobs found.\n"));
1085 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1086 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1092 * Return next JobId from comma separated list
1095 * 1 if next JobId returned
1096 * 0 if no more JobIds are in list
1097 * -1 there is an error
1099 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1105 for (int i=0; i<(int)sizeof(jobid); i++) {
1108 } else if (*q == ',') {
1115 if (jobid[0] == 0) {
1117 } else if (!is_a_number(jobid)) {
1118 return -1; /* error */
1121 *JobId = str_to_int64(jobid);
1125 static int count_handler(void *ctx, int num_fields, char **row)
1127 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1128 rx->JobId = str_to_int64(row[0]);
1134 * Callback handler to get JobId and FileIndex for files
1135 * can insert more than one depending on the caller.
1137 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1139 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1140 rx->JobId = str_to_int64(row[0]);
1141 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1143 rx->selected_files++;
1148 * Callback handler make list of JobIds
1150 static int jobid_handler(void *ctx, int num_fields, char **row)
1152 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1154 if (strcmp(rx->last_jobid, row[0]) == 0) {
1155 return 0; /* duplicate id */
1157 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1158 if (rx->JobIds[0] != 0) {
1159 pm_strcat(rx->JobIds, ",");
1161 pm_strcat(rx->JobIds, row[0]);
1167 * Callback handler to pickup last Full backup JobTDate
1169 static int last_full_handler(void *ctx, int num_fields, char **row)
1171 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1173 rx->JobTDate = str_to_int64(row[1]);
1178 * Callback handler build FileSet name prompt list
1180 static int fileset_handler(void *ctx, int num_fields, char **row)
1182 /* row[0] = FileSet (name) */
1184 add_prompt((UAContext *)ctx, row[0]);
1190 * Free names in the list
1192 static void free_name_list(NAME_LIST *name_list)
1194 for (int i=0; i < name_list->num_ids; i++) {
1195 free(name_list->name[i]);
1197 if (name_list->name) {
1198 free(name_list->name);
1199 name_list->name = NULL;
1201 name_list->max_ids = 0;
1202 name_list->num_ids = 0;
1205 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1210 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1214 * Try looking up Storage by name
1217 foreach_res(store, R_STORAGE) {
1218 if (strcmp(Storage, store->name()) == 0) {
1219 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1228 /* Check if an explicit storage resource is given */
1230 int i = find_arg_with_value(ua, "storage");
1232 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1233 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1237 if (store && (store != rx.store)) {
1238 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1241 Dmsg1(200, "Set store=%s\n", rx.store->name());
1246 /* If no storage resource, try to find one from MediaType */
1249 foreach_res(store, R_STORAGE) {
1250 if (strcmp(MediaType, store->media_type) == 0) {
1251 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1253 Dmsg1(200, "Set store=%s\n", rx.store->name());
1254 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1255 Storage, store->name(), MediaType);
1262 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1263 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1266 /* Take command line arg, or ask user if none */
1267 rx.store = get_storage_resource(ua, false /* don't use default */);
1268 Dmsg1(200, "Set store=%s\n", rx.store->name());