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);
596 /* Find and insert jobid and File Index */
597 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
598 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
599 rx->query, db_strerror(ua->db));
602 bsendmsg(ua, _("No database record found for: %s\n"), file);
605 rx->selected_files++;
607 * Find the MediaTypes for this JobId and add to the name_list
609 Mmsg(&rx->query, uar_mediatype, rx->JobId);
610 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
611 bsendmsg(ua, "%s", db_strerror(ua->db));
617 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
621 /* Find path without the filename.
622 * I.e. everything after the last / is a "filename".
623 * OK, maybe it is a directory name, but we treat it like
624 * a filename. If we don't find a / then the whole name
625 * must be a path name (e.g. c:).
627 for (p=f=name; *p; p++) {
629 f = p; /* set pos of last slash */
632 if (*f == '/') { /* did we find a slash? */
633 f++; /* yes, point to filename */
634 } else { /* no, whole thing must be path name */
638 /* If filename doesn't exist (i.e. root directory), we
639 * simply create a blank name consisting of a single
640 * space. This makes handling zero length filenames
645 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
646 memcpy(rx->fname, f, rx->fnl); /* copy filename */
647 rx->fname[rx->fnl] = 0;
649 rx->fname[0] = ' '; /* blank filename */
656 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
657 memcpy(rx->path, name, rx->pnl);
658 rx->path[rx->pnl] = 0;
665 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
668 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
671 JobId_t JobId, last_JobId;
676 memset(&tree, 0, sizeof(TREE_CTX));
678 * Build the directory tree containing JobIds user selected
680 tree.root = new_tree(rx->TotalFiles);
681 tree.root->fname = nofname;
685 * For display purposes, the same JobId, with different volumes may
686 * appear more than once, however, we only insert it once.
689 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
691 if (JobId == last_JobId) {
692 continue; /* eliminate duplicate JobIds */
695 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
698 * Find files for this JobId and insert them in the tree
700 Mmsg(&rx->query, uar_sel_files, JobId);
701 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
702 bsendmsg(ua, "%s", db_strerror(ua->db));
705 * Find the MediaTypes for this JobId and add to the name_list
707 Mmsg(&rx->query, uar_mediatype, JobId);
708 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
709 bsendmsg(ua, "%s", db_strerror(ua->db));
712 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
713 items, items==1?"":"s");
715 /* Check MediaType and select storage that corresponds */
716 get_storage_from_mediatype(ua, &rx->name_list, rx);
718 if (find_arg(ua, _("all")) < 0) {
719 /* Let the user interact in selecting which files to restore */
720 OK = user_select_files_from_tree(&tree);
724 * Walk down through the tree finding all files marked to be
725 * extracted making a bootstrap file.
728 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
729 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
730 if (node->extract || node->extract_dir) {
731 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
732 add_findex(rx->bsr, node->JobId, node->FileIndex);
733 rx->selected_files++;
738 free_tree(tree.root); /* free the directory tree */
744 * This routine is used to get the current backup or a backup
745 * before the specified date.
747 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
752 char fileset_name[MAX_NAME_LENGTH];
754 char pool_select[MAX_NAME_LENGTH];
758 /* Create temp tables */
759 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
760 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
761 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
762 bsendmsg(ua, "%s\n", db_strerror(ua->db));
764 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
765 bsendmsg(ua, "%s\n", db_strerror(ua->db));
768 * Select Client from the Catalog
770 memset(&cr, 0, sizeof(cr));
771 if (!get_client_dbr(ua, &cr)) {
774 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
779 memset(&fsr, 0, sizeof(fsr));
780 i = find_arg_with_value(ua, "FileSet");
782 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
783 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
784 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
785 db_strerror(ua->db));
789 if (i < 0) { /* fileset not found */
790 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
791 start_prompt(ua, _("The defined FileSet resources are:\n"));
792 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
793 bsendmsg(ua, "%s\n", db_strerror(ua->db));
795 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
796 fileset_name, sizeof(fileset_name)) < 0) {
800 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
801 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
802 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
803 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
804 "Continuing anyway.\n"));
808 /* If Pool specified, add PoolId specification */
812 memset(&pr, 0, sizeof(pr));
813 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
814 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
815 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
817 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
821 /* Find JobId of last Full backup for this client, fileset */
822 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
824 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
825 bsendmsg(ua, "%s\n", db_strerror(ua->db));
829 /* Find all Volumes used by that JobId */
830 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
831 bsendmsg(ua, "%s\n", db_strerror(ua->db));
834 /* Note, this is needed because I don't seem to get the callback
835 * from the call just above.
838 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
839 bsendmsg(ua, "%s\n", db_strerror(ua->db));
841 if (rx->JobTDate == 0) {
842 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
846 /* Now find most recent Differental Job after Full save, if any */
847 Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
848 cr.ClientId, fsr.FileSet, pool_select);
849 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
850 bsendmsg(ua, "%s\n", db_strerror(ua->db));
852 /* Now update JobTDate to lock onto Differental, if any */
854 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
855 bsendmsg(ua, "%s\n", db_strerror(ua->db));
857 if (rx->JobTDate == 0) {
858 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
862 /* Now find all Incremental Jobs after Full/dif save */
863 Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
864 cr.ClientId, fsr.FileSet, pool_select);
865 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
866 bsendmsg(ua, "%s\n", db_strerror(ua->db));
869 /* Get the JobIds from that list */
871 rx->last_jobid[0] = 0;
872 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
873 bsendmsg(ua, "%s\n", db_strerror(ua->db));
876 if (rx->JobIds[0] != 0) {
877 /* Display a list of Jobs selected for this restore */
878 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
880 bsendmsg(ua, _("No jobs found.\n"));
886 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
887 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
892 /* Return next JobId from comma separated list */
893 static int next_jobid_from_list(char **p, uint32_t *JobId)
899 for (int i=0; i<(int)sizeof(jobid); i++) {
900 if (*q == ',' || *q == 0) {
907 if (jobid[0] == 0 || !is_a_number(jobid)) {
911 *JobId = strtoul(jobid, NULL, 10);
916 * Callback handler to get JobId and FileIndex for files
918 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
920 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
921 rx->JobId = atoi(row[0]);
922 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
928 * Callback handler make list of JobIds
930 static int jobid_handler(void *ctx, int num_fields, char **row)
932 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
934 if (strcmp(rx->last_jobid, row[0]) == 0) {
935 return 0; /* duplicate id */
937 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
938 if (rx->JobIds[0] != 0) {
939 pm_strcat(&rx->JobIds, ",");
941 pm_strcat(&rx->JobIds, row[0]);
947 * Callback handler to pickup last Full backup JobTDate
949 static int last_full_handler(void *ctx, int num_fields, char **row)
951 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
953 rx->JobTDate = str_to_int64(row[1]);
958 * Callback handler build FileSet name prompt list
960 static int fileset_handler(void *ctx, int num_fields, char **row)
962 /* row[0] = FileSet (name) */
964 add_prompt((UAContext *)ctx, row[0]);
970 * Called here with each name to be added to the list. The name is
971 * added to the list if it is not already in the list.
973 * Used to make unique list of FileSets and MediaTypes
975 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
977 NAME_LIST *name = (NAME_LIST *)ctx;
979 if (name->num_ids == MAX_ID_LIST_LEN) {
982 if (name->num_ids == name->max_ids) {
983 if (name->max_ids == 0) {
984 name->max_ids = 1000;
985 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
987 name->max_ids = (name->max_ids * 3) / 2;
988 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
991 for (int i=0; i<name->num_ids; i++) {
992 if (strcmp(name->name[i], row[0]) == 0) {
993 return 0; /* already in list, return */
996 /* Add new name to list */
997 name->name[name->num_ids++] = bstrdup(row[0]);
1003 * Print names in the list
1005 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1007 for (int i=0; i < name_list->num_ids; i++) {
1008 bsendmsg(ua, "%s\n", name_list->name[i]);
1014 * Free names in the list
1016 static void free_name_list(NAME_LIST *name_list)
1018 for (int i=0; i < name_list->num_ids; i++) {
1019 free(name_list->name[i]);
1021 if (name_list->name) {
1022 free(name_list->name);
1023 name_list->name = NULL;
1025 name_list->max_ids = 0;
1026 name_list->num_ids = 0;
1029 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1031 if (name_list->num_ids > 1) {
1032 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1033 "Restore is not possible. The MediaTypes used are:\n"));
1034 print_name_list(ua, name_list);
1035 rx->store = select_storage_resource(ua);
1039 if (name_list->num_ids == 0) {
1040 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1041 rx->store = select_storage_resource(ua);
1045 rx->store = get_storage_resource(ua, false /* don't use default */);
1048 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1049 "MediaType %s, needed by the Jobs you selected.\n"
1050 "You will be allowed to select a Storage device later.\n"),
1051 name_list->name[0]);