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;
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);
116 int restore_cmd(UAContext *ua, const char *cmd)
118 RESTORE_CTX rx; /* restore context */
122 memset(&rx, 0, sizeof(rx));
123 rx.path = get_pool_memory(PM_FNAME);
124 rx.fname = get_pool_memory(PM_FNAME);
125 rx.JobIds = get_pool_memory(PM_FNAME);
126 rx.query = get_pool_memory(PM_FNAME);
129 i = find_arg_with_value(ua, "where");
131 rx.where = ua->argv[i];
138 /* Ensure there is at least one Restore Job */
140 foreach_res(job, R_JOB) {
141 if (job->JobType == JT_RESTORE) {
142 if (!rx.restore_job) {
143 rx.restore_job = job;
149 if (!rx.restore_jobs) {
151 "No Restore Job Resource found. You must create at least\n"
152 "one before running this command.\n"));
157 * Request user to select JobIds or files by various different methods
158 * last 20 jobs, where File saved, most recent backup, ...
159 * In the end, a list of files are pumped into
162 switch (user_select_jobids_or_files(ua, &rx)) {
165 case 1: /* select by jobid */
166 if (!build_directory_tree(ua, &rx)) {
167 bsendmsg(ua, _("Restore not done.\n"));
171 case 2: /* select by filename, no tree needed */
176 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
177 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
180 if (!write_bsr_file(ua, rx.bsr)) {
183 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
184 rx.selected_files==1?"":"s");
186 bsendmsg(ua, _("No files selected to be restored.\n"));
190 if (rx.restore_jobs == 1) {
191 job = rx.restore_job;
193 job = select_restore_job_resource(ua);
199 get_client_name(ua, &rx);
200 if (!rx.ClientName) {
201 bsendmsg(ua, _("No Restore Job resource found!\n"));
205 /* Build run command */
208 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
209 " where=\"%s\" files=%d",
210 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
211 working_directory, rx.where, rx.selected_files);
214 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
216 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
217 working_directory, rx.selected_files);
219 if (find_arg(ua, _("yes")) > 0) {
220 pm_strcat(&ua->cmd, " yes"); /* pass it on to the run command */
222 Dmsg1(400, "Submitting: %s\n", ua->cmd);
224 run_cmd(ua, ua->cmd);
226 bsendmsg(ua, _("Restore command done.\n"));
236 static void free_rx(RESTORE_CTX *rx)
241 free_pool_memory(rx->JobIds);
245 free_pool_memory(rx->fname);
249 free_pool_memory(rx->path);
253 free_pool_memory(rx->query);
256 free_name_list(&rx->name_list);
259 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
261 /* If no client name specified yet, get it now */
262 if (!rx->ClientName[0]) {
264 /* try command line argument */
265 int i = find_arg_with_value(ua, _("client"));
267 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
270 memset(&cr, 0, sizeof(cr));
271 if (!get_client_dbr(ua, &cr)) {
274 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
280 * The first step in the restore process is for the user to
281 * select a list of JobIds from which he will subsequently
282 * select which files are to be restored.
284 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
287 char date[MAX_TIME_LENGTH];
288 bool have_date = false;
293 const char *list[] = {
294 "List last 20 Jobs run",
295 "List Jobs where a given File is saved",
296 "Enter list of comma separated JobIds to select",
297 "Enter SQL list command",
298 "Select the most recent backup for a client",
299 "Select backup for a client before a specified time",
300 "Enter a list of files to restore",
301 "Enter a list of files to restore before a specified time",
324 for (i=1; i<ua->argc; i++) { /* loop through arguments */
325 bool found_kw = false;
326 for (j=0; kw[j]; j++) { /* loop through keywords */
327 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
333 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
336 /* Found keyword in kw[] list, process it */
339 if (*rx->JobIds != 0) {
340 pm_strcat(&rx->JobIds, ",");
342 pm_strcat(&rx->JobIds, ua->argv[i]);
345 case 1: /* current */
346 bstrutime(date, sizeof(date), time(NULL));
350 if (str_to_utime(ua->argv[i]) == 0) {
351 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
354 bstrncpy(date, ua->argv[i], sizeof(date));
359 bstrutime(date, sizeof(date), time(NULL));
361 if (!get_client_name(ua, rx)) {
364 pm_strcpy(&ua->cmd, ua->argv[i]);
365 insert_one_file(ua, rx, date);
366 if (rx->name_list.num_ids) {
367 /* Check MediaType and select storage that corresponds */
368 get_storage_from_mediatype(ua, &rx->name_list, rx);
374 bstrutime(date, sizeof(date), time(NULL));
376 if (!select_backups_before_date(ua, rx, date)) {
381 case 5: /* pool specified */
382 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
384 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
387 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
389 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
393 case 6: /* all specified */
397 * All keywords 7 or greater are ignored or handled by a select prompt
403 if (rx->name_list.num_ids) {
404 return 2; /* filename list made */
408 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
409 "to be restored. You will be presented several methods\n"
410 "of specifying the JobIds. Then you will be allowed to\n"
411 "select which files from those JobIds are to be restored.\n\n"));
414 /* 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 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
431 case 1: /* list where a file is saved */
432 if (!get_cmd(ua, _("Enter Filename: "))) {
435 len = strlen(ua->cmd);
436 fname = (char *)malloc(len * 2 + 1);
437 db_escape_string(fname, ua->cmd, len);
438 Mmsg(&rx->query, uar_file, fname);
440 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
443 case 2: /* enter a list of JobIds */
444 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
447 pm_strcpy(&rx->JobIds, ua->cmd);
449 case 3: /* Enter an SQL list command */
450 if (!get_cmd(ua, _("Enter SQL list command: "))) {
453 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
456 case 4: /* Select the most recent backups */
457 bstrutime(date, sizeof(date), time(NULL));
458 if (!select_backups_before_date(ua, rx, date)) {
462 case 5: /* select backup at specified time */
463 if (!get_date(ua, date, sizeof(date))) {
466 if (!select_backups_before_date(ua, rx, date)) {
470 case 6: /* Enter files */
471 bstrutime(date, sizeof(date), time(NULL));
472 if (!get_client_name(ua, rx)) {
475 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
476 "containg a list of file names, and terminate\n"
477 "them with a blank line.\n"));
479 if (!get_cmd(ua, _("Enter filename: "))) {
482 len = strlen(ua->cmd);
486 insert_one_file(ua, rx, date);
488 /* Check MediaType and select storage that corresponds */
489 if (rx->name_list.num_ids) {
490 get_storage_from_mediatype(ua, &rx->name_list, rx);
493 case 7: /* enter files backed up before specified time */
494 if (!get_date(ua, date, sizeof(date))) {
497 if (!get_client_name(ua, rx)) {
500 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
501 "containg a list of file names, and terminate\n"
502 "them with a blank line.\n"));
504 if (!get_cmd(ua, _("Enter filename: "))) {
507 len = strlen(ua->cmd);
511 insert_one_file(ua, rx, date);
513 /* Check MediaType and select storage that corresponds */
514 if (rx->name_list.num_ids) {
515 get_storage_from_mediatype(ua, &rx->name_list, rx);
520 case 8: /* Cancel or quit */
525 if (*rx->JobIds == 0) {
526 bsendmsg(ua, _("No Jobs selected.\n"));
529 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
530 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
532 memset(&jr, 0, sizeof(JOB_DBR));
535 for (p=rx->JobIds; ; ) {
536 int stat = get_next_jobid_from_list(&p, &JobId);
538 bsendmsg(ua, _("Invalid JobId in list.\n"));
544 if (jr.JobId == JobId) {
545 continue; /* duplicate of last JobId */
548 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
549 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
550 JobId, db_strerror(ua->db));
553 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
554 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
558 rx->TotalFiles += jr.JobFiles;
566 static int get_date(UAContext *ua, char *date, int date_len)
568 bsendmsg(ua, _("The restored files will the most current backup\n"
569 "BEFORE the date you specify below.\n\n"));
571 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
574 if (str_to_utime(ua->cmd) != 0) {
577 bsendmsg(ua, _("Improper date format.\n"));
579 bstrncpy(date, ua->cmd, date_len);
584 * Insert a single file, or read a list of files from a file
586 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
596 if ((ffd = fopen(p, "r")) == NULL) {
597 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
601 while (fgets(file, sizeof(file), ffd)) {
603 if (!insert_file_into_findex_list(ua, rx, file, date)) {
604 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
610 insert_file_into_findex_list(ua, rx, ua->cmd, date);
616 * For a given file (path+filename), split into path and file, then
617 * lookup the most recent backup in the catalog to get the JobId
618 * and FileIndex, then insert them into the findex list.
620 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
623 strip_trailing_junk(file);
624 split_path_and_filename(rx, file);
625 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
627 /* Find and insert jobid and File Index */
628 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
629 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
630 rx->query, db_strerror(ua->db));
633 bsendmsg(ua, _("No database record found for: %s\n"), file);
636 rx->selected_files++;
638 * Find the MediaTypes for this JobId and add to the name_list
640 Mmsg(&rx->query, uar_mediatype, rx->JobId);
641 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
642 bsendmsg(ua, "%s", db_strerror(ua->db));
648 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
652 /* Find path without the filename.
653 * I.e. everything after the last / is a "filename".
654 * OK, maybe it is a directory name, but we treat it like
655 * a filename. If we don't find a / then the whole name
656 * must be a path name (e.g. c:).
658 for (p=f=name; *p; p++) {
660 f = p; /* set pos of last slash */
663 if (*f == '/') { /* did we find a slash? */
664 f++; /* yes, point to filename */
665 } else { /* no, whole thing must be path name */
669 /* If filename doesn't exist (i.e. root directory), we
670 * simply create a blank name consisting of a single
671 * space. This makes handling zero length filenames
676 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
677 memcpy(rx->fname, f, rx->fnl); /* copy filename */
678 rx->fname[rx->fnl] = 0;
680 rx->fname[0] = ' '; /* blank filename */
687 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
688 memcpy(rx->path, name, rx->pnl);
689 rx->path[rx->pnl] = 0;
696 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
699 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
702 JobId_t JobId, last_JobId;
709 memset(&tree, 0, sizeof(TREE_CTX));
711 * Build the directory tree containing JobIds user selected
713 tree.root = new_tree(rx->TotalFiles);
714 tree.root->fname = nofname;
719 * For display purposes, the same JobId, with different volumes may
720 * appear more than once, however, we only insert it once.
723 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
725 if (JobId == last_JobId) {
726 continue; /* eliminate duplicate JobIds */
729 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
732 * Find files for this JobId and insert them in the tree
734 Mmsg(&rx->query, uar_sel_files, JobId);
735 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
736 bsendmsg(ua, "%s", db_strerror(ua->db));
739 * Find the MediaTypes for this JobId and add to the name_list
741 Mmsg(&rx->query, uar_mediatype, JobId);
742 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
743 bsendmsg(ua, "%s", db_strerror(ua->db));
746 bsendmsg(ua, "%d Job%s inserted into the tree%s.\n",
747 items, items==1?"":"s", tree.all?" and marked for extraction":"");
749 /* Check MediaType and select storage that corresponds */
750 get_storage_from_mediatype(ua, &rx->name_list, rx);
752 if (find_arg(ua, _("done")) < 0) {
753 /* Let the user interact in selecting which files to restore */
754 OK = user_select_files_from_tree(&tree);
758 * Walk down through the tree finding all files marked to be
759 * extracted making a bootstrap file.
762 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
763 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
764 if (node->extract || node->extract_dir) {
765 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
766 add_findex(rx->bsr, node->JobId, node->FileIndex);
768 rx->selected_files++; /* count only saved files */
774 free_tree(tree.root); /* free the directory tree */
780 * This routine is used to get the current backup or a backup
781 * before the specified date.
783 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
788 char fileset_name[MAX_NAME_LENGTH];
790 char pool_select[MAX_NAME_LENGTH];
794 /* Create temp tables */
795 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
796 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
797 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
798 bsendmsg(ua, "%s\n", db_strerror(ua->db));
800 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
801 bsendmsg(ua, "%s\n", db_strerror(ua->db));
804 * Select Client from the Catalog
806 memset(&cr, 0, sizeof(cr));
807 if (!get_client_dbr(ua, &cr)) {
810 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
815 memset(&fsr, 0, sizeof(fsr));
816 i = find_arg_with_value(ua, "FileSet");
818 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
819 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
820 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
821 db_strerror(ua->db));
825 if (i < 0) { /* fileset not found */
826 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
827 start_prompt(ua, _("The defined FileSet resources are:\n"));
828 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
829 bsendmsg(ua, "%s\n", db_strerror(ua->db));
831 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
832 fileset_name, sizeof(fileset_name)) < 0) {
836 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
837 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
838 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
839 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
840 "Continuing anyway.\n"));
844 /* If Pool specified, add PoolId specification */
848 memset(&pr, 0, sizeof(pr));
849 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
850 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
851 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
853 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
857 /* Find JobId of last Full backup for this client, fileset */
858 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
860 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
861 bsendmsg(ua, "%s\n", db_strerror(ua->db));
865 /* Find all Volumes used by that JobId */
866 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
867 bsendmsg(ua, "%s\n", db_strerror(ua->db));
870 /* Note, this is needed because I don't seem to get the callback
871 * from the call just above.
874 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
875 bsendmsg(ua, "%s\n", db_strerror(ua->db));
877 if (rx->JobTDate == 0) {
878 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
882 /* Now find most recent Differental Job after Full save, if any */
883 Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
884 cr.ClientId, fsr.FileSet, pool_select);
885 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
886 bsendmsg(ua, "%s\n", db_strerror(ua->db));
888 /* Now update JobTDate to lock onto Differental, if any */
890 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
891 bsendmsg(ua, "%s\n", db_strerror(ua->db));
893 if (rx->JobTDate == 0) {
894 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
898 /* Now find all Incremental Jobs after Full/dif save */
899 Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
900 cr.ClientId, fsr.FileSet, pool_select);
901 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
902 bsendmsg(ua, "%s\n", db_strerror(ua->db));
905 /* Get the JobIds from that list */
907 rx->last_jobid[0] = 0;
908 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
909 bsendmsg(ua, "%s\n", db_strerror(ua->db));
912 if (rx->JobIds[0] != 0) {
913 /* Display a list of Jobs selected for this restore */
914 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
916 bsendmsg(ua, _("No jobs found.\n"));
922 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
923 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
928 /* Return next JobId from comma separated list */
929 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
935 for (int i=0; i<(int)sizeof(jobid); i++) {
936 if (*q == ',' || *q == 0) {
943 if (jobid[0] == 0 || !is_a_number(jobid)) {
947 *JobId = strtoul(jobid, NULL, 10);
952 * Callback handler to get JobId and FileIndex for files
954 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
956 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
957 rx->JobId = atoi(row[0]);
958 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
964 * Callback handler make list of JobIds
966 static int jobid_handler(void *ctx, int num_fields, char **row)
968 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
970 if (strcmp(rx->last_jobid, row[0]) == 0) {
971 return 0; /* duplicate id */
973 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
974 if (rx->JobIds[0] != 0) {
975 pm_strcat(&rx->JobIds, ",");
977 pm_strcat(&rx->JobIds, row[0]);
983 * Callback handler to pickup last Full backup JobTDate
985 static int last_full_handler(void *ctx, int num_fields, char **row)
987 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
989 rx->JobTDate = str_to_int64(row[1]);
994 * Callback handler build FileSet name prompt list
996 static int fileset_handler(void *ctx, int num_fields, char **row)
998 /* row[0] = FileSet (name) */
1000 add_prompt((UAContext *)ctx, row[0]);
1006 * Called here with each name to be added to the list. The name is
1007 * added to the list if it is not already in the list.
1009 * Used to make unique list of FileSets and MediaTypes
1011 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1013 NAME_LIST *name = (NAME_LIST *)ctx;
1015 if (name->num_ids == MAX_ID_LIST_LEN) {
1018 if (name->num_ids == name->max_ids) {
1019 if (name->max_ids == 0) {
1020 name->max_ids = 1000;
1021 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1023 name->max_ids = (name->max_ids * 3) / 2;
1024 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1027 for (int i=0; i<name->num_ids; i++) {
1028 if (strcmp(name->name[i], row[0]) == 0) {
1029 return 0; /* already in list, return */
1032 /* Add new name to list */
1033 name->name[name->num_ids++] = bstrdup(row[0]);
1039 * Print names in the list
1041 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1043 for (int i=0; i < name_list->num_ids; i++) {
1044 bsendmsg(ua, "%s\n", name_list->name[i]);
1050 * Free names in the list
1052 static void free_name_list(NAME_LIST *name_list)
1054 for (int i=0; i < name_list->num_ids; i++) {
1055 free(name_list->name[i]);
1057 if (name_list->name) {
1058 free(name_list->name);
1059 name_list->name = NULL;
1061 name_list->max_ids = 0;
1062 name_list->num_ids = 0;
1065 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1069 if (name_list->num_ids > 1) {
1070 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1071 "Restore is not possible. The MediaTypes used are:\n"));
1072 print_name_list(ua, name_list);
1073 rx->store = select_storage_resource(ua);
1077 if (name_list->num_ids == 0) {
1078 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1079 rx->store = select_storage_resource(ua);
1086 * We have a single MediaType, look it up in our Storage resource
1089 foreach_res(store, R_STORAGE) {
1090 if (strcmp(name_list->name[0], store->media_type) == 0) {
1091 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1100 /* Check if an explicit storage resource is given */
1102 int i = find_arg_with_value(ua, "storage");
1104 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1105 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1109 if (store && (store != rx->store)) {
1110 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1117 /* Take command line arg, or ask user if none */
1118 rx->store = get_storage_resource(ua, false /* don't use default */);
1121 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1122 "MediaType \"%s\", needed by the Jobs you selected.\n"
1123 "You will be allowed to select a Storage device later.\n"),
1124 name_list->name[0]);