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
17 Copyright (C) 2002-2004 Kern Sibbald and John Walker
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of
22 the License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public
30 License along with this program; if not, write to the Free
31 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40 /* Imported functions */
41 extern void print_bsr(UAContext *ua, RBSR *bsr);
43 /* Imported variables */
44 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
45 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
46 extern char *uar_create_temp1, *uar_last_full, *uar_full;
47 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
48 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
49 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
50 extern char *uar_count_files, *uar_jobids_fileindex;
54 char **name; /* list of names */
55 int num_ids; /* ids stored */
56 int max_ids; /* size of array */
57 int num_del; /* number deleted */
58 int tot_ids; /* total to process */
62 /* Main structure for obtaining JobIds or Files to be restored */
67 char ClientName[MAX_NAME_LENGTH];
69 POOLMEM *JobIds; /* User entered string of JobIds */
74 uint32_t selected_files;
77 POOLMEM *fname; /* filename only */
78 POOLMEM *path; /* path only */
80 int fnl; /* filename length */
81 int pnl; /* path length */
83 bool all; /* mark all as default */
88 #define MAX_ID_LIST_LEN 1000000
91 /* Forward referenced functions */
92 static int last_full_handler(void *ctx, int num_fields, char **row);
93 static int jobid_handler(void *ctx, int num_fields, char **row);
94 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
95 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
96 static int fileset_handler(void *ctx, int num_fields, char **row);
97 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
98 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
99 static void free_name_list(NAME_LIST *name_list);
100 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
101 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
102 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
103 static void free_rx(RESTORE_CTX *rx);
104 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
105 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
106 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
108 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
109 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
110 static int get_date(UAContext *ua, char *date, int date_len);
111 static int count_handler(void *ctx, int num_fields, char **row);
117 int restore_cmd(UAContext *ua, const char *cmd)
119 RESTORE_CTX rx; /* restore context */
123 memset(&rx, 0, sizeof(rx));
124 rx.path = get_pool_memory(PM_FNAME);
125 rx.fname = get_pool_memory(PM_FNAME);
126 rx.JobIds = get_pool_memory(PM_FNAME);
127 rx.query = get_pool_memory(PM_FNAME);
130 i = find_arg_with_value(ua, "where");
132 rx.where = ua->argv[i];
139 /* Ensure there is at least one Restore Job */
141 foreach_res(job, R_JOB) {
142 if (job->JobType == JT_RESTORE) {
143 if (!rx.restore_job) {
144 rx.restore_job = job;
150 if (!rx.restore_jobs) {
152 "No Restore Job Resource found. You must create at least\n"
153 "one before running this command.\n"));
158 * Request user to select JobIds or files by various different methods
159 * last 20 jobs, where File saved, most recent backup, ...
160 * In the end, a list of files are pumped into
163 switch (user_select_jobids_or_files(ua, &rx)) {
166 case 1: /* select by jobid */
167 if (!build_directory_tree(ua, &rx)) {
168 bsendmsg(ua, _("Restore not done.\n"));
172 case 2: /* select by filename, no tree needed */
177 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
178 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
181 if (!write_bsr_file(ua, rx.bsr)) {
184 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
185 rx.selected_files==1?"":"s");
187 bsendmsg(ua, _("No files selected to be restored.\n"));
191 if (rx.restore_jobs == 1) {
192 job = rx.restore_job;
194 job = select_restore_job_resource(ua);
200 get_client_name(ua, &rx);
201 if (!rx.ClientName) {
202 bsendmsg(ua, _("No Restore Job resource found!\n"));
206 /* Build run command */
209 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
210 " where=\"%s\" files=%d catalog=\"%s\"",
211 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
212 working_directory, rx.where, rx.selected_files, ua->catalog->hdr.name);
215 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
216 " files=%d catalog=\"%s\"",
217 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
218 working_directory, rx.selected_files, ua->catalog->hdr.name);
220 if (find_arg(ua, _("yes")) > 0) {
221 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
223 Dmsg1(400, "Submitting: %s\n", ua->cmd);
225 run_cmd(ua, ua->cmd);
235 static void free_rx(RESTORE_CTX *rx)
240 free_pool_memory(rx->JobIds);
244 free_pool_memory(rx->fname);
248 free_pool_memory(rx->path);
252 free_pool_memory(rx->query);
255 free_name_list(&rx->name_list);
258 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
260 /* If no client name specified yet, get it now */
261 if (!rx->ClientName[0]) {
263 /* try command line argument */
264 int i = find_arg_with_value(ua, _("client"));
266 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
269 memset(&cr, 0, sizeof(cr));
270 if (!get_client_dbr(ua, &cr)) {
273 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
279 * The first step in the restore process is for the user to
280 * select a list of JobIds from which he will subsequently
281 * select which files are to be restored.
283 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
286 char date[MAX_TIME_LENGTH];
287 bool have_date = false;
292 const char *list[] = {
293 "List last 20 Jobs run",
294 "List Jobs where a given File is saved",
295 "Enter list of comma separated JobIds to select",
296 "Enter SQL list command",
297 "Select the most recent backup for a client",
298 "Select backup for a client before a specified time",
299 "Enter a list of files to restore",
300 "Enter a list of files to restore before a specified time",
323 for (i=1; i<ua->argc; i++) { /* loop through arguments */
324 bool found_kw = false;
325 for (j=0; kw[j]; j++) { /* loop through keywords */
326 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
332 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
335 /* Found keyword in kw[] list, process it */
338 if (*rx->JobIds != 0) {
339 pm_strcat(rx->JobIds, ",");
341 pm_strcat(rx->JobIds, ua->argv[i]);
344 case 1: /* current */
345 bstrutime(date, sizeof(date), time(NULL));
349 if (str_to_utime(ua->argv[i]) == 0) {
350 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
353 bstrncpy(date, ua->argv[i], sizeof(date));
358 bstrutime(date, sizeof(date), time(NULL));
360 if (!get_client_name(ua, rx)) {
363 pm_strcpy(ua->cmd, ua->argv[i]);
364 insert_one_file(ua, rx, date);
365 if (rx->name_list.num_ids) {
366 /* Check MediaType and select storage that corresponds */
367 get_storage_from_mediatype(ua, &rx->name_list, rx);
373 bstrutime(date, sizeof(date), time(NULL));
375 if (!select_backups_before_date(ua, rx, date)) {
380 case 5: /* pool specified */
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 6: /* all specified */
396 * All keywords 7 or greater are ignored or handled by a select prompt
402 if (rx->name_list.num_ids) {
403 return 2; /* filename list made */
407 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
408 "to be restored. You will be presented several methods\n"
409 "of specifying the JobIds. Then you will be allowed to\n"
410 "select which files from those JobIds are to be restored.\n\n"));
413 /* If choice not already made above, prompt */
419 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
420 for (int i=0; list[i]; i++) {
421 add_prompt(ua, list[i]);
424 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
427 case 0: /* list last 20 Jobs run */
428 gui_save = ua->jcr->gui;
430 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
431 ua->jcr->gui = gui_save;
434 case 1: /* list where a file is saved */
435 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
438 len = strlen(ua->cmd);
439 fname = (char *)malloc(len * 2 + 1);
440 db_escape_string(fname, ua->cmd, len);
441 Mmsg(rx->query, uar_file, fname);
443 gui_save = ua->jcr->gui;
445 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
446 ua->jcr->gui = gui_save;
449 case 2: /* enter a list of JobIds */
450 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
453 pm_strcpy(rx->JobIds, ua->cmd);
455 case 3: /* Enter an SQL list command */
456 if (!get_cmd(ua, _("Enter SQL list command: "))) {
459 gui_save = ua->jcr->gui;
461 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
462 ua->jcr->gui = gui_save;
465 case 4: /* Select the most recent backups */
466 bstrutime(date, sizeof(date), time(NULL));
467 if (!select_backups_before_date(ua, rx, date)) {
471 case 5: /* select backup at specified time */
472 if (!get_date(ua, date, sizeof(date))) {
475 if (!select_backups_before_date(ua, rx, date)) {
479 case 6: /* Enter files */
480 bstrutime(date, sizeof(date), time(NULL));
481 if (!get_client_name(ua, rx)) {
484 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
485 "containg a list of file names with paths, and terminate\n"
486 "them with a blank line.\n"));
488 if (!get_cmd(ua, _("Enter full filename: "))) {
491 len = strlen(ua->cmd);
495 insert_one_file(ua, rx, date);
497 /* Check MediaType and select storage that corresponds */
498 if (rx->name_list.num_ids) {
499 get_storage_from_mediatype(ua, &rx->name_list, rx);
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 "containg 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(ua, rx, date);
522 /* Check MediaType and select storage that corresponds */
523 if (rx->name_list.num_ids) {
524 get_storage_from_mediatype(ua, &rx->name_list, rx);
529 case 8: /* Cancel or quit */
534 if (*rx->JobIds == 0) {
535 bsendmsg(ua, _("No Jobs selected.\n"));
538 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
539 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
541 memset(&jr, 0, sizeof(JOB_DBR));
544 for (p=rx->JobIds; ; ) {
545 int stat = get_next_jobid_from_list(&p, &JobId);
547 bsendmsg(ua, _("Invalid JobId in list.\n"));
553 if (jr.JobId == JobId) {
554 continue; /* duplicate of last JobId */
557 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
558 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
559 JobId, db_strerror(ua->db));
562 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
563 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
567 rx->TotalFiles += jr.JobFiles;
575 static int get_date(UAContext *ua, char *date, int date_len)
577 bsendmsg(ua, _("The restored files will the most current backup\n"
578 "BEFORE the date you specify below.\n\n"));
580 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
583 if (str_to_utime(ua->cmd) != 0) {
586 bsendmsg(ua, _("Improper date format.\n"));
588 bstrncpy(date, ua->cmd, date_len);
593 * Insert a single file, or read a list of files from a file
595 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
605 if ((ffd = fopen(p, "r")) == NULL) {
606 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
610 while (fgets(file, sizeof(file), ffd)) {
612 if (!insert_file_into_findex_list(ua, rx, file, date)) {
613 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
619 insert_file_into_findex_list(ua, rx, ua->cmd, date);
625 * For a given file (path+filename), split into path and file, then
626 * lookup the most recent backup in the catalog to get the JobId
627 * and FileIndex, then insert them into the findex list.
629 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
632 strip_trailing_junk(file);
633 split_path_and_filename(rx, file);
634 if (*rx->JobIds == 0) {
635 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
637 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
638 rx->path, rx->fname, rx->ClientName);
641 /* Find and insert jobid and File Index */
642 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
643 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
644 rx->query, db_strerror(ua->db));
647 bsendmsg(ua, _("No database record found for: %s\n"), file);
650 rx->selected_files++;
652 * Find the MediaTypes for this JobId and add to the name_list
654 Mmsg(rx->query, uar_mediatype, rx->JobId);
655 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
656 bsendmsg(ua, "%s", db_strerror(ua->db));
662 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
666 /* Find path without the filename.
667 * I.e. everything after the last / is a "filename".
668 * OK, maybe it is a directory name, but we treat it like
669 * a filename. If we don't find a / then the whole name
670 * must be a path name (e.g. c:).
672 for (p=f=name; *p; p++) {
674 f = p; /* set pos of last slash */
677 if (*f == '/') { /* did we find a slash? */
678 f++; /* yes, point to filename */
679 } else { /* no, whole thing must be path name */
683 /* If filename doesn't exist (i.e. root directory), we
684 * simply create a blank name consisting of a single
685 * space. This makes handling zero length filenames
690 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
691 memcpy(rx->fname, f, rx->fnl); /* copy filename */
692 rx->fname[rx->fnl] = 0;
700 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
701 memcpy(rx->path, name, rx->pnl);
702 rx->path[rx->pnl] = 0;
708 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
711 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
714 JobId_t JobId, last_JobId;
718 memset(&tree, 0, sizeof(TREE_CTX));
720 * Build the directory tree containing JobIds user selected
722 tree.root = new_tree(rx->TotalFiles);
727 * For display purposes, the same JobId, with different volumes may
728 * appear more than once, however, we only insert it once.
732 tree.FileEstimate = 0;
733 if (get_next_jobid_from_list(&p, &JobId) > 0) {
734 /* Use first JobId as estimate of the number of files to restore */
735 Mmsg(rx->query, uar_count_files, JobId);
736 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
737 bsendmsg(ua, "%s\n", db_strerror(ua->db));
740 /* Add about 25% more than this job for over estimate */
741 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
742 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
745 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
747 if (JobId == last_JobId) {
748 continue; /* eliminate duplicate JobIds */
751 bsendmsg(ua, _("\nBuilding directory tree for JobId %u ... "), JobId);
754 * Find files for this JobId and insert them in the tree
756 Mmsg(rx->query, uar_sel_files, JobId);
757 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
758 bsendmsg(ua, "%s", db_strerror(ua->db));
761 * Find the MediaTypes for this JobId and add to the name_list
763 Mmsg(rx->query, uar_mediatype, JobId);
764 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
765 bsendmsg(ua, "%s", db_strerror(ua->db));
769 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
770 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
771 tree.all?" and marked for extraction":"");
773 /* Check MediaType and select storage that corresponds */
774 get_storage_from_mediatype(ua, &rx->name_list, rx);
776 if (find_arg(ua, _("done")) < 0) {
777 /* Let the user interact in selecting which files to restore */
778 OK = user_select_files_from_tree(&tree);
782 * Walk down through the tree finding all files marked to be
783 * extracted making a bootstrap file.
786 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
787 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
788 if (node->extract || node->extract_dir) {
789 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
790 add_findex(rx->bsr, node->JobId, node->FileIndex);
791 if (node->extract && node->type != TN_NEWDIR) {
792 rx->selected_files++; /* count only saved files */
798 free_tree(tree.root); /* free the directory tree */
804 * This routine is used to get the current backup or a backup
805 * before the specified date.
807 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
812 char fileset_name[MAX_NAME_LENGTH];
814 char pool_select[MAX_NAME_LENGTH];
818 /* Create temp tables */
819 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
820 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
821 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
822 bsendmsg(ua, "%s\n", db_strerror(ua->db));
824 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
825 bsendmsg(ua, "%s\n", db_strerror(ua->db));
828 * Select Client from the Catalog
830 memset(&cr, 0, sizeof(cr));
831 if (!get_client_dbr(ua, &cr)) {
834 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
839 memset(&fsr, 0, sizeof(fsr));
840 i = find_arg_with_value(ua, "FileSet");
842 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
843 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
844 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
845 db_strerror(ua->db));
849 if (i < 0) { /* fileset not found */
850 Mmsg(rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
851 start_prompt(ua, _("The defined FileSet resources are:\n"));
852 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
853 bsendmsg(ua, "%s\n", db_strerror(ua->db));
855 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
856 fileset_name, sizeof(fileset_name)) < 0) {
860 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
861 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
862 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
863 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
864 "Continuing anyway.\n"));
868 /* If Pool specified, add PoolId specification */
872 memset(&pr, 0, sizeof(pr));
873 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
874 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
875 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
877 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
881 /* Find JobId of last Full backup for this client, fileset */
882 Mmsg(rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
884 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
885 bsendmsg(ua, "%s\n", db_strerror(ua->db));
889 /* Find all Volumes used by that JobId */
890 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
891 bsendmsg(ua, "%s\n", db_strerror(ua->db));
894 /* Note, this is needed because I don't seem to get the callback
895 * from the call just above.
898 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
899 bsendmsg(ua, "%s\n", db_strerror(ua->db));
901 if (rx->JobTDate == 0) {
902 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
906 /* Now find most recent Differental Job after Full save, if any */
907 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
908 cr.ClientId, fsr.FileSet, pool_select);
909 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
910 bsendmsg(ua, "%s\n", db_strerror(ua->db));
912 /* Now update JobTDate to lock onto Differental, if any */
914 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
915 bsendmsg(ua, "%s\n", db_strerror(ua->db));
917 if (rx->JobTDate == 0) {
918 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
922 /* Now find all Incremental Jobs after Full/dif save */
923 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
924 cr.ClientId, fsr.FileSet, pool_select);
925 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
926 bsendmsg(ua, "%s\n", db_strerror(ua->db));
929 /* Get the JobIds from that list */
931 rx->last_jobid[0] = 0;
932 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
933 bsendmsg(ua, "%s\n", db_strerror(ua->db));
936 if (rx->JobIds[0] != 0) {
937 /* Display a list of Jobs selected for this restore */
938 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
940 bsendmsg(ua, _("No jobs found.\n"));
946 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
947 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
952 /* Return next JobId from comma separated list */
953 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
959 for (int i=0; i<(int)sizeof(jobid); i++) {
960 if (*q == ',' || *q == 0) {
967 if (jobid[0] == 0 || !is_a_number(jobid)) {
971 *JobId = strtoul(jobid, NULL, 10);
975 static int count_handler(void *ctx, int num_fields, char **row)
977 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
978 rx->JobId = atoi(row[0]);
984 * Callback handler to get JobId and FileIndex for files
986 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
988 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
989 rx->JobId = atoi(row[0]);
990 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
996 * Callback handler make list of JobIds
998 static int jobid_handler(void *ctx, int num_fields, char **row)
1000 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1002 if (strcmp(rx->last_jobid, row[0]) == 0) {
1003 return 0; /* duplicate id */
1005 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1006 if (rx->JobIds[0] != 0) {
1007 pm_strcat(rx->JobIds, ",");
1009 pm_strcat(rx->JobIds, row[0]);
1015 * Callback handler to pickup last Full backup JobTDate
1017 static int last_full_handler(void *ctx, int num_fields, char **row)
1019 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1021 rx->JobTDate = str_to_int64(row[1]);
1026 * Callback handler build FileSet name prompt list
1028 static int fileset_handler(void *ctx, int num_fields, char **row)
1030 /* row[0] = FileSet (name) */
1032 add_prompt((UAContext *)ctx, row[0]);
1038 * Called here with each name to be added to the list. The name is
1039 * added to the list if it is not already in the list.
1041 * Used to make unique list of FileSets and MediaTypes
1043 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1045 NAME_LIST *name = (NAME_LIST *)ctx;
1047 if (name->num_ids == MAX_ID_LIST_LEN) {
1050 if (name->num_ids == name->max_ids) {
1051 if (name->max_ids == 0) {
1052 name->max_ids = 1000;
1053 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1055 name->max_ids = (name->max_ids * 3) / 2;
1056 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1059 for (int i=0; i<name->num_ids; i++) {
1060 if (strcmp(name->name[i], row[0]) == 0) {
1061 return 0; /* already in list, return */
1064 /* Add new name to list */
1065 name->name[name->num_ids++] = bstrdup(row[0]);
1071 * Print names in the list
1073 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1075 for (int i=0; i < name_list->num_ids; i++) {
1076 bsendmsg(ua, "%s\n", name_list->name[i]);
1082 * Free names in the list
1084 static void free_name_list(NAME_LIST *name_list)
1086 for (int i=0; i < name_list->num_ids; i++) {
1087 free(name_list->name[i]);
1089 if (name_list->name) {
1090 free(name_list->name);
1091 name_list->name = NULL;
1093 name_list->max_ids = 0;
1094 name_list->num_ids = 0;
1097 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1101 if (name_list->num_ids > 1) {
1102 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1103 "Restore is not possible. The MediaTypes used are:\n"));
1104 print_name_list(ua, name_list);
1105 rx->store = select_storage_resource(ua);
1109 if (name_list->num_ids == 0) {
1110 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1111 rx->store = select_storage_resource(ua);
1118 * We have a single MediaType, look it up in our Storage resource
1121 foreach_res(store, R_STORAGE) {
1122 if (strcmp(name_list->name[0], store->media_type) == 0) {
1123 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1132 /* Check if an explicit storage resource is given */
1134 int i = find_arg_with_value(ua, "storage");
1136 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1137 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1141 if (store && (store != rx->store)) {
1142 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1149 /* Take command line arg, or ask user if none */
1150 rx->store = get_storage_resource(ua, false /* don't use default */);
1153 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1154 "MediaType \"%s\", needed by the Jobs you selected.\n"
1155 "You will be allowed to select a Storage device later.\n"),
1156 name_list->name[0]);