]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_restore.c
7fae1160ab95da16c44a46ffe3fb679ce498a469
[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-2003 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 runcmd(UAContext *ua, char *cmd);
42
43 /* Imported variables */
44 extern char *uar_list_jobs,     *uar_file,        *uar_sel_files;
45 extern char *uar_del_temp,      *uar_del_temp1,   *uar_create_temp;
46 extern char *uar_create_temp1,  *uar_last_full,   *uar_full;
47 extern char *uar_inc,           *uar_list_temp,   *uar_sel_jobid_temp;
48 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
49
50
51 /* Main structure for obtaining JobIds */
52 struct JOBIDS {
53    utime_t JobTDate;
54    uint32_t TotalFiles;
55    char ClientName[MAX_NAME_LENGTH];
56    char JobIds[200];                  /* User entered string of JobIds */
57    STORE  *store;
58 };
59
60 struct NAME_LIST {
61    char **name;                       /* list of names */
62    int num_ids;                       /* ids stored */
63    int max_ids;                       /* size of array */
64    int num_del;                       /* number deleted */
65    int tot_ids;                       /* total to process */
66 };
67
68 #define MAX_ID_LIST_LEN 1000000
69
70
71 /* Forward referenced functions */
72 static int last_full_handler(void *ctx, int num_fields, char **row);
73 static int jobid_handler(void *ctx, int num_fields, char **row);
74 static int next_jobid_from_list(char **p, uint32_t *JobId);
75 static int user_select_jobids(UAContext *ua, JOBIDS *ji);
76 static int fileset_handler(void *ctx, int num_fields, char **row);
77 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
78 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
79 static void free_name_list(NAME_LIST *name_list);
80 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBIDS *ji);
81
82
83 /*
84  *   Restore files
85  *
86  */
87 int restorecmd(UAContext *ua, char *cmd)
88 {
89    POOLMEM *query;
90    TREE_CTX tree;
91    JobId_t JobId, last_JobId;
92    char *p;
93    RBSR *bsr;
94    char *nofname = "";
95    JOBIDS ji;
96    JOB *job = NULL;
97    JOB *restore_job = NULL;
98    int restore_jobs = 0;
99    NAME_LIST name_list;
100    uint32_t selected_files = 0;
101    char *where = NULL;
102    int i;
103
104    i = find_arg_with_value(ua, "where");
105    if (i >= 0) {
106       where = ua->argv[i];
107    }
108
109    if (!open_db(ua)) {
110       return 0;
111    }
112
113    memset(&tree, 0, sizeof(TREE_CTX));
114    memset(&name_list, 0, sizeof(name_list));
115    memset(&ji, 0, sizeof(ji));
116
117    /* Ensure there is at least one Restore Job */
118    LockRes();
119    while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
120       if (job->JobType == JT_RESTORE) {
121          if (!restore_job) {
122             restore_job = job;
123          }
124          restore_jobs++;
125       }
126    }
127    UnlockRes();
128    if (!restore_jobs) {
129       bsendmsg(ua, _(
130          "No Restore Job Resource found. You must create at least\n"
131          "one before running this command.\n"));
132       return 0;
133    }
134
135    /* 
136     * Request user to select JobIds by various different methods
137     *  last 20 jobs, where File saved, most recent backup, ...
138     */
139    if (!user_select_jobids(ua, &ji)) {
140       return 0;
141    }
142
143    /* 
144     * Build the directory tree containing JobIds user selected
145     */
146    tree.root = new_tree(ji.TotalFiles);
147    tree.root->fname = nofname;
148    tree.ua = ua;
149    query = get_pool_memory(PM_MESSAGE);
150    last_JobId = 0;
151    /*
152     * For display purposes, the same JobId, with different volumes may
153     * appear more than once, however, we only insert it once.
154     */
155    int items = 0;
156    for (p=ji.JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
157
158       if (JobId == last_JobId) {             
159          continue;                    /* eliminate duplicate JobIds */
160       }
161       last_JobId = JobId;
162       bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
163       items++;
164       /*
165        * Find files for this JobId and insert them in the tree
166        */
167       Mmsg(&query, uar_sel_files, JobId);
168       if (!db_sql_query(ua->db, query, insert_tree_handler, (void *)&tree)) {
169          bsendmsg(ua, "%s", db_strerror(ua->db));
170       }
171       /*
172        * Find the FileSets for this JobId and add to the name_list
173        */
174       Mmsg(&query, uar_mediatype, JobId);
175       if (!db_sql_query(ua->db, query, unique_name_list_handler, (void *)&name_list)) {
176          bsendmsg(ua, "%s", db_strerror(ua->db));
177       }
178
179    }
180    bsendmsg(ua, "%d item%s inserted into the tree and marked for extraction.\n", 
181       items, items==1?"":"s");
182    free_pool_memory(query);
183
184    /* Check MediaType and select storage that corresponds */
185    get_storage_from_mediatype(ua, &name_list, &ji);
186    free_name_list(&name_list);
187
188    /* Let the user select which files to restore */
189    user_select_files_from_tree(&tree);
190
191    /*
192     * Walk down through the tree finding all files marked to be 
193     *  extracted making a bootstrap file.
194     */
195    bsr = new_bsr();
196    for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
197       Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
198       if (node->extract) {
199          Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
200          add_findex(bsr, node->JobId, node->FileIndex);
201          selected_files++;
202       }
203    }
204
205    free_tree(tree.root);              /* free the directory tree */
206
207    if (bsr->JobId) {
208       if (!complete_bsr(ua, bsr)) {   /* find Vol, SessId, SessTime from JobIds */
209          bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
210          free_bsr(bsr);
211          return 0;
212       }
213 //    print_bsr(ua, bsr);
214       write_bsr_file(ua, bsr);
215       bsendmsg(ua, _("\n%u files selected to restore.\n\n"), selected_files);
216    } else {
217       bsendmsg(ua, _("No files selected to restore.\n"));
218    }
219    free_bsr(bsr);
220
221    if (restore_jobs == 1) {
222       job = restore_job;
223    } else {
224       job = select_restore_job_resource(ua);
225    }
226    if (!job) {
227       bsendmsg(ua, _("No Restore Job resource found!\n"));
228       return 0;
229    }
230
231    /* If no client name specified yet, get it now */
232    if (!ji.ClientName[0]) {
233       CLIENT_DBR cr;
234       memset(&cr, 0, sizeof(cr));
235       if (!get_client_dbr(ua, &cr)) {
236          return 0;
237       }
238       bstrncpy(ji.ClientName, cr.Name, sizeof(ji.ClientName));
239    }
240
241    /* Build run command */
242    if (where) {
243       Mmsg(&ua->cmd, 
244           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
245           " where=\"%s\"",
246           job->hdr.name, ji.ClientName, ji.store?ji.store->hdr.name:"",
247           working_directory, where);
248    } else {
249       Mmsg(&ua->cmd, 
250           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
251           job->hdr.name, ji.ClientName, ji.store?ji.store->hdr.name:"",
252           working_directory);
253    }
254    Dmsg1(400, "Submitting: %s\n", ua->cmd);
255    
256    parse_ua_args(ua);
257    runcmd(ua, ua->cmd);
258
259    bsendmsg(ua, _("Restore command done.\n"));
260    return 1;
261 }
262
263 /*
264  * The first step in the restore process is for the user to 
265  *  select a list of JobIds from which he will subsequently
266  *  select which files are to be restored.
267  */
268 static int user_select_jobids(UAContext *ua, JOBIDS *ji)
269 {
270    char fileset_name[MAX_NAME_LENGTH];
271    char *p, ed1[50];
272    FILESET_DBR fsr;
273    CLIENT_DBR cr;
274    JobId_t JobId;
275    JOB_DBR jr;
276    POOLMEM *query;
277    int done = 0;
278    char *list[] = { 
279       "List last 20 Jobs run",
280       "List Jobs where a given File is saved",
281       "Enter list of JobIds to select",
282       "Enter SQL list command", 
283       "Select the most recent backup for a client",
284       "Cancel",
285       NULL };
286
287    bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
288                   "to be restored. You will be presented several methods\n"
289                   "of specifying the JobIds. Then you will be allowed to\n"
290                   "select which files from those JobIds are to be restored.\n\n"));
291
292    for ( ; !done; ) {
293       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
294       for (int i=0; list[i]; i++) {
295          add_prompt(ua, list[i]);
296       }
297       done = 1;
298       switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
299       case -1:                        /* error */
300          return 0;
301       case 0:                         /* list last 20 Jobs run */
302          db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
303          done = 0;
304          break;
305       case 1:                         /* list where a file is saved */
306          char *fname;
307          int len;
308          if (!get_cmd(ua, _("Enter Filename: "))) {
309             return 0;
310          }
311          len = strlen(ua->cmd);
312          fname = (char *)malloc(len * 2 + 1);
313          db_escape_string(fname, ua->cmd, len);
314          query = get_pool_memory(PM_MESSAGE);
315          Mmsg(&query, uar_file, fname);
316          free(fname);
317          db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
318          free_pool_memory(query);
319          done = 0;
320          break;
321       case 2:                         /* enter a list of JobIds */
322          if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
323             return 0;
324          }
325          bstrncpy(ji->JobIds, ua->cmd, sizeof(ji->JobIds));
326          break;
327       case 3:                         /* Enter an SQL list command */
328          if (!get_cmd(ua, _("Enter SQL list command: "))) {
329             return 0;
330          }
331          db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
332          done = 0;
333          break;
334       case 4:                         /* Select the most recent backups */
335          query = get_pool_memory(PM_MESSAGE);
336          db_sql_query(ua->db, uar_del_temp, NULL, NULL);
337          db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
338          if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
339             bsendmsg(ua, "%s\n", db_strerror(ua->db));
340          }
341          if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
342             bsendmsg(ua, "%s\n", db_strerror(ua->db));
343          }
344          /*
345           * Select Client from the Catalog
346           */
347          memset(&cr, 0, sizeof(cr));
348          if (!get_client_dbr(ua, &cr)) {
349             free_pool_memory(query);
350             db_sql_query(ua->db, uar_del_temp, NULL, NULL);
351             db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
352             return 0;
353          }
354          bstrncpy(ji->ClientName, cr.Name, sizeof(ji->ClientName));
355
356          /*
357           * Select FileSet 
358           */
359          Mmsg(&query, uar_sel_fileset, cr.ClientId, cr.ClientId);
360          start_prompt(ua, _("The defined FileSet resources are:\n"));
361          if (!db_sql_query(ua->db, query, fileset_handler, (void *)ua)) {
362             bsendmsg(ua, "%s\n", db_strerror(ua->db));
363          }
364          if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), 
365                        fileset_name, sizeof(fileset_name)) < 0) {
366             free_pool_memory(query);
367             db_sql_query(ua->db, uar_del_temp, NULL, NULL);
368             db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
369             return 0;
370          }
371          fsr.FileSetId = atoi(fileset_name);  /* Id is first part of name */
372          if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
373             bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
374             bsendmsg(ua, _("This probably means you modified the FileSet.\n"
375                            "Continuing anyway.\n"));
376          }
377
378          /* Find JobId of last Full backup for this client, fileset */
379          Mmsg(&query, uar_last_full, cr.ClientId, cr.ClientId, fsr.FileSetId);
380          if (!db_sql_query(ua->db, query, NULL, NULL)) {
381             bsendmsg(ua, "%s\n", db_strerror(ua->db));
382          }
383          /* Find all Volumes used by that JobId */
384          if (!db_sql_query(ua->db, uar_full, NULL,NULL)) {
385             bsendmsg(ua, "%s\n", db_strerror(ua->db));
386          }
387          /* Note, this is needed as I don't seem to get the callback
388           * from the call just above.
389           */
390          if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)ji)) {
391             bsendmsg(ua, "%s\n", db_strerror(ua->db));
392          }
393          /* Now find all Incremental Jobs */
394          Mmsg(&query, uar_inc, edit_uint64(ji->JobTDate, ed1), cr.ClientId, fsr.FileSetId);
395          if (!db_sql_query(ua->db, query, NULL, NULL)) {
396             bsendmsg(ua, "%s\n", db_strerror(ua->db));
397          }
398          free_pool_memory(query);
399          db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
400
401          if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)ji)) {
402             bsendmsg(ua, "%s\n", db_strerror(ua->db));
403          }
404          db_sql_query(ua->db, uar_del_temp, NULL, NULL);
405          db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
406          break;
407       case 5:
408          return 0;
409       }
410    }
411
412    if (*ji->JobIds == 0) {
413       bsendmsg(ua, _("No Jobs selected.\n"));
414       return 0;
415    }
416    bsendmsg(ua, _("You have selected the following JobId: %s\n"), ji->JobIds);
417
418    memset(&jr, 0, sizeof(JOB_DBR));
419
420    ji->TotalFiles = 0;
421    for (p=ji->JobIds; ; ) {
422       int stat = next_jobid_from_list(&p, &JobId);
423       if (stat < 0) {
424          bsendmsg(ua, _("Invalid JobId in list.\n"));
425          return 0;
426       }
427       if (stat == 0) {
428          break;
429       }
430       jr.JobId = JobId;
431       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
432          bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
433          return 0;
434       }
435       ji->TotalFiles += jr.JobFiles;
436    }
437    return 1;
438 }
439
440 static int next_jobid_from_list(char **p, uint32_t *JobId)
441 {
442    char jobid[30];
443    int i;
444    char *q = *p;
445
446    jobid[0] = 0;
447    for (i=0; i<(int)sizeof(jobid); i++) {
448       if (*q == ',' || *q == 0) {
449          q++;
450          break;
451       }
452       jobid[i] = *q++;
453       jobid[i+1] = 0;
454    }
455    if (jobid[0] == 0 || !is_a_number(jobid)) {
456       return 0;
457    }
458    *p = q;
459    *JobId = strtoul(jobid, NULL, 10);
460    return 1;
461 }
462
463 /*
464  * Callback handler make list of JobIds
465  */
466 static int jobid_handler(void *ctx, int num_fields, char **row)
467 {
468    JOBIDS *ji = (JOBIDS *)ctx;
469
470    if (strlen(ji->JobIds)+strlen(row[0])+2 < sizeof(ji->JobIds)) {
471       if (ji->JobIds[0] != 0) {
472          strcat(ji->JobIds, ",");
473       }
474       strcat(ji->JobIds, row[0]);
475    }
476
477    return 0;
478 }
479
480
481 /*
482  * Callback handler to pickup last Full backup JobId and ClientId
483  */
484 static int last_full_handler(void *ctx, int num_fields, char **row)
485 {
486    JOBIDS *ji = (JOBIDS *)ctx;
487
488    ji->JobTDate = strtoll(row[1], NULL, 10);
489
490    return 0;
491 }
492
493 /*
494  * Callback handler build fileset prompt list
495  */
496 static int fileset_handler(void *ctx, int num_fields, char **row)
497 {
498    char prompt[MAX_NAME_LENGTH+200];
499
500    snprintf(prompt, sizeof(prompt), "%s  %s  %s", row[0], row[1], row[2]);
501    add_prompt((UAContext *)ctx, prompt);
502    return 0;
503 }
504
505 /*
506  * Called here with each name to be added to the list. The name is
507  *   added to the list if it is not already in the list.
508  *
509  * Used to make unique list of FileSets and MediaTypes
510  */
511 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
512 {
513    NAME_LIST *name = (NAME_LIST *)ctx;
514
515    if (name->num_ids == MAX_ID_LIST_LEN) {  
516       return 1;
517    }
518    if (name->num_ids == name->max_ids) {
519       if (name->max_ids == 0) {
520          name->max_ids = 1000;
521          name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
522       } else {
523          name->max_ids = (name->max_ids * 3) / 2;
524          name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
525       }
526    }
527    for (int i=0; i<name->num_ids; i++) {
528       if (strcmp(name->name[i], row[0]) == 0) {
529          return 0;                    /* already in list, return */
530       }
531    }
532    /* Add new name to list */
533    name->name[name->num_ids++] = bstrdup(row[0]);
534    return 0;
535 }
536
537
538 /*
539  * Print names in the list
540  */
541 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
542
543    int i;
544
545    for (i=0; i < name_list->num_ids; i++) {
546       bsendmsg(ua, "%s\n", name_list->name[i]);
547    }
548 }
549
550
551 /*
552  * Free names in the list
553  */
554 static void free_name_list(NAME_LIST *name_list)
555
556    int i;
557
558    for (i=0; i < name_list->num_ids; i++) {
559       free(name_list->name[i]);
560    }
561    if (name_list->name) {
562       free(name_list->name);
563    }
564    name_list->max_ids = 0;
565    name_list->num_ids = 0;
566 }
567
568 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBIDS *ji)
569 {
570    char name[MAX_NAME_LENGTH];
571    STORE *store = NULL;
572
573    if (name_list->num_ids > 1) {
574       bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
575          "Restore is not possible. The MediaTypes used are:\n"));
576       print_name_list(ua, name_list);
577       ji->store = select_storage_resource(ua);
578       return;
579    }
580
581    if (name_list->num_ids == 0) {
582       bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
583       ji->store = select_storage_resource(ua);
584       return;
585    }
586
587    start_prompt(ua, _("The defined Storage resources are:\n"));
588    LockRes();
589    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
590       if (strcmp(store->media_type, name_list->name[0]) == 0) {
591          add_prompt(ua, store->hdr.name);
592       }
593    }
594    UnlockRes();
595    do_prompt(ua, _("Storage"),  _("Select Storage resource"), name, sizeof(name));
596    ji->store = (STORE *)GetResWithName(R_STORAGE, name);
597    if (!ji->store) {
598       bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
599          "MediaType %s, needed by the Jobs you selected.\n"
600          "You will be allowed to select a Storage device later.\n"),
601          name_list->name[0]); 
602    }
603 }