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, 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;
87 #define MAX_ID_LIST_LEN 1000000
90 /* Forward referenced functions */
91 static int last_full_handler(void *ctx, int num_fields, char **row);
92 static int jobid_handler(void *ctx, int num_fields, char **row);
93 static int next_jobid_from_list(char **p, uint32_t *JobId);
94 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
95 static int fileset_handler(void *ctx, int num_fields, char **row);
96 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
97 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
98 static void free_name_list(NAME_LIST *name_list);
99 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
100 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
101 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
102 static void free_rx(RESTORE_CTX *rx);
103 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
104 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
105 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
107 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
108 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
109 static int get_date(UAContext *ua, char *date, int date_len);
115 int restore_cmd(UAContext *ua, char *cmd)
117 RESTORE_CTX rx; /* restore context */
121 memset(&rx, 0, sizeof(rx));
122 rx.path = get_pool_memory(PM_FNAME);
123 rx.fname = get_pool_memory(PM_FNAME);
124 rx.JobIds = get_pool_memory(PM_FNAME);
125 rx.query = get_pool_memory(PM_FNAME);
128 i = find_arg_with_value(ua, "where");
130 rx.where = ua->argv[i];
137 /* Ensure there is at least one Restore Job */
139 foreach_res(job, R_JOB) {
140 if (job->JobType == JT_RESTORE) {
141 if (!rx.restore_job) {
142 rx.restore_job = job;
148 if (!rx.restore_jobs) {
150 "No Restore Job Resource found. You must create at least\n"
151 "one before running this command.\n"));
156 * Request user to select JobIds or files by various different methods
157 * last 20 jobs, where File saved, most recent backup, ...
158 * In the end, a list of files are pumped into
161 switch (user_select_jobids_or_files(ua, &rx)) {
164 case 1: /* select by jobid */
165 if (!build_directory_tree(ua, &rx)) {
166 bsendmsg(ua, _("Restore not done.\n"));
170 case 2: /* select by filename, no tree needed */
175 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
176 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
179 write_bsr_file(ua, rx.bsr);
180 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
181 rx.selected_files==1?"":"s");
183 bsendmsg(ua, _("No files selected to be restored.\n"));
187 if (rx.restore_jobs == 1) {
188 job = rx.restore_job;
190 job = select_restore_job_resource(ua);
196 get_client_name(ua, &rx);
197 if (!rx.ClientName) {
198 bsendmsg(ua, _("No Restore Job resource found!\n"));
202 /* Build run command */
205 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
206 " where=\"%s\" files=%d",
207 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
208 working_directory, rx.where, rx.selected_files);
211 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
212 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
215 if (find_arg(ua, _("yes")) > 0) {
216 pm_strcat(&ua->cmd, " yes"); /* pass it on to the run command */
218 Dmsg1(400, "Submitting: %s\n", ua->cmd);
220 run_cmd(ua, ua->cmd);
222 bsendmsg(ua, _("Restore command done.\n"));
232 static void free_rx(RESTORE_CTX *rx)
237 free_pool_memory(rx->JobIds);
241 free_pool_memory(rx->fname);
245 free_pool_memory(rx->path);
249 free_pool_memory(rx->query);
252 free_name_list(&rx->name_list);
255 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
257 /* If no client name specified yet, get it now */
258 if (!rx->ClientName[0]) {
260 /* try command line argument */
261 int i = find_arg_with_value(ua, _("client"));
263 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
266 memset(&cr, 0, sizeof(cr));
267 if (!get_client_dbr(ua, &cr)) {
270 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
276 * The first step in the restore process is for the user to
277 * select a list of JobIds from which he will subsequently
278 * select which files are to be restored.
280 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
283 char date[MAX_TIME_LENGTH];
284 bool have_date = false;
290 "List last 20 Jobs run",
291 "List Jobs where a given File is saved",
292 "Enter list of JobIds to select",
293 "Enter SQL list command",
294 "Select the most recent backup for a client",
295 "Select backup for a client before a specified time",
296 "Enter a list of files to restore",
297 "Enter a list of files to restore before a specified time",
312 switch (find_arg_keyword(ua, kw)) {
315 i = find_arg_with_value(ua, _("jobid"));
319 pm_strcpy(&rx->JobIds, ua->argv[i]);
320 ua->argk[i][0] = 0; /* "consume" jobid= */
324 case 1: /* current */
325 bstrutime(date, sizeof(date), time(NULL));
329 i = find_arg_with_value(ua, _("before"));
333 if (str_to_utime(ua->argv[i]) == 0) {
334 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
337 bstrncpy(date, ua->argv[i], sizeof(date));
342 bstrutime(date, sizeof(date), time(NULL));
344 if (!get_client_name(ua, rx)) {
348 i = find_arg_with_value(ua, _("file"));
352 pm_strcpy(&ua->cmd, ua->argv[i]);
353 insert_one_file(ua, rx, date);
354 ua->argk[i][0] = 0; /* "consume" the file= */
356 /* Check MediaType and select storage that corresponds */
357 get_storage_from_mediatype(ua, &rx->name_list, rx);
361 bstrutime(date, sizeof(date), time(NULL));
363 if (!select_backups_before_date(ua, rx, date)) {
368 case 5: /* pool specified */
369 i = find_arg_with_value(ua, "pool");
370 if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
371 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
373 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
381 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
382 "to be restored. You will be presented several methods\n"
383 "of specifying the JobIds. Then you will be allowed to\n"
384 "select which files from those JobIds are to be restored.\n\n"));
387 /* If choice not already made above, prompt */
392 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
393 for (int i=0; list[i]; i++) {
394 add_prompt(ua, list[i]);
397 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
400 case 0: /* list last 20 Jobs run */
401 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
404 case 1: /* list where a file is saved */
405 if (!get_cmd(ua, _("Enter Filename: "))) {
408 len = strlen(ua->cmd);
409 fname = (char *)malloc(len * 2 + 1);
410 db_escape_string(fname, ua->cmd, len);
411 Mmsg(&rx->query, uar_file, fname);
413 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
416 case 2: /* enter a list of JobIds */
417 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
420 pm_strcpy(&rx->JobIds, ua->cmd);
422 case 3: /* Enter an SQL list command */
423 if (!get_cmd(ua, _("Enter SQL list command: "))) {
426 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
429 case 4: /* Select the most recent backups */
430 bstrutime(date, sizeof(date), time(NULL));
431 if (!select_backups_before_date(ua, rx, date)) {
435 case 5: /* select backup at specified time */
436 if (!get_date(ua, date, sizeof(date))) {
439 if (!select_backups_before_date(ua, rx, date)) {
443 case 6: /* Enter files */
444 bstrutime(date, sizeof(date), time(NULL));
445 if (!get_client_name(ua, rx)) {
448 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
449 "containg a list of file names, and terminate\n"
450 "them with a blank line.\n"));
452 if (!get_cmd(ua, _("Enter filename: "))) {
455 len = strlen(ua->cmd);
459 insert_one_file(ua, rx, date);
461 /* Check MediaType and select storage that corresponds */
462 get_storage_from_mediatype(ua, &rx->name_list, rx);
464 case 7: /* enter files backed up before specified time */
465 if (!get_date(ua, date, sizeof(date))) {
468 if (!get_client_name(ua, rx)) {
471 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
472 "containg a list of file names, and terminate\n"
473 "them with a blank line.\n"));
475 if (!get_cmd(ua, _("Enter filename: "))) {
478 len = strlen(ua->cmd);
482 insert_one_file(ua, rx, date);
484 /* Check MediaType and select storage that corresponds */
485 get_storage_from_mediatype(ua, &rx->name_list, rx);
489 case 8: /* Cancel or quit */
494 if (*rx->JobIds == 0) {
495 bsendmsg(ua, _("No Jobs selected.\n"));
498 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
499 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
501 memset(&jr, 0, sizeof(JOB_DBR));
504 for (p=rx->JobIds; ; ) {
505 int stat = next_jobid_from_list(&p, &JobId);
507 bsendmsg(ua, _("Invalid JobId in list.\n"));
513 if (jr.JobId == JobId) {
514 continue; /* duplicate of last JobId */
517 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
518 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
519 JobId, db_strerror(ua->db));
522 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
523 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
527 rx->TotalFiles += jr.JobFiles;
535 static int get_date(UAContext *ua, char *date, int date_len)
537 bsendmsg(ua, _("The restored files will the most current backup\n"
538 "BEFORE the date you specify below.\n\n"));
540 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
543 if (str_to_utime(ua->cmd) != 0) {
546 bsendmsg(ua, _("Improper date format.\n"));
548 bstrncpy(date, ua->cmd, date_len);
553 * Insert a single file, or read a list of files from a file
555 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
565 if ((ffd = fopen(p, "r")) == NULL) {
566 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
570 while (fgets(file, sizeof(file), ffd)) {
572 if (!insert_file_into_findex_list(ua, rx, file, date)) {
573 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
579 insert_file_into_findex_list(ua, rx, ua->cmd, date);
585 * For a given file (path+filename), split into path and file, then
586 * lookup the most recent backup in the catalog to get the JobId
587 * and FileIndex, then insert them into the findex list.
589 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
592 strip_trailing_junk(file);
593 split_path_and_filename(rx, file);
594 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
595 Dmsg1(000, "Query=%s\n", rx->query);
597 /* Find and insert jobid and File Index */
598 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
599 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
600 rx->query, db_strerror(ua->db));
603 bsendmsg(ua, _("No database record found for: %s\n"), file);
606 rx->selected_files++;
608 * Find the MediaTypes for this JobId and add to the name_list
610 Mmsg(&rx->query, uar_mediatype, rx->JobId);
611 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
612 bsendmsg(ua, "%s", db_strerror(ua->db));
618 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
622 /* Find path without the filename.
623 * I.e. everything after the last / is a "filename".
624 * OK, maybe it is a directory name, but we treat it like
625 * a filename. If we don't find a / then the whole name
626 * must be a path name (e.g. c:).
628 for (p=f=name; *p; p++) {
630 f = p; /* set pos of last slash */
633 if (*f == '/') { /* did we find a slash? */
634 f++; /* yes, point to filename */
635 } else { /* no, whole thing must be path name */
639 /* If filename doesn't exist (i.e. root directory), we
640 * simply create a blank name consisting of a single
641 * space. This makes handling zero length filenames
646 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
647 memcpy(rx->fname, f, rx->fnl); /* copy filename */
648 rx->fname[rx->fnl] = 0;
650 rx->fname[0] = ' '; /* blank filename */
657 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
658 memcpy(rx->path, name, rx->pnl);
659 rx->path[rx->pnl] = 0;
666 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
669 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
672 JobId_t JobId, last_JobId;
677 memset(&tree, 0, sizeof(TREE_CTX));
679 * Build the directory tree containing JobIds user selected
681 tree.root = new_tree(rx->TotalFiles);
682 tree.root->fname = nofname;
686 * For display purposes, the same JobId, with different volumes may
687 * appear more than once, however, we only insert it once.
690 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
692 if (JobId == last_JobId) {
693 continue; /* eliminate duplicate JobIds */
696 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
699 * Find files for this JobId and insert them in the tree
701 Mmsg(&rx->query, uar_sel_files, JobId);
702 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
703 bsendmsg(ua, "%s", db_strerror(ua->db));
706 * Find the MediaTypes for this JobId and add to the name_list
708 Mmsg(&rx->query, uar_mediatype, JobId);
709 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
710 bsendmsg(ua, "%s", db_strerror(ua->db));
713 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
714 items, items==1?"":"s");
716 /* Check MediaType and select storage that corresponds */
717 get_storage_from_mediatype(ua, &rx->name_list, rx);
719 if (find_arg(ua, _("all")) < 0) {
720 /* Let the user interact in selecting which files to restore */
721 OK = user_select_files_from_tree(&tree);
725 * Walk down through the tree finding all files marked to be
726 * extracted making a bootstrap file.
729 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
730 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
731 if (node->extract || node->extract_dir) {
732 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
733 add_findex(rx->bsr, node->JobId, node->FileIndex);
734 rx->selected_files++;
739 free_tree(tree.root); /* free the directory tree */
745 * This routine is used to get the current backup or a backup
746 * before the specified date.
748 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
753 char fileset_name[MAX_NAME_LENGTH];
755 char pool_select[MAX_NAME_LENGTH];
759 /* Create temp tables */
760 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
761 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
762 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
763 bsendmsg(ua, "%s\n", db_strerror(ua->db));
765 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
766 bsendmsg(ua, "%s\n", db_strerror(ua->db));
769 * Select Client from the Catalog
771 memset(&cr, 0, sizeof(cr));
772 if (!get_client_dbr(ua, &cr)) {
775 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
780 memset(&fsr, 0, sizeof(fsr));
781 i = find_arg_with_value(ua, "FileSet");
783 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
784 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
785 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
786 db_strerror(ua->db));
790 if (i < 0) { /* fileset not found */
791 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
792 start_prompt(ua, _("The defined FileSet resources are:\n"));
793 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
794 bsendmsg(ua, "%s\n", db_strerror(ua->db));
796 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
797 fileset_name, sizeof(fileset_name)) < 0) {
801 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
802 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
803 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
804 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
805 "Continuing anyway.\n"));
809 /* If Pool specified, add PoolId specification */
813 memset(&pr, 0, sizeof(pr));
814 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
815 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
816 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
818 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
822 /* Find JobId of last Full backup for this client, fileset */
823 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
825 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
826 bsendmsg(ua, "%s\n", db_strerror(ua->db));
830 /* Find all Volumes used by that JobId */
831 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
832 bsendmsg(ua, "%s\n", db_strerror(ua->db));
835 /* Note, this is needed because I don't seem to get the callback
836 * from the call just above.
839 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
840 bsendmsg(ua, "%s\n", db_strerror(ua->db));
842 if (rx->JobTDate == 0) {
843 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
847 /* Now find most recent Differental Job after Full save, if any */
848 Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
849 cr.ClientId, fsr.FileSet, pool_select);
850 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
851 bsendmsg(ua, "%s\n", db_strerror(ua->db));
853 /* Now update JobTDate to lock onto Differental, if any */
855 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
856 bsendmsg(ua, "%s\n", db_strerror(ua->db));
858 if (rx->JobTDate == 0) {
859 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
863 /* Now find all Incremental Jobs after Full/dif save */
864 Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
865 cr.ClientId, fsr.FileSet, pool_select);
866 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
867 bsendmsg(ua, "%s\n", db_strerror(ua->db));
870 /* Get the JobIds from that list */
872 rx->last_jobid[0] = 0;
873 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
874 bsendmsg(ua, "%s\n", db_strerror(ua->db));
877 if (rx->JobIds[0] != 0) {
878 /* Display a list of Jobs selected for this restore */
879 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
881 bsendmsg(ua, _("No jobs found.\n"));
887 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
888 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
893 /* Return next JobId from comma separated list */
894 static int next_jobid_from_list(char **p, uint32_t *JobId)
900 for (int i=0; i<(int)sizeof(jobid); i++) {
901 if (*q == ',' || *q == 0) {
908 if (jobid[0] == 0 || !is_a_number(jobid)) {
912 *JobId = strtoul(jobid, NULL, 10);
917 * Callback handler to get JobId and FileIndex for files
919 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
921 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
922 rx->JobId = atoi(row[0]);
923 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
929 * Callback handler make list of JobIds
931 static int jobid_handler(void *ctx, int num_fields, char **row)
933 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
935 if (strcmp(rx->last_jobid, row[0]) == 0) {
936 return 0; /* duplicate id */
938 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
939 if (rx->JobIds[0] != 0) {
940 pm_strcat(&rx->JobIds, ",");
942 pm_strcat(&rx->JobIds, row[0]);
948 * Callback handler to pickup last Full backup JobTDate
950 static int last_full_handler(void *ctx, int num_fields, char **row)
952 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
954 rx->JobTDate = str_to_int64(row[1]);
959 * Callback handler build FileSet name prompt list
961 static int fileset_handler(void *ctx, int num_fields, char **row)
963 /* row[0] = FileSet (name) */
965 add_prompt((UAContext *)ctx, row[0]);
971 * Called here with each name to be added to the list. The name is
972 * added to the list if it is not already in the list.
974 * Used to make unique list of FileSets and MediaTypes
976 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
978 NAME_LIST *name = (NAME_LIST *)ctx;
980 if (name->num_ids == MAX_ID_LIST_LEN) {
983 if (name->num_ids == name->max_ids) {
984 if (name->max_ids == 0) {
985 name->max_ids = 1000;
986 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
988 name->max_ids = (name->max_ids * 3) / 2;
989 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
992 for (int i=0; i<name->num_ids; i++) {
993 if (strcmp(name->name[i], row[0]) == 0) {
994 return 0; /* already in list, return */
997 /* Add new name to list */
998 name->name[name->num_ids++] = bstrdup(row[0]);
1004 * Print names in the list
1006 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1008 for (int i=0; i < name_list->num_ids; i++) {
1009 bsendmsg(ua, "%s\n", name_list->name[i]);
1015 * Free names in the list
1017 static void free_name_list(NAME_LIST *name_list)
1019 for (int i=0; i < name_list->num_ids; i++) {
1020 free(name_list->name[i]);
1022 if (name_list->name) {
1023 free(name_list->name);
1024 name_list->name = NULL;
1026 name_list->max_ids = 0;
1027 name_list->num_ids = 0;
1030 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1032 if (name_list->num_ids > 1) {
1033 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1034 "Restore is not possible. The MediaTypes used are:\n"));
1035 print_name_list(ua, name_list);
1036 rx->store = select_storage_resource(ua);
1040 if (name_list->num_ids == 0) {
1041 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1042 rx->store = select_storage_resource(ua);
1046 rx->store = get_storage_resource(ua, false /* don't use default */);
1049 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1050 "MediaType %s, needed by the Jobs you selected.\n"
1051 "You will be allowed to select a Storage device later.\n"),
1052 name_list->name[0]);