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-2003 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_dec, *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;
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 void 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));
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 while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)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 build_directory_tree(ua, &rx);
168 case 2: /* select by filename, no tree needed */
173 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
174 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
177 write_bsr_file(ua, rx.bsr);
178 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
179 rx.selected_files==1?"":"s");
181 bsendmsg(ua, _("No files selected to be restored.\n"));
185 if (rx.restore_jobs == 1) {
186 job = rx.restore_job;
188 job = select_restore_job_resource(ua);
194 get_client_name(ua, &rx);
195 if (!rx.ClientName) {
196 bsendmsg(ua, _("No Restore Job resource found!\n"));
200 /* Build run command */
203 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
205 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
206 working_directory, rx.where);
209 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
210 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
213 if (find_arg(ua, _("run")) >= 0) {
214 pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */
216 Dmsg1(400, "Submitting: %s\n", ua->cmd);
218 run_cmd(ua, ua->cmd);
220 bsendmsg(ua, _("Restore command done.\n"));
230 static void free_rx(RESTORE_CTX *rx)
235 free_pool_memory(rx->JobIds);
239 free_pool_memory(rx->fname);
243 free_pool_memory(rx->path);
247 free_pool_memory(rx->query);
250 free_name_list(&rx->name_list);
253 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
255 /* If no client name specified yet, get it now */
256 if (!rx->ClientName[0]) {
258 /* try command line argument */
259 int i = find_arg_with_value(ua, _("client"));
261 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
264 memset(&cr, 0, sizeof(cr));
265 if (!get_client_dbr(ua, &cr)) {
268 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
274 * The first step in the restore process is for the user to
275 * select a list of JobIds from which he will subsequently
276 * select which files are to be restored.
278 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
281 char date[MAX_TIME_LENGTH];
282 bool have_date = false;
288 "List last 20 Jobs run",
289 "List Jobs where a given File is saved",
290 "Enter list of JobIds to select",
291 "Enter SQL list command",
292 "Select the most recent backup for a client",
293 "Select backup for a client before a specified time",
294 "Enter a list of files to restore",
295 "Enter a list of files to restore before a specified time",
310 switch (find_arg_keyword(ua, kw)) {
313 i = find_arg_with_value(ua, _("jobid"));
317 pm_strcpy(&rx->JobIds, ua->argv[i]);
318 ua->argk[i][0] = 0; /* "consume" jobid= */
322 case 1: /* current */
323 bstrutime(date, sizeof(date), time(NULL));
327 i = find_arg_with_value(ua, _("before"));
331 if (str_to_utime(ua->argv[i]) == 0) {
332 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
335 bstrncpy(date, ua->argv[i], sizeof(date));
340 bstrutime(date, sizeof(date), time(NULL));
342 if (!get_client_name(ua, rx)) {
346 i = find_arg_with_value(ua, _("file"));
350 pm_strcpy(&ua->cmd, ua->argv[i]);
351 insert_one_file(ua, rx, date);
352 ua->argk[i][0] = 0; /* "consume" the file= */
354 /* Check MediaType and select storage that corresponds */
355 get_storage_from_mediatype(ua, &rx->name_list, rx);
359 bstrutime(date, sizeof(date), time(NULL));
361 if (!select_backups_before_date(ua, rx, date)) {
366 case 5: /* pool specified */
367 i = find_arg_with_value(ua, "pool");
369 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
371 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
379 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
380 "to be restored. You will be presented several methods\n"
381 "of specifying the JobIds. Then you will be allowed to\n"
382 "select which files from those JobIds are to be restored.\n\n"));
385 /* If choice not already made above, prompt */
390 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
391 for (int i=0; list[i]; i++) {
392 add_prompt(ua, list[i]);
395 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
398 case 0: /* list last 20 Jobs run */
399 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
402 case 1: /* list where a file is saved */
403 if (!get_cmd(ua, _("Enter Filename: "))) {
406 len = strlen(ua->cmd);
407 fname = (char *)malloc(len * 2 + 1);
408 db_escape_string(fname, ua->cmd, len);
409 Mmsg(&rx->query, uar_file, fname);
411 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
414 case 2: /* enter a list of JobIds */
415 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
418 pm_strcpy(&rx->JobIds, ua->cmd);
420 case 3: /* Enter an SQL list command */
421 if (!get_cmd(ua, _("Enter SQL list command: "))) {
424 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
427 case 4: /* Select the most recent backups */
428 bstrutime(date, sizeof(date), time(NULL));
429 if (!select_backups_before_date(ua, rx, date)) {
433 case 5: /* select backup at specified time */
434 if (!get_date(ua, date, sizeof(date))) {
437 if (!select_backups_before_date(ua, rx, date)) {
441 case 6: /* Enter files */
442 bstrutime(date, sizeof(date), time(NULL));
443 if (!get_client_name(ua, rx)) {
446 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
447 "containg a list of file names, and terminate\n"
448 "them with a blank line.\n"));
450 if (!get_cmd(ua, _("Enter filename: "))) {
453 len = strlen(ua->cmd);
457 insert_one_file(ua, rx, date);
459 /* Check MediaType and select storage that corresponds */
460 get_storage_from_mediatype(ua, &rx->name_list, rx);
462 case 7: /* enter files backed up before specified time */
463 if (!get_date(ua, date, sizeof(date))) {
466 if (!get_client_name(ua, rx)) {
469 bsendmsg(ua, _("Enter file names, or < to enter a filename\n"
470 "containg a list of file names, and terminate\n"
471 "them with a blank line.\n"));
473 if (!get_cmd(ua, _("Enter filename: "))) {
476 len = strlen(ua->cmd);
480 insert_one_file(ua, rx, date);
482 /* Check MediaType and select storage that corresponds */
483 get_storage_from_mediatype(ua, &rx->name_list, rx);
487 case 8: /* Cancel or quit */
492 if (*rx->JobIds == 0) {
493 bsendmsg(ua, _("No Jobs selected.\n"));
496 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
497 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
499 memset(&jr, 0, sizeof(JOB_DBR));
502 for (p=rx->JobIds; ; ) {
503 int stat = next_jobid_from_list(&p, &JobId);
505 bsendmsg(ua, _("Invalid JobId in list.\n"));
511 if (jr.JobId == JobId) {
512 continue; /* duplicate of last JobId */
515 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
516 bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"),
517 JobId, db_strerror(ua->db));
520 rx->TotalFiles += jr.JobFiles;
528 static int get_date(UAContext *ua, char *date, int date_len)
530 bsendmsg(ua, _("The restored files will the most current backup\n"
531 "BEFORE the date you specify below.\n\n"));
533 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
536 if (str_to_utime(ua->cmd) != 0) {
539 bsendmsg(ua, _("Improper date format.\n"));
541 bstrncpy(date, ua->cmd, date_len);
546 * Insert a single file, or read a list of files from a file
548 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
558 if ((ffd = fopen(p, "r")) == NULL) {
559 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
563 while (fgets(file, sizeof(file), ffd)) {
565 if (!insert_file_into_findex_list(ua, rx, file, date)) {
566 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
572 insert_file_into_findex_list(ua, rx, ua->cmd, date);
578 * For a given file (path+filename), split into path and file, then
579 * lookup the most recent backup in the catalog to get the JobId
580 * and FileIndex, then insert them into the findex list.
582 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
585 strip_trailing_junk(file);
586 split_path_and_filename(rx, file);
587 Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
589 /* Find and insert jobid and File Index */
590 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
591 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
592 rx->query, db_strerror(ua->db));
595 bsendmsg(ua, _("No database record found for: %s\n"), file);
598 rx->selected_files++;
600 * Find the MediaTypes for this JobId and add to the name_list
602 Mmsg(&rx->query, uar_mediatype, rx->JobId);
603 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
604 bsendmsg(ua, "%s", db_strerror(ua->db));
610 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
614 /* Find path without the filename.
615 * I.e. everything after the last / is a "filename".
616 * OK, maybe it is a directory name, but we treat it like
617 * a filename. If we don't find a / then the whole name
618 * must be a path name (e.g. c:).
620 for (p=f=name; *p; p++) {
622 f = p; /* set pos of last slash */
625 if (*f == '/') { /* did we find a slash? */
626 f++; /* yes, point to filename */
627 } else { /* no, whole thing must be path name */
631 /* If filename doesn't exist (i.e. root directory), we
632 * simply create a blank name consisting of a single
633 * space. This makes handling zero length filenames
638 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
639 memcpy(rx->fname, f, rx->fnl); /* copy filename */
640 rx->fname[rx->fnl] = 0;
642 rx->fname[0] = ' '; /* blank filename */
649 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
650 memcpy(rx->path, name, rx->pnl);
651 rx->path[rx->pnl] = 0;
658 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
661 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
664 JobId_t JobId, last_JobId;
668 memset(&tree, 0, sizeof(TREE_CTX));
670 * Build the directory tree containing JobIds user selected
672 tree.root = new_tree(rx->TotalFiles);
673 tree.root->fname = nofname;
677 * For display purposes, the same JobId, with different volumes may
678 * appear more than once, however, we only insert it once.
681 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
683 if (JobId == last_JobId) {
684 continue; /* eliminate duplicate JobIds */
687 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
690 * Find files for this JobId and insert them in the tree
692 Mmsg(&rx->query, uar_sel_files, JobId);
693 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
694 bsendmsg(ua, "%s", db_strerror(ua->db));
697 * Find the MediaTypes for this JobId and add to the name_list
699 Mmsg(&rx->query, uar_mediatype, JobId);
700 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
701 bsendmsg(ua, "%s", db_strerror(ua->db));
704 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
705 items, items==1?"":"s");
707 /* Check MediaType and select storage that corresponds */
708 get_storage_from_mediatype(ua, &rx->name_list, rx);
710 if (find_arg(ua, _("all")) < 0) {
711 /* Let the user select which files to restore */
712 user_select_files_from_tree(&tree);
716 * Walk down through the tree finding all files marked to be
717 * extracted making a bootstrap file.
719 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
720 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
722 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
723 add_findex(rx->bsr, node->JobId, node->FileIndex);
724 rx->selected_files++;
728 free_tree(tree.root); /* free the directory tree */
733 * This routine is used to get the current backup or a backup
734 * before the specified date.
736 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
741 char fileset_name[MAX_NAME_LENGTH];
743 char pool_select[MAX_NAME_LENGTH];
747 /* Create temp tables */
748 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
749 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
750 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
751 bsendmsg(ua, "%s\n", db_strerror(ua->db));
753 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
754 bsendmsg(ua, "%s\n", db_strerror(ua->db));
757 * Select Client from the Catalog
759 memset(&cr, 0, sizeof(cr));
760 if (!get_client_dbr(ua, &cr)) {
763 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
768 memset(&fsr, 0, sizeof(fsr));
769 i = find_arg_with_value(ua, "FileSet");
771 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
772 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
773 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
774 db_strerror(ua->db));
778 if (i < 0) { /* fileset not found */
779 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
780 start_prompt(ua, _("The defined FileSet resources are:\n"));
781 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
782 bsendmsg(ua, "%s\n", db_strerror(ua->db));
784 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
785 fileset_name, sizeof(fileset_name)) < 0) {
789 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
790 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
791 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
792 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
793 "Continuing anyway.\n"));
797 /* If Pool specified, add PoolId specification */
801 memset(&pr, 0, sizeof(pr));
802 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
803 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
804 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
806 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
810 /* Find JobId of last Full backup for this client, fileset */
811 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
813 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
814 bsendmsg(ua, "%s\n", db_strerror(ua->db));
818 /* Find all Volumes used by that JobId */
819 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
820 bsendmsg(ua, "%s\n", db_strerror(ua->db));
823 /* Note, this is needed because I don't seem to get the callback
824 * from the call just above.
827 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
828 bsendmsg(ua, "%s\n", db_strerror(ua->db));
830 if (rx->JobTDate == 0) {
831 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
835 /* Now find all Incremental/Decremental Jobs after Full save */
836 Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
837 cr.ClientId, fsr.FileSet, pool_select);
838 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
839 bsendmsg(ua, "%s\n", db_strerror(ua->db));
842 /* Get the JobIds from that list */
844 rx->last_jobid[0] = 0;
845 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
846 bsendmsg(ua, "%s\n", db_strerror(ua->db));
849 if (rx->JobIds[0] != 0) {
850 /* Display a list of Jobs selected for this restore */
851 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
853 bsendmsg(ua, _("No jobs found.\n"));
859 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
860 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
865 /* Return next JobId from comma separated list */
866 static int next_jobid_from_list(char **p, uint32_t *JobId)
872 for (int i=0; i<(int)sizeof(jobid); i++) {
873 if (*q == ',' || *q == 0) {
880 if (jobid[0] == 0 || !is_a_number(jobid)) {
884 *JobId = strtoul(jobid, NULL, 10);
889 * Callback handler to get JobId and FileIndex for files
891 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
893 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
894 rx->JobId = atoi(row[0]);
895 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
901 * Callback handler make list of JobIds
903 static int jobid_handler(void *ctx, int num_fields, char **row)
905 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
907 if (strcmp(rx->last_jobid, row[0]) == 0) {
908 return 0; /* duplicate id */
910 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
911 if (rx->JobIds[0] != 0) {
912 pm_strcat(&rx->JobIds, ",");
914 pm_strcat(&rx->JobIds, row[0]);
920 * Callback handler to pickup last Full backup JobTDate
922 static int last_full_handler(void *ctx, int num_fields, char **row)
924 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
926 rx->JobTDate = str_to_int64(row[1]);
931 * Callback handler build FileSet name prompt list
933 static int fileset_handler(void *ctx, int num_fields, char **row)
935 /* row[0] = FileSet (name) */
937 add_prompt((UAContext *)ctx, row[0]);
943 * Called here with each name to be added to the list. The name is
944 * added to the list if it is not already in the list.
946 * Used to make unique list of FileSets and MediaTypes
948 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
950 NAME_LIST *name = (NAME_LIST *)ctx;
952 if (name->num_ids == MAX_ID_LIST_LEN) {
955 if (name->num_ids == name->max_ids) {
956 if (name->max_ids == 0) {
957 name->max_ids = 1000;
958 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
960 name->max_ids = (name->max_ids * 3) / 2;
961 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
964 for (int i=0; i<name->num_ids; i++) {
965 if (strcmp(name->name[i], row[0]) == 0) {
966 return 0; /* already in list, return */
969 /* Add new name to list */
970 name->name[name->num_ids++] = bstrdup(row[0]);
976 * Print names in the list
978 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
980 for (int i=0; i < name_list->num_ids; i++) {
981 bsendmsg(ua, "%s\n", name_list->name[i]);
987 * Free names in the list
989 static void free_name_list(NAME_LIST *name_list)
991 for (int i=0; i < name_list->num_ids; i++) {
992 free(name_list->name[i]);
994 if (name_list->name) {
995 free(name_list->name);
996 name_list->name = NULL;
998 name_list->max_ids = 0;
999 name_list->num_ids = 0;
1002 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1004 if (name_list->num_ids > 1) {
1005 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1006 "Restore is not possible. The MediaTypes used are:\n"));
1007 print_name_list(ua, name_list);
1008 rx->store = select_storage_resource(ua);
1012 if (name_list->num_ids == 0) {
1013 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1014 rx->store = select_storage_resource(ua);
1018 rx->store = get_storage_resource(ua, false /* don't use default */);
1021 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1022 "MediaType %s, needed by the Jobs you selected.\n"
1023 "You will be allowed to select a Storage device later.\n"),
1024 name_list->name[0]);