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 int run_cmd(UAContext *ua, const char *cmd);
42 extern void print_bsr(UAContext *ua, RBSR *bsr);
44 /* Imported variables */
45 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
46 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
47 extern char *uar_create_temp1, *uar_last_full, *uar_full;
48 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
49 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
50 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
51 extern char *uar_count_files;
55 char **name; /* list of names */
56 int num_ids; /* ids stored */
57 int max_ids; /* size of array */
58 int num_del; /* number deleted */
59 int tot_ids; /* total to process */
63 /* Main structure for obtaining JobIds or Files to be restored */
68 char ClientName[MAX_NAME_LENGTH];
70 POOLMEM *JobIds; /* User entered string of JobIds */
75 uint32_t selected_files;
78 POOLMEM *fname; /* filename only */
79 POOLMEM *path; /* path only */
81 int fnl; /* filename length */
82 int pnl; /* path length */
84 bool all; /* mark all as default */
89 #define MAX_ID_LIST_LEN 1000000
92 /* Forward referenced functions */
93 static int last_full_handler(void *ctx, int num_fields, char **row);
94 static int jobid_handler(void *ctx, int num_fields, char **row);
95 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
96 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
97 static int fileset_handler(void *ctx, int num_fields, char **row);
98 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
99 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
100 static void free_name_list(NAME_LIST *name_list);
101 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
102 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
103 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
104 static void free_rx(RESTORE_CTX *rx);
105 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
106 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
107 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
109 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
110 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
111 static int get_date(UAContext *ua, char *date, int date_len);
112 static int count_handler(void *ctx, int num_fields, char **row);
118 int restore_cmd(UAContext *ua, const char *cmd)
120 RESTORE_CTX rx; /* restore context */
124 memset(&rx, 0, sizeof(rx));
125 rx.path = get_pool_memory(PM_FNAME);
126 rx.fname = get_pool_memory(PM_FNAME);
127 rx.JobIds = get_pool_memory(PM_FNAME);
128 rx.query = get_pool_memory(PM_FNAME);
131 i = find_arg_with_value(ua, "where");
133 rx.where = ua->argv[i];
140 /* Ensure there is at least one Restore Job */
142 foreach_res(job, R_JOB) {
143 if (job->JobType == JT_RESTORE) {
144 if (!rx.restore_job) {
145 rx.restore_job = job;
151 if (!rx.restore_jobs) {
153 "No Restore Job Resource found. You must create at least\n"
154 "one before running this command.\n"));
159 * Request user to select JobIds or files by various different methods
160 * last 20 jobs, where File saved, most recent backup, ...
161 * In the end, a list of files are pumped into
164 switch (user_select_jobids_or_files(ua, &rx)) {
167 case 1: /* select by jobid */
168 if (!build_directory_tree(ua, &rx)) {
169 bsendmsg(ua, _("Restore not done.\n"));
173 case 2: /* select by filename, no tree needed */
178 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
179 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
182 if (!write_bsr_file(ua, rx.bsr)) {
185 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
186 rx.selected_files==1?"":"s");
188 bsendmsg(ua, _("No files selected to be restored.\n"));
192 if (rx.restore_jobs == 1) {
193 job = rx.restore_job;
195 job = select_restore_job_resource(ua);
201 get_client_name(ua, &rx);
202 if (!rx.ClientName) {
203 bsendmsg(ua, _("No Restore Job resource found!\n"));
207 /* Build run command */
210 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
211 " where=\"%s\" files=%d",
212 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
213 working_directory, rx.where, rx.selected_files);
216 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
218 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
219 working_directory, rx.selected_files);
221 if (find_arg(ua, _("yes")) > 0) {
222 pm_strcat(&ua->cmd, " yes"); /* pass it on to the run command */
224 Dmsg1(400, "Submitting: %s\n", ua->cmd);
226 run_cmd(ua, ua->cmd);
228 bsendmsg(ua, _("Restore command done.\n"));
238 static void free_rx(RESTORE_CTX *rx)
243 free_pool_memory(rx->JobIds);
247 free_pool_memory(rx->fname);
251 free_pool_memory(rx->path);
255 free_pool_memory(rx->query);
258 free_name_list(&rx->name_list);
261 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
263 /* If no client name specified yet, get it now */
264 if (!rx->ClientName[0]) {
266 /* try command line argument */
267 int i = find_arg_with_value(ua, _("client"));
269 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
272 memset(&cr, 0, sizeof(cr));
273 if (!get_client_dbr(ua, &cr)) {
276 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
282 * The first step in the restore process is for the user to
283 * select a list of JobIds from which he will subsequently
284 * select which files are to be restored.
286 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
289 char date[MAX_TIME_LENGTH];
290 bool have_date = false;
295 const char *list[] = {
296 "List last 20 Jobs run",
297 "List Jobs where a given File is saved",
298 "Enter list of comma separated JobIds to select",
299 "Enter SQL list command",
300 "Select the most recent backup for a client",
301 "Select backup for a client before a specified time",
302 "Enter a list of files to restore",
303 "Enter a list of files to restore before a specified time",
326 for (i=1; i<ua->argc; i++) { /* loop through arguments */
327 bool found_kw = false;
328 for (j=0; kw[j]; j++) { /* loop through keywords */
329 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
335 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
338 /* Found keyword in kw[] list, process it */
341 if (*rx->JobIds != 0) {
342 pm_strcat(&rx->JobIds, ",");
344 pm_strcat(&rx->JobIds, ua->argv[i]);
347 case 1: /* current */
348 bstrutime(date, sizeof(date), time(NULL));
352 if (str_to_utime(ua->argv[i]) == 0) {
353 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
356 bstrncpy(date, ua->argv[i], sizeof(date));
361 bstrutime(date, sizeof(date), time(NULL));
363 if (!get_client_name(ua, rx)) {
366 pm_strcpy(&ua->cmd, ua->argv[i]);
367 insert_one_file(ua, rx, date);
368 if (rx->name_list.num_ids) {
369 /* Check MediaType and select storage that corresponds */
370 get_storage_from_mediatype(ua, &rx->name_list, rx);
376 bstrutime(date, sizeof(date), time(NULL));
378 if (!select_backups_before_date(ua, rx, date)) {
383 case 5: /* pool specified */
384 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
386 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
389 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
391 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
395 case 6: /* all specified */
399 * All keywords 7 or greater are ignored or handled by a select prompt
405 if (rx->name_list.num_ids) {
406 return 2; /* filename list made */
410 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
411 "to be restored. You will be presented several methods\n"
412 "of specifying the JobIds. Then you will be allowed to\n"
413 "select which files from those JobIds are to be restored.\n\n"));
416 /* If choice not already made above, prompt */
421 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
422 for (int i=0; list[i]; i++) {
423 add_prompt(ua, list[i]);
426 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
429 case 0: /* list last 20 Jobs run */
430 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
433 case 1: /* list where a file is saved */
434 if (!get_cmd(ua, _("Enter Filename: "))) {
437 len = strlen(ua->cmd);
438 fname = (char *)malloc(len * 2 + 1);
439 db_escape_string(fname, ua->cmd, len);
440 Mmsg(&rx->query, uar_file, fname);
442 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
445 case 2: /* enter a list of JobIds */
446 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
449 pm_strcpy(&rx->JobIds, ua->cmd);
451 case 3: /* Enter an SQL list command */
452 if (!get_cmd(ua, _("Enter SQL list command: "))) {
455 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
458 case 4: /* Select the most recent backups */
459 bstrutime(date, sizeof(date), time(NULL));
460 if (!select_backups_before_date(ua, rx, date)) {
464 case 5: /* select backup at specified time */
465 if (!get_date(ua, date, sizeof(date))) {
468 if (!select_backups_before_date(ua, rx, date)) {
472 case 6: /* Enter files */
473 bstrutime(date, sizeof(date), time(NULL));
474 if (!get_client_name(ua, rx)) {
477 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
478 "containg a list of file names, and terminate\n"
479 "them with a blank line.\n"));
481 if (!get_cmd(ua, _("Enter filename: "))) {
484 len = strlen(ua->cmd);
488 insert_one_file(ua, rx, date);
490 /* Check MediaType and select storage that corresponds */
491 if (rx->name_list.num_ids) {
492 get_storage_from_mediatype(ua, &rx->name_list, rx);
495 case 7: /* enter files backed up before specified time */
496 if (!get_date(ua, date, sizeof(date))) {
499 if (!get_client_name(ua, rx)) {
502 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
503 "containg a list of file names, and terminate\n"
504 "them with a blank line.\n"));
506 if (!get_cmd(ua, _("Enter filename: "))) {
509 len = strlen(ua->cmd);
513 insert_one_file(ua, rx, date);
515 /* Check MediaType and select storage that corresponds */
516 if (rx->name_list.num_ids) {
517 get_storage_from_mediatype(ua, &rx->name_list, rx);
522 case 8: /* Cancel or quit */
527 if (*rx->JobIds == 0) {
528 bsendmsg(ua, _("No Jobs selected.\n"));
531 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
532 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
534 memset(&jr, 0, sizeof(JOB_DBR));
537 for (p=rx->JobIds; ; ) {
538 int stat = get_next_jobid_from_list(&p, &JobId);
540 bsendmsg(ua, _("Invalid JobId in list.\n"));
546 if (jr.JobId == JobId) {
547 continue; /* duplicate of last JobId */
550 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
551 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
552 JobId, db_strerror(ua->db));
555 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
556 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
560 rx->TotalFiles += jr.JobFiles;
568 static int get_date(UAContext *ua, char *date, int date_len)
570 bsendmsg(ua, _("The restored files will the most current backup\n"
571 "BEFORE the date you specify below.\n\n"));
573 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
576 if (str_to_utime(ua->cmd) != 0) {
579 bsendmsg(ua, _("Improper date format.\n"));
581 bstrncpy(date, ua->cmd, date_len);
586 * Insert a single file, or read a list of files from a file
588 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
598 if ((ffd = fopen(p, "r")) == NULL) {
599 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
603 while (fgets(file, sizeof(file), ffd)) {
605 if (!insert_file_into_findex_list(ua, rx, file, date)) {
606 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
612 insert_file_into_findex_list(ua, rx, ua->cmd, date);
618 * For a given file (path+filename), split into path and file, then
619 * lookup the most recent backup in the catalog to get the JobId
620 * and FileIndex, then insert them into the findex list.
622 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
625 strip_trailing_junk(file);
626 split_path_and_filename(rx, file);
627 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
629 /* Find and insert jobid and File Index */
630 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
631 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
632 rx->query, db_strerror(ua->db));
635 bsendmsg(ua, _("No database record found for: %s\n"), file);
638 rx->selected_files++;
640 * Find the MediaTypes for this JobId and add to the name_list
642 Mmsg(&rx->query, uar_mediatype, rx->JobId);
643 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
644 bsendmsg(ua, "%s", db_strerror(ua->db));
650 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
654 /* Find path without the filename.
655 * I.e. everything after the last / is a "filename".
656 * OK, maybe it is a directory name, but we treat it like
657 * a filename. If we don't find a / then the whole name
658 * must be a path name (e.g. c:).
660 for (p=f=name; *p; p++) {
662 f = p; /* set pos of last slash */
665 if (*f == '/') { /* did we find a slash? */
666 f++; /* yes, point to filename */
667 } else { /* no, whole thing must be path name */
671 /* If filename doesn't exist (i.e. root directory), we
672 * simply create a blank name consisting of a single
673 * space. This makes handling zero length filenames
678 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
679 memcpy(rx->fname, f, rx->fnl); /* copy filename */
680 rx->fname[rx->fnl] = 0;
682 rx->fname[0] = ' '; /* blank filename */
689 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
690 memcpy(rx->path, name, rx->pnl);
691 rx->path[rx->pnl] = 0;
698 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
701 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
704 JobId_t JobId, last_JobId;
708 memset(&tree, 0, sizeof(TREE_CTX));
710 * Build the directory tree containing JobIds user selected
712 tree.root = new_tree(rx->TotalFiles);
717 * For display purposes, the same JobId, with different volumes may
718 * appear more than once, however, we only insert it once.
722 tree.FileEstimate = 0;
723 if (get_next_jobid_from_list(&p, &JobId) > 0) {
724 /* Use first JobId as estimate of the number of files to restore */
725 Mmsg(&rx->query, uar_count_files, JobId);
726 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
727 bsendmsg(ua, "%s\n", db_strerror(ua->db));
730 /* Add about 25% more than this job for over estimate */
731 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
732 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
735 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
737 if (JobId == last_JobId) {
738 continue; /* eliminate duplicate JobIds */
741 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
744 * Find files for this JobId and insert them in the tree
746 Mmsg(&rx->query, uar_sel_files, JobId);
747 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
748 bsendmsg(ua, "%s", db_strerror(ua->db));
751 * Find the MediaTypes for this JobId and add to the name_list
753 Mmsg(&rx->query, uar_mediatype, JobId);
754 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
755 bsendmsg(ua, "%s", db_strerror(ua->db));
759 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
760 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
761 tree.all?" and marked for extraction":"");
763 /* Check MediaType and select storage that corresponds */
764 get_storage_from_mediatype(ua, &rx->name_list, rx);
766 if (find_arg(ua, _("done")) < 0) {
767 /* Let the user interact in selecting which files to restore */
768 OK = user_select_files_from_tree(&tree);
772 * Walk down through the tree finding all files marked to be
773 * extracted making a bootstrap file.
776 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
777 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
778 if (node->extract || node->extract_dir) {
779 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
780 add_findex(rx->bsr, node->JobId, node->FileIndex);
782 rx->selected_files++; /* count only saved files */
788 free_tree(tree.root); /* free the directory tree */
794 * This routine is used to get the current backup or a backup
795 * before the specified date.
797 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
802 char fileset_name[MAX_NAME_LENGTH];
804 char pool_select[MAX_NAME_LENGTH];
808 /* Create temp tables */
809 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
810 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
811 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
812 bsendmsg(ua, "%s\n", db_strerror(ua->db));
814 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
815 bsendmsg(ua, "%s\n", db_strerror(ua->db));
818 * Select Client from the Catalog
820 memset(&cr, 0, sizeof(cr));
821 if (!get_client_dbr(ua, &cr)) {
824 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
829 memset(&fsr, 0, sizeof(fsr));
830 i = find_arg_with_value(ua, "FileSet");
832 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
833 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
834 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
835 db_strerror(ua->db));
839 if (i < 0) { /* fileset not found */
840 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
841 start_prompt(ua, _("The defined FileSet resources are:\n"));
842 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
843 bsendmsg(ua, "%s\n", db_strerror(ua->db));
845 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
846 fileset_name, sizeof(fileset_name)) < 0) {
850 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
851 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
852 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
853 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
854 "Continuing anyway.\n"));
858 /* If Pool specified, add PoolId specification */
862 memset(&pr, 0, sizeof(pr));
863 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
864 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
865 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
867 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
871 /* Find JobId of last Full backup for this client, fileset */
872 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
874 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
875 bsendmsg(ua, "%s\n", db_strerror(ua->db));
879 /* Find all Volumes used by that JobId */
880 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
881 bsendmsg(ua, "%s\n", db_strerror(ua->db));
884 /* Note, this is needed because I don't seem to get the callback
885 * from the call just above.
888 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
889 bsendmsg(ua, "%s\n", db_strerror(ua->db));
891 if (rx->JobTDate == 0) {
892 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
896 /* Now find most recent Differental Job after Full save, if any */
897 Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
898 cr.ClientId, fsr.FileSet, pool_select);
899 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
900 bsendmsg(ua, "%s\n", db_strerror(ua->db));
902 /* Now update JobTDate to lock onto Differental, if any */
904 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
905 bsendmsg(ua, "%s\n", db_strerror(ua->db));
907 if (rx->JobTDate == 0) {
908 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
912 /* Now find all Incremental Jobs after Full/dif save */
913 Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
914 cr.ClientId, fsr.FileSet, pool_select);
915 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
916 bsendmsg(ua, "%s\n", db_strerror(ua->db));
919 /* Get the JobIds from that list */
921 rx->last_jobid[0] = 0;
922 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
923 bsendmsg(ua, "%s\n", db_strerror(ua->db));
926 if (rx->JobIds[0] != 0) {
927 /* Display a list of Jobs selected for this restore */
928 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
930 bsendmsg(ua, _("No jobs found.\n"));
936 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
937 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
942 /* Return next JobId from comma separated list */
943 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
949 for (int i=0; i<(int)sizeof(jobid); i++) {
950 if (*q == ',' || *q == 0) {
957 if (jobid[0] == 0 || !is_a_number(jobid)) {
961 *JobId = strtoul(jobid, NULL, 10);
965 static int count_handler(void *ctx, int num_fields, char **row)
967 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
968 rx->JobId = atoi(row[0]);
974 * Callback handler to get JobId and FileIndex for files
976 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
978 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
979 rx->JobId = atoi(row[0]);
980 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
986 * Callback handler make list of JobIds
988 static int jobid_handler(void *ctx, int num_fields, char **row)
990 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
992 if (strcmp(rx->last_jobid, row[0]) == 0) {
993 return 0; /* duplicate id */
995 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
996 if (rx->JobIds[0] != 0) {
997 pm_strcat(&rx->JobIds, ",");
999 pm_strcat(&rx->JobIds, row[0]);
1005 * Callback handler to pickup last Full backup JobTDate
1007 static int last_full_handler(void *ctx, int num_fields, char **row)
1009 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1011 rx->JobTDate = str_to_int64(row[1]);
1016 * Callback handler build FileSet name prompt list
1018 static int fileset_handler(void *ctx, int num_fields, char **row)
1020 /* row[0] = FileSet (name) */
1022 add_prompt((UAContext *)ctx, row[0]);
1028 * Called here with each name to be added to the list. The name is
1029 * added to the list if it is not already in the list.
1031 * Used to make unique list of FileSets and MediaTypes
1033 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1035 NAME_LIST *name = (NAME_LIST *)ctx;
1037 if (name->num_ids == MAX_ID_LIST_LEN) {
1040 if (name->num_ids == name->max_ids) {
1041 if (name->max_ids == 0) {
1042 name->max_ids = 1000;
1043 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1045 name->max_ids = (name->max_ids * 3) / 2;
1046 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1049 for (int i=0; i<name->num_ids; i++) {
1050 if (strcmp(name->name[i], row[0]) == 0) {
1051 return 0; /* already in list, return */
1054 /* Add new name to list */
1055 name->name[name->num_ids++] = bstrdup(row[0]);
1061 * Print names in the list
1063 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1065 for (int i=0; i < name_list->num_ids; i++) {
1066 bsendmsg(ua, "%s\n", name_list->name[i]);
1072 * Free names in the list
1074 static void free_name_list(NAME_LIST *name_list)
1076 for (int i=0; i < name_list->num_ids; i++) {
1077 free(name_list->name[i]);
1079 if (name_list->name) {
1080 free(name_list->name);
1081 name_list->name = NULL;
1083 name_list->max_ids = 0;
1084 name_list->num_ids = 0;
1087 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1091 if (name_list->num_ids > 1) {
1092 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1093 "Restore is not possible. The MediaTypes used are:\n"));
1094 print_name_list(ua, name_list);
1095 rx->store = select_storage_resource(ua);
1099 if (name_list->num_ids == 0) {
1100 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1101 rx->store = select_storage_resource(ua);
1108 * We have a single MediaType, look it up in our Storage resource
1111 foreach_res(store, R_STORAGE) {
1112 if (strcmp(name_list->name[0], store->media_type) == 0) {
1113 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1122 /* Check if an explicit storage resource is given */
1124 int i = find_arg_with_value(ua, "storage");
1126 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1127 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1131 if (store && (store != rx->store)) {
1132 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1139 /* Take command line arg, or ask user if none */
1140 rx->store = get_storage_resource(ua, false /* don't use default */);
1143 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1144 "MediaType \"%s\", needed by the Jobs you selected.\n"
1145 "You will be allowed to select a Storage device later.\n"),
1146 name_list->name[0]);