]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_restore.c
Add IP address to BSOCK + authentication sets console + tweak run argument handling...
[bacula/bacula] / bacula / src / dird / ua_restore.c
1 /*
2  *
3  *   Bacula Director -- User Agent Database restore Command
4  *      Creates a bootstrap file for restoring files and
5  *      starts the restore job.
6  *
7  *      Tree handling routines split into ua_tree.c July MMIII.
8  *      BSR (bootstrap record) handling routines split into
9  *        bsr.c July MMIII
10  *
11  *     Kern Sibbald, July MMII
12  *
13  *   Version $Id$
14  */
15
16 /*
17    Copyright (C) 2002-2004 Kern Sibbald and John Walker
18
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.
23
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.
28
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,
32    MA 02111-1307, USA.
33
34  */
35
36 #include "bacula.h"
37 #include "dird.h"
38
39
40 /* Imported functions */
41 extern int run_cmd(UAContext *ua, char *cmd);
42 extern void print_bsr(UAContext *ua, RBSR *bsr);
43
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;
51
52
53 struct NAME_LIST {
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 */
59 };
60
61
62 /* Main structure for obtaining JobIds or Files to be restored */
63 struct RESTORE_CTX {
64    utime_t JobTDate;
65    uint32_t TotalFiles;
66    uint32_t JobId;
67    char ClientName[MAX_NAME_LENGTH];
68    char last_jobid[10];
69    POOLMEM *JobIds;                   /* User entered string of JobIds */
70    STORE  *store;
71    JOB *restore_job;
72    POOL *pool;
73    int restore_jobs;
74    uint32_t selected_files;
75    char *where;
76    RBSR *bsr;
77    POOLMEM *fname;
78    POOLMEM *path;
79    POOLMEM *query;
80    int fnl;
81    int pnl;
82    bool found;
83    NAME_LIST name_list;
84 };
85
86
87 #define MAX_ID_LIST_LEN 1000000
88
89
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,
106                                         char *date);
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);
110
111 /*
112  *   Restore files
113  *
114  */
115 int restore_cmd(UAContext *ua, char *cmd)
116 {
117    RESTORE_CTX rx;                    /* restore context */
118    JOB *job = NULL;
119    int i;
120
121    memset(&rx, 0, sizeof(rx));
122
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);
127    rx.bsr = new_bsr();
128
129    i = find_arg_with_value(ua, "where");
130    if (i >= 0) {
131       rx.where = ua->argv[i];
132    }
133
134    if (!open_db(ua)) {
135       goto bail_out;
136    }
137
138    /* Ensure there is at least one Restore Job */
139    LockRes();
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;
144          }
145          rx.restore_jobs++;
146       }
147    }
148    UnlockRes();
149    if (!rx.restore_jobs) {
150       bsendmsg(ua, _(
151          "No Restore Job Resource found. You must create at least\n"
152          "one before running this command.\n"));
153       goto bail_out;
154    }
155
156    /* 
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
160     *  add_findex()
161     */
162    switch (user_select_jobids_or_files(ua, &rx)) {
163    case 0:
164       goto bail_out;
165    case 1:                            /* select by jobid */
166       if (!build_directory_tree(ua, &rx)) {
167          bsendmsg(ua, _("Restore not done.\n"));
168          goto bail_out;
169       }
170       break;
171    case 2:                            /* select by filename, no tree needed */
172       break;
173    }
174
175    if (rx.bsr->JobId) {
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"));
178          goto bail_out;
179       }
180       write_bsr_file(ua, rx.bsr);
181       bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
182          rx.selected_files==1?"":"s");
183    } else {
184       bsendmsg(ua, _("No files selected to be restored.\n"));
185       goto bail_out;
186    }
187
188    if (rx.restore_jobs == 1) {
189       job = rx.restore_job;
190    } else {
191       job = select_restore_job_resource(ua);
192    }
193    if (!job) {
194       goto bail_out;
195    }
196
197    get_client_name(ua, &rx);
198    if (!rx.ClientName) {
199       bsendmsg(ua, _("No Restore Job resource found!\n"));
200       goto bail_out;
201    }
202
203    /* Build run command */
204    if (rx.where) {
205       Mmsg(&ua->cmd, 
206           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
207           " where=\"%s\" files=%d",
208           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
209           working_directory, rx.where, rx.selected_files);
210    } else {
211       Mmsg(&ua->cmd, 
212           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
213           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
214           working_directory);
215    }
216    if (find_arg(ua, _("yes")) > 0) {
217       pm_strcat(&ua->cmd, " yes");    /* pass it on to the run command */
218    }
219    Dmsg1(400, "Submitting: %s\n", ua->cmd);
220    parse_ua_args(ua);
221    run_cmd(ua, ua->cmd);
222
223    bsendmsg(ua, _("Restore command done.\n"));
224    free_rx(&rx);
225    return 1;
226
227 bail_out:
228    free_rx(&rx);
229    return 0;
230
231 }
232
233 static void free_rx(RESTORE_CTX *rx) 
234 {
235    free_bsr(rx->bsr);
236    rx->bsr = NULL;
237    if (rx->JobIds) {
238       free_pool_memory(rx->JobIds);
239       rx->JobIds = NULL;
240    }
241    if (rx->fname) {
242       free_pool_memory(rx->fname);
243       rx->fname = NULL;
244    }
245    if (rx->path) {
246       free_pool_memory(rx->path);
247       rx->path = NULL;
248    }
249    if (rx->query) {
250       free_pool_memory(rx->query);
251       rx->query = NULL;
252    }
253    free_name_list(&rx->name_list);
254 }
255
256 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
257 {
258    /* If no client name specified yet, get it now */
259    if (!rx->ClientName[0]) {
260       CLIENT_DBR cr;
261       /* try command line argument */
262       int i = find_arg_with_value(ua, _("client"));
263       if (i >= 0) {
264          bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
265          return 1;
266       }
267       memset(&cr, 0, sizeof(cr));
268       if (!get_client_dbr(ua, &cr)) {
269          return 0;
270       }
271       bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
272    }
273    return 1;
274 }
275
276 /*
277  * The first step in the restore process is for the user to 
278  *  select a list of JobIds from which he will subsequently
279  *  select which files are to be restored.
280  */
281 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
282 {
283    char *p;
284    char date[MAX_TIME_LENGTH];
285    bool have_date = false;
286    JobId_t JobId;
287    JOB_DBR jr;
288    bool done = false;
289    int i;
290    char *list[] = { 
291       "List last 20 Jobs run",
292       "List Jobs where a given File is saved",
293       "Enter list of JobIds to select",
294       "Enter SQL list command", 
295       "Select the most recent backup for a client",
296       "Select backup for a client before a specified time",
297       "Enter a list of files to restore",
298       "Enter a list of files to restore before a specified time",
299       "Cancel",
300       NULL };
301
302    char *kw[] = {
303       "jobid",     /* 0 */
304       "current",   /* 1 */
305       "before",    /* 2 */
306       "file",      /* 3 */
307       "select",    /* 4 */
308       "pool",      /* 5 */
309       NULL
310    };
311
312    *rx->JobIds = 0;
313    switch (find_arg_keyword(ua, kw)) {
314    case 0:                            /* jobid */
315       for ( ;; ) {
316          i = find_arg_with_value(ua, _("jobid"));
317          if (i < 0) {
318             break;
319          }
320          pm_strcpy(&rx->JobIds, ua->argv[i]);
321          ua->argk[i][0] = 0;          /* "consume" jobid= */
322       }
323       done = true;
324       break;
325    case 1:                            /* current */
326       bstrutime(date, sizeof(date), time(NULL));
327       have_date = true;
328       break;
329    case 2:                            /* before */
330       i = find_arg_with_value(ua, _("before"));
331       if (i < 0) {
332          return 0;
333       }
334       if (str_to_utime(ua->argv[i]) == 0) {
335          bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
336          return 0;
337       }
338       bstrncpy(date, ua->argv[i], sizeof(date));
339       have_date = true;
340       break;
341    case 3:                            /* file */
342       if (!have_date) {
343          bstrutime(date, sizeof(date), time(NULL));
344       }
345       if (!get_client_name(ua, rx)) {
346          return 0;
347       }
348       for ( ;; ) {
349          i = find_arg_with_value(ua, _("file"));
350          if (i < 0) {
351             break;
352          }
353          pm_strcpy(&ua->cmd, ua->argv[i]);
354          insert_one_file(ua, rx, date);
355          ua->argk[i][0] = 0;          /* "consume" the file= */
356       }
357       /* Check MediaType and select storage that corresponds */
358       get_storage_from_mediatype(ua, &rx->name_list, rx);
359       return 2;
360    case 4:                            /* select */
361       if (!have_date) {
362          bstrutime(date, sizeof(date), time(NULL));
363       }
364       if (!select_backups_before_date(ua, rx, date)) {
365          return 0;
366       }
367       done = true;
368       break;
369    case 5:                            /* pool specified */
370       i = find_arg_with_value(ua, "pool");
371       if (i >= 0) {
372          rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
373       } else {
374          bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
375       }
376       break;
377    default:
378       break;
379    }
380        
381    if (!done) {
382       bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
383                   "to be restored. You will be presented several methods\n"
384                   "of specifying the JobIds. Then you will be allowed to\n"
385                   "select which files from those JobIds are to be restored.\n\n"));
386    }
387
388    /* If choice not already made above, prompt */
389    for ( ; !done; ) {
390       char *fname;
391       int len;
392
393       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
394       for (int i=0; list[i]; i++) {
395          add_prompt(ua, list[i]);
396       }
397       done = true;
398       switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
399       case -1:                        /* error */
400          return 0;
401       case 0:                         /* list last 20 Jobs run */
402          db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
403          done = false;
404          break;
405       case 1:                         /* list where a file is saved */
406          if (!get_cmd(ua, _("Enter Filename: "))) {
407             return 0;
408          }
409          len = strlen(ua->cmd);
410          fname = (char *)malloc(len * 2 + 1);
411          db_escape_string(fname, ua->cmd, len);
412          Mmsg(&rx->query, uar_file, fname);
413          free(fname);
414          db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
415          done = false;
416          break;
417       case 2:                         /* enter a list of JobIds */
418          if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
419             return 0;
420          }
421          pm_strcpy(&rx->JobIds, ua->cmd);
422          break;
423       case 3:                         /* Enter an SQL list command */
424          if (!get_cmd(ua, _("Enter SQL list command: "))) {
425             return 0;
426          }
427          db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
428          done = false;
429          break;
430       case 4:                         /* Select the most recent backups */
431          bstrutime(date, sizeof(date), time(NULL));
432          if (!select_backups_before_date(ua, rx, date)) {
433             return 0;
434          }
435          break;
436       case 5:                         /* select backup at specified time */
437          if (!get_date(ua, date, sizeof(date))) {
438             return 0;
439          }
440          if (!select_backups_before_date(ua, rx, date)) {
441             return 0;
442          }
443          break;
444       case 6:                         /* Enter files */
445          bstrutime(date, sizeof(date), time(NULL));
446          if (!get_client_name(ua, rx)) {
447             return 0;
448          }
449          bsendmsg(ua, _("Enter file names, or < to enter a filename\n"      
450                         "containg a list of file names, and terminate\n"
451                         "them with a blank line.\n"));
452          for ( ;; ) {
453             if (!get_cmd(ua, _("Enter filename: "))) {
454                return 0;
455             }
456             len = strlen(ua->cmd);
457             if (len == 0) {
458                break;
459             }
460             insert_one_file(ua, rx, date);
461          }
462          /* Check MediaType and select storage that corresponds */
463          get_storage_from_mediatype(ua, &rx->name_list, rx);
464          return 2;
465        case 7:                        /* enter files backed up before specified time */
466          if (!get_date(ua, date, sizeof(date))) {
467             return 0;
468          }
469          if (!get_client_name(ua, rx)) {
470             return 0;
471          }
472          bsendmsg(ua, _("Enter file names, or < to enter a filename\n"      
473                         "containg a list of file names, and terminate\n"
474                         "them with a blank line.\n"));
475          for ( ;; ) {
476             if (!get_cmd(ua, _("Enter filename: "))) {
477                return 0;
478             }
479             len = strlen(ua->cmd);
480             if (len == 0) {
481                break;
482             }
483             insert_one_file(ua, rx, date);
484          }
485          /* Check MediaType and select storage that corresponds */
486          get_storage_from_mediatype(ua, &rx->name_list, rx);
487          return 2;
488
489       
490       case 8:                         /* Cancel or quit */
491          return 0;
492       }
493    }
494
495    if (*rx->JobIds == 0) {
496       bsendmsg(ua, _("No Jobs selected.\n"));
497       return 0;
498    }
499    bsendmsg(ua, _("You have selected the following JobId%s: %s\n"), 
500       strchr(rx->JobIds,',')?"s":"",rx->JobIds);
501
502    memset(&jr, 0, sizeof(JOB_DBR));
503
504    rx->TotalFiles = 0;
505    for (p=rx->JobIds; ; ) {
506       int stat = next_jobid_from_list(&p, &JobId);
507       if (stat < 0) {
508          bsendmsg(ua, _("Invalid JobId in list.\n"));
509          return 0;
510       }
511       if (stat == 0) {
512          break;
513       }
514       if (jr.JobId == JobId) {
515          continue;                    /* duplicate of last JobId */
516       }
517       jr.JobId = JobId;
518       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
519          bsendmsg(ua, _("Unable to get Job record for JobId=%u: ERR=%s\n"), 
520             JobId, db_strerror(ua->db));
521          return 0;
522       }
523       rx->TotalFiles += jr.JobFiles;
524    }
525    return 1;
526 }
527
528 /* 
529  * Get date from user
530  */
531 static int get_date(UAContext *ua, char *date, int date_len)
532 {
533    bsendmsg(ua, _("The restored files will the most current backup\n"
534                   "BEFORE the date you specify below.\n\n"));
535    for ( ;; ) {
536       if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
537          return 0;
538       }
539       if (str_to_utime(ua->cmd) != 0) {
540          break;
541       }
542       bsendmsg(ua, _("Improper date format.\n"));
543    }              
544    bstrncpy(date, ua->cmd, date_len);
545    return 1;
546 }
547
548 /*
549  * Insert a single file, or read a list of files from a file 
550  */
551 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date)
552 {
553    FILE *ffd;
554    char file[5000];
555    char *p = ua->cmd;
556    int line = 0;
557   
558    switch (*p) {
559    case '<':
560       p++;
561       if ((ffd = fopen(p, "r")) == NULL) {
562          bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
563             p, strerror(errno));
564          break;
565       }
566       while (fgets(file, sizeof(file), ffd)) {
567          line++;
568          if (!insert_file_into_findex_list(ua, rx, file, date)) {
569             bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
570          }
571       }
572       fclose(ffd);
573       break;
574    default:
575       insert_file_into_findex_list(ua, rx, ua->cmd, date);
576       break;
577    }
578 }
579
580 /*
581  * For a given file (path+filename), split into path and file, then
582  *   lookup the most recent backup in the catalog to get the JobId
583  *   and FileIndex, then insert them into the findex list.
584  */
585 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file, 
586                                         char *date)
587 {
588    strip_trailing_junk(file);
589    split_path_and_filename(rx, file);
590    Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
591    rx->found = false;
592    /* Find and insert jobid and File Index */
593    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
594       bsendmsg(ua, _("Query failed: %s. ERR=%s\n"), 
595          rx->query, db_strerror(ua->db));
596    }
597    if (!rx->found) {
598       bsendmsg(ua, _("No database record found for: %s\n"), file);
599       return 0;
600    }
601    rx->selected_files++;
602    /*
603     * Find the MediaTypes for this JobId and add to the name_list
604     */
605    Mmsg(&rx->query, uar_mediatype, rx->JobId);
606    if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
607       bsendmsg(ua, "%s", db_strerror(ua->db));
608       return 0;
609    }
610    return 1;
611 }
612
613 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
614 {
615    char *p, *f;
616
617    /* Find path without the filename.  
618     * I.e. everything after the last / is a "filename".
619     * OK, maybe it is a directory name, but we treat it like
620     * a filename. If we don't find a / then the whole name
621     * must be a path name (e.g. c:).
622     */
623    for (p=f=name; *p; p++) {
624       if (*p == '/') {
625          f = p;                       /* set pos of last slash */
626       }
627    }
628    if (*f == '/') {                   /* did we find a slash? */
629       f++;                            /* yes, point to filename */
630    } else {                           /* no, whole thing must be path name */
631       f = p;
632    }
633
634    /* If filename doesn't exist (i.e. root directory), we
635     * simply create a blank name consisting of a single 
636     * space. This makes handling zero length filenames
637     * easier.
638     */
639    rx->fnl = p - f;
640    if (rx->fnl > 0) {
641       rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
642       memcpy(rx->fname, f, rx->fnl);    /* copy filename */
643       rx->fname[rx->fnl] = 0;
644    } else {
645       rx->fname[0] = ' ';            /* blank filename */
646       rx->fname[1] = 0;
647       rx->fnl = 1;
648    }
649
650    rx->pnl = f - name;    
651    if (rx->pnl > 0) {
652       rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
653       memcpy(rx->path, name, rx->pnl);
654       rx->path[rx->pnl] = 0;
655    } else {
656       rx->path[0] = ' ';
657       rx->path[1] = 0;
658       rx->pnl = 1;
659    }
660
661    Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
662 }
663
664 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
665 {
666    TREE_CTX tree;
667    JobId_t JobId, last_JobId;
668    char *p;
669    char *nofname = "";
670    bool OK = true;
671
672    memset(&tree, 0, sizeof(TREE_CTX));
673    /* 
674     * Build the directory tree containing JobIds user selected
675     */
676    tree.root = new_tree(rx->TotalFiles);
677    tree.root->fname = nofname;
678    tree.ua = ua;
679    last_JobId = 0;
680    /*
681     * For display purposes, the same JobId, with different volumes may
682     * appear more than once, however, we only insert it once.
683     */
684    int items = 0;
685    for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
686
687       if (JobId == last_JobId) {             
688          continue;                    /* eliminate duplicate JobIds */
689       }
690       last_JobId = JobId;
691       bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
692       items++;
693       /*
694        * Find files for this JobId and insert them in the tree
695        */
696       Mmsg(&rx->query, uar_sel_files, JobId);
697       if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
698          bsendmsg(ua, "%s", db_strerror(ua->db));
699       }
700       /*
701        * Find the MediaTypes for this JobId and add to the name_list
702        */
703       Mmsg(&rx->query, uar_mediatype, JobId);
704       if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
705          bsendmsg(ua, "%s", db_strerror(ua->db));
706       }
707    }
708    bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n", 
709       items, items==1?"":"s");
710
711    /* Check MediaType and select storage that corresponds */
712    get_storage_from_mediatype(ua, &rx->name_list, rx);
713
714    if (find_arg(ua, _("all")) < 0) {
715       /* Let the user interact in selecting which files to restore */
716       OK = user_select_files_from_tree(&tree);
717    }
718
719    /*
720     * Walk down through the tree finding all files marked to be 
721     *  extracted making a bootstrap file.
722     */
723    if (OK) {
724       for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
725          Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
726          if (node->extract || node->extract_dir) {
727             Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
728             add_findex(rx->bsr, node->JobId, node->FileIndex);
729             rx->selected_files++;
730          }
731       }
732    }
733
734    free_tree(tree.root);              /* free the directory tree */
735    return OK;
736 }
737
738
739 /*
740  * This routine is used to get the current backup or a backup
741  *   before the specified date.
742  */
743 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
744 {
745    int stat = 0;
746    FILESET_DBR fsr;
747    CLIENT_DBR cr;
748    char fileset_name[MAX_NAME_LENGTH];
749    char ed1[50];
750    char pool_select[MAX_NAME_LENGTH];
751    int i;
752
753
754    /* Create temp tables */
755    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
756    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
757    if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
758       bsendmsg(ua, "%s\n", db_strerror(ua->db));
759    }
760    if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
761       bsendmsg(ua, "%s\n", db_strerror(ua->db));
762    }
763    /*
764     * Select Client from the Catalog
765     */
766    memset(&cr, 0, sizeof(cr));
767    if (!get_client_dbr(ua, &cr)) {
768       goto bail_out;
769    }
770    bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
771
772    /*
773     * Get FileSet 
774     */
775    memset(&fsr, 0, sizeof(fsr));
776    i = find_arg_with_value(ua, "FileSet"); 
777    if (i >= 0) {
778       bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
779       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
780          bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet, 
781             db_strerror(ua->db));
782          i = -1;
783       }
784    }
785    if (i < 0) {                       /* fileset not found */
786       Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
787       start_prompt(ua, _("The defined FileSet resources are:\n"));
788       if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
789          bsendmsg(ua, "%s\n", db_strerror(ua->db));
790       }
791       if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), 
792                  fileset_name, sizeof(fileset_name)) < 0) {
793          goto bail_out;
794       }
795
796       bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
797       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
798          bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
799          bsendmsg(ua, _("This probably means you modified the FileSet.\n"
800                      "Continuing anyway.\n"));
801       }
802    }
803
804    /* If Pool specified, add PoolId specification */
805    pool_select[0] = 0;
806    if (rx->pool) {
807       POOL_DBR pr;
808       memset(&pr, 0, sizeof(pr));
809       bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
810       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
811          bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%u ", pr.PoolId);
812       } else {
813          bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
814       }
815    }
816
817    /* Find JobId of last Full backup for this client, fileset */
818    Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
819          pool_select);
820    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
821       bsendmsg(ua, "%s\n", db_strerror(ua->db));
822       goto bail_out;
823    }
824
825    /* Find all Volumes used by that JobId */
826    if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
827       bsendmsg(ua, "%s\n", db_strerror(ua->db));
828       goto bail_out;
829    }
830    /* Note, this is needed because I don't seem to get the callback
831     * from the call just above.
832     */
833    rx->JobTDate = 0;
834    if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
835       bsendmsg(ua, "%s\n", db_strerror(ua->db));
836    }
837    if (rx->JobTDate == 0) {
838       bsendmsg(ua, _("No Full backup before %s found.\n"), date);
839       goto bail_out;
840    }
841
842    /* Now find all Incremental/Decremental Jobs after Full save */
843    Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
844         cr.ClientId, fsr.FileSet, pool_select);
845    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
846       bsendmsg(ua, "%s\n", db_strerror(ua->db));
847    }
848
849    /* Get the JobIds from that list */
850    rx->JobIds[0] = 0;
851    rx->last_jobid[0] = 0;
852    if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
853       bsendmsg(ua, "%s\n", db_strerror(ua->db));
854    }
855
856    if (rx->JobIds[0] != 0) {
857       /* Display a list of Jobs selected for this restore */
858       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
859    } else {
860       bsendmsg(ua, _("No jobs found.\n")); 
861    }
862
863    stat = 1;
864  
865 bail_out:
866    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
867    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
868    return stat;
869 }
870
871
872 /* Return next JobId from comma separated list */
873 static int next_jobid_from_list(char **p, uint32_t *JobId)
874 {
875    char jobid[30];
876    char *q = *p;
877
878    jobid[0] = 0;
879    for (int i=0; i<(int)sizeof(jobid); i++) {
880       if (*q == ',' || *q == 0) {
881          q++;
882          break;
883       }
884       jobid[i] = *q++;
885       jobid[i+1] = 0;
886    }
887    if (jobid[0] == 0 || !is_a_number(jobid)) {
888       return 0;
889    }
890    *p = q;
891    *JobId = strtoul(jobid, NULL, 10);
892    return 1;
893 }
894
895 /*
896  * Callback handler to get JobId and FileIndex for files
897  */
898 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
899 {
900    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
901    rx->JobId = atoi(row[0]);
902    add_findex(rx->bsr, rx->JobId, atoi(row[1]));
903    rx->found = true;
904    return 0;
905 }
906
907 /*
908  * Callback handler make list of JobIds
909  */
910 static int jobid_handler(void *ctx, int num_fields, char **row)
911 {
912    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
913
914    if (strcmp(rx->last_jobid, row[0]) == 0) {           
915       return 0;                       /* duplicate id */
916    }
917    bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
918    if (rx->JobIds[0] != 0) {
919       pm_strcat(&rx->JobIds, ",");
920    }
921    pm_strcat(&rx->JobIds, row[0]);
922    return 0;
923 }
924
925
926 /*
927  * Callback handler to pickup last Full backup JobTDate
928  */
929 static int last_full_handler(void *ctx, int num_fields, char **row)
930 {
931    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
932
933    rx->JobTDate = str_to_int64(row[1]); 
934    return 0;
935 }
936
937 /*
938  * Callback handler build FileSet name prompt list
939  */
940 static int fileset_handler(void *ctx, int num_fields, char **row)
941 {
942    /* row[0] = FileSet (name) */
943    if (row[0]) {
944       add_prompt((UAContext *)ctx, row[0]);
945    }
946    return 0;
947 }
948
949 /*
950  * Called here with each name to be added to the list. The name is
951  *   added to the list if it is not already in the list.
952  *
953  * Used to make unique list of FileSets and MediaTypes
954  */
955 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
956 {
957    NAME_LIST *name = (NAME_LIST *)ctx;
958
959    if (name->num_ids == MAX_ID_LIST_LEN) {  
960       return 1;
961    }
962    if (name->num_ids == name->max_ids) {
963       if (name->max_ids == 0) {
964          name->max_ids = 1000;
965          name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
966       } else {
967          name->max_ids = (name->max_ids * 3) / 2;
968          name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
969       }
970    }
971    for (int i=0; i<name->num_ids; i++) {
972       if (strcmp(name->name[i], row[0]) == 0) {
973          return 0;                    /* already in list, return */
974       }
975    }
976    /* Add new name to list */
977    name->name[name->num_ids++] = bstrdup(row[0]);
978    return 0;
979 }
980
981
982 /*
983  * Print names in the list
984  */
985 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
986
987    for (int i=0; i < name_list->num_ids; i++) {
988       bsendmsg(ua, "%s\n", name_list->name[i]);
989    }
990 }
991
992
993 /*
994  * Free names in the list
995  */
996 static void free_name_list(NAME_LIST *name_list)
997
998    for (int i=0; i < name_list->num_ids; i++) {
999       free(name_list->name[i]);
1000    }
1001    if (name_list->name) {
1002       free(name_list->name);
1003       name_list->name = NULL;
1004    }
1005    name_list->max_ids = 0;
1006    name_list->num_ids = 0;
1007 }
1008
1009 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1010 {
1011    if (name_list->num_ids > 1) {
1012       bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1013          "Restore is not possible. The MediaTypes used are:\n"));
1014       print_name_list(ua, name_list);
1015       rx->store = select_storage_resource(ua);
1016       return;
1017    }
1018
1019    if (name_list->num_ids == 0) {
1020       bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1021       rx->store = select_storage_resource(ua);
1022       return;
1023    }
1024
1025    rx->store = get_storage_resource(ua, false /* don't use default */);
1026
1027    if (!rx->store) {
1028       bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1029          "MediaType %s, needed by the Jobs you selected.\n"
1030          "You will be allowed to select a Storage device later.\n"),
1031          name_list->name[0]); 
1032    }
1033 }