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