]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/restore.c
1cae3457ca1cec4462ac4ae2017d8abeb077df6a
[bacula/bacula] / bacula / src / dird / restore.c
1 /*
2  *
3  *   Bacula Director -- restore.c -- responsible for restoring files
4  *
5  *     Kern Sibbald, November MM
6  *
7  *    This routine is run as a separate thread.  There may be more
8  *    work to be done to make it totally reentrant!!!!
9  * 
10  * Current implementation is Catalog verification only (i.e. no
11  *  verification versus tape).
12  *
13  *  Basic tasks done here:
14  *     Open DB
15  *     Open Message Channel with Storage daemon to tell him a job will be starting.
16  *     Open connection with File daemon and pass him commands
17  *       to do the restore.
18  *     Update the DB according to what files where restored????
19  *
20  *   Version $Id$
21  */
22
23 /*
24    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
25
26    This program is free software; you can redistribute it and/or
27    modify it under the terms of the GNU General Public License as
28    published by the Free Software Foundation; either version 2 of
29    the License, or (at your option) any later version.
30
31    This program is distributed in the hope that it will be useful,
32    but WITHOUT ANY WARRANTY; without even the implied warranty of
33    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34    General Public License for more details.
35
36    You should have received a copy of the GNU General Public
37    License along with this program; if not, write to the Free
38    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
39    MA 02111-1307, USA.
40
41  */
42
43 #include "bacula.h"
44 #include "dird.h"
45
46 /* Commands sent to File daemon */
47 static char restorecmd[]   = "restore where=%s\n";
48 static char storaddr[]     = "storage address=%s port=%d\n";
49 static char sessioncmd[]   = "session %s %ld %ld %ld %ld %ld %ld\n";  
50
51 /* Responses received from File daemon */
52 static char OKrestore[]   = "2000 OK restore\n";
53 static char OKstore[]     = "2000 OK storage\n";
54 static char OKsession[]   = "2000 OK session\n";
55 static char OKbootstrap[] = "2000 OK bootstrap\n";
56 static char EndJob[]      = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n";
57
58 /* Forward referenced functions */
59 static void restore_cleanup(JCR *jcr, int status);
60 static int send_bootstrap_file(JCR *jcr);
61
62 /* External functions */
63
64 /* 
65  * Do a restore of the specified files
66  *    
67  *  Returns:  0 on failure
68  *            1 on success
69  */
70 int do_restore(JCR *jcr) 
71 {
72    char dt[MAX_TIME_LENGTH];
73    BSOCK   *fd;
74    JOB_DBR rjr;                       /* restore job record */
75    int ok = FALSE;
76
77
78    if (!get_or_create_client_record(jcr)) {
79       restore_cleanup(jcr, JS_ErrorTerminated);
80       return 0;
81    }
82
83    memset(&rjr, 0, sizeof(rjr));
84    jcr->jr.Level = 'F';            /* Full restore */
85    jcr->jr.StartTime = jcr->start_time;
86    if (!db_update_job_start_record(jcr->db, &jcr->jr)) {
87       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
88       restore_cleanup(jcr, JS_ErrorTerminated);
89       return 0;
90    }
91    Dmsg0(20, "Updated job start record\n");
92    jcr->fname = (char *) get_pool_memory(PM_FNAME);
93
94    Dmsg1(20, "RestoreJobId=%d\n", jcr->job->RestoreJobId);
95
96    /* 
97     * The following code is kept temporarily for compatibility.
98     * It is the predecessor to the Bootstrap file.
99     */
100    if (!jcr->RestoreBootstrap) {
101       /*
102        * Find Job Record for Files to be restored
103        */
104       if (jcr->RestoreJobId != 0) {
105          rjr.JobId = jcr->RestoreJobId;     /* specified by UA */
106       } else {
107          rjr.JobId = jcr->job->RestoreJobId; /* specified by Job Resource */
108       }
109       if (!db_get_job_record(jcr->db, &rjr)) {
110          Jmsg2(jcr, M_FATAL, 0, _("Cannot get job record id=%d %s"), rjr.JobId,
111             db_strerror(jcr->db));
112          restore_cleanup(jcr, JS_ErrorTerminated);
113          return 0;
114       }
115
116       /*
117        * Now find the Volumes we will need for the Restore
118        */
119       jcr->VolumeName[0] = 0;
120       if (!db_get_job_volume_names(jcr->db, rjr.JobId, &jcr->VolumeName) ||
121            jcr->VolumeName[0] == 0) {
122          Jmsg(jcr, M_FATAL, 0, _("Cannot find Volume Name for restore Job %d. %s"), 
123             rjr.JobId, db_strerror(jcr->db));
124          restore_cleanup(jcr, JS_ErrorTerminated);
125          return 0;
126       }
127       Dmsg1(20, "Got job Volume Names: %s\n", jcr->VolumeName);
128    }
129       
130
131    /* Print Job Start message */
132    bstrftime(dt, sizeof(dt), jcr->start_time);
133    Jmsg(jcr, M_INFO, 0, _("%s Start Restore Job %s Name=%s, Client=%s, FileSet=%s\n"), 
134       dt, jcr->Job, jcr->job->hdr.name, jcr->client->hdr.name, 
135       jcr->fileset->hdr.name);
136
137    /*
138     * Open a message channel connection with the Storage
139     * daemon. This is to let him know that our client
140     * will be contacting him for a backup  session.
141     *
142     */
143    Dmsg0(10, "Open connection with storage daemon\n");
144    jcr->JobStatus = JS_Blocked;
145    /*
146     * Start conversation with Storage daemon  
147     */
148    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
149       restore_cleanup(jcr, JS_ErrorTerminated);
150       return 0;
151    }
152    /*
153     * Now start a job with the Storage daemon
154     */
155    if (!start_storage_daemon_job(jcr)) {
156       restore_cleanup(jcr, JS_ErrorTerminated);
157       return 0;
158    }
159    /*
160     * Now start a Storage daemon message thread
161     */
162    if (!start_storage_daemon_message_thread(jcr)) {
163       restore_cleanup(jcr, JS_ErrorTerminated);
164       return 0;
165    }
166    Dmsg0(50, "Storage daemon connection OK\n");
167
168    /* 
169     * Start conversation with File daemon  
170     */
171    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
172       restore_cleanup(jcr, JS_ErrorTerminated);
173       return 0;
174    }
175
176    fd = jcr->file_bsock;
177    jcr->JobStatus = JS_Running;
178
179    if (!send_include_list(jcr)) {
180       restore_cleanup(jcr, JS_ErrorTerminated);
181       return 0;
182    }
183
184    if (!send_exclude_list(jcr)) {
185       restore_cleanup(jcr, JS_ErrorTerminated);
186       return 0;
187    }
188
189
190    /* 
191     * send Storage daemon address to the File daemon,
192     *   then wait for File daemon to make connection
193     *   with Storage daemon.
194     */
195    jcr->JobStatus = JS_Blocked;
196    if (jcr->store->SDDport == 0) {
197       jcr->store->SDDport = jcr->store->SDport;
198    }
199    bnet_fsend(fd, storaddr, jcr->store->address, jcr->store->SDDport);
200    Dmsg1(6, "dird>filed: %s\n", fd->msg);
201    if (!response(fd, OKstore, "Storage")) {
202       restore_cleanup(jcr, JS_ErrorTerminated);
203       return 0;
204    }
205    jcr->JobStatus = JS_Running;
206
207    /* 
208     * Send the bootstrap file -- what Volumes/files to restore
209     */
210    if (!send_bootstrap_file(jcr)) {
211       restore_cleanup(jcr, JS_ErrorTerminated);
212       return 0;
213    }
214
215    /* 
216     * The following code is deprecated   
217     */
218    if (!jcr->RestoreBootstrap) {
219       /*
220        * Pass the VolSessionId, VolSessionTime, Start and
221        * end File and Blocks on the session command.
222        */
223       bnet_fsend(fd, sessioncmd, 
224                 jcr->VolumeName,
225                 rjr.VolSessionId, rjr.VolSessionTime, 
226                 rjr.StartFile, rjr.EndFile, rjr.StartBlock, 
227                 rjr.EndBlock);
228       if (!response(fd, OKsession, "Session")) {
229          restore_cleanup(jcr, JS_ErrorTerminated);
230          return 0;
231       }
232    }
233
234    /* Send restore command */
235    if (jcr->RestoreWhere) {
236       bnet_fsend(fd, restorecmd, jcr->RestoreWhere);
237    } else {
238       bnet_fsend(fd, restorecmd, 
239                  jcr->job->RestoreWhere ? jcr->job->RestoreWhere : "");
240    }
241    if (!response(fd, OKrestore, "Restore")) {
242       restore_cleanup(jcr, JS_ErrorTerminated);
243       return 0;
244    }
245
246    /* Wait for Job Termination */
247    /*** ****FIXME**** get job termination status */
248    Dmsg0(20, "wait for job termination\n");
249    while (bget_msg(fd, 0) >  0) {
250       Dmsg1(100, "dird<filed: %s\n", fd->msg);
251       if (sscanf(fd->msg, EndJob, &jcr->JobStatus, &jcr->JobFiles,
252           &jcr->JobBytes) == 3) {
253          ok = TRUE;
254       }
255    }
256
257    restore_cleanup(jcr, ok?jcr->JobStatus:JS_ErrorTerminated);
258
259    return 1;
260 }
261
262 /*
263  * Release resources allocated during restore.
264  *
265  */
266 static void restore_cleanup(JCR *jcr, int TermCode)
267 {
268    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
269    char ec1[30], ec2[30];
270    char term_code[100];
271    char *term_msg;
272    int msg_type;
273    double kbps;
274
275    Dmsg0(20, "In restore_cleanup\n");
276    jcr->JobStatus = TermCode;
277
278    update_job_end_record(jcr);
279
280    msg_type = M_INFO;                 /* by default INFO message */
281    switch (TermCode) {
282       case JS_Terminated:
283          term_msg = _("Restore OK");
284          break;
285       case JS_FatalError:
286       case JS_ErrorTerminated:
287          term_msg = _("*** Restore Error ***"); 
288          msg_type = M_ERROR;          /* Generate error message */
289          if (jcr->store_bsock) {
290             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
291             pthread_cancel(jcr->SD_msg_chan);
292          }
293          break;
294       case JS_Cancelled:
295          term_msg = _("Restore Cancelled");
296          if (jcr->store_bsock) {
297             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
298             pthread_cancel(jcr->SD_msg_chan);
299          }
300          break;
301       default:
302          term_msg = term_code;
303          sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode);
304          break;
305    }
306    bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
307    bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
308    kbps = (double)jcr->jr.JobBytes / (1000 * (jcr->jr.EndTime - jcr->jr.StartTime));
309
310    Jmsg(jcr, msg_type, 0, _("%s\n\
311 JobId:                  %d\n\
312 Job:                    %s\n\
313 Client:                 %s\n\
314 Start time:             %s\n\
315 End time:               %s\n\
316 Files Restored:         %s\n\
317 Bytes Restored:         %s\n\
318 Rate:                   %.1f KB/s\n\
319 Termination:            %s\n\n"),
320         edt,
321         jcr->jr.JobId,
322         jcr->jr.Job,
323         jcr->client->hdr.name,
324         sdt,
325         edt,
326         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
327         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
328         (float)kbps,
329         term_msg);
330
331    Dmsg0(20, "Leaving restore_cleanup\n");
332 }
333
334 static int send_bootstrap_file(JCR *jcr)
335 {
336    FILE *bs;
337    char buf[1000];
338    BSOCK *fd = jcr->file_bsock;
339    char *bootstrap = "bootstrap\n";
340
341    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
342    if (!jcr->RestoreBootstrap) {
343       return 1;
344    }
345    bs = fopen(jcr->RestoreBootstrap, "r");
346    if (!bs) {
347       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"), 
348          jcr->RestoreBootstrap, strerror(errno));
349       jcr->JobStatus = JS_ErrorTerminated;
350       return 0;
351    }
352    strcpy(fd->msg, bootstrap);  
353    fd->msglen = strlen(fd->msg);
354    bnet_send(fd);
355    while (fgets(buf, sizeof(buf), bs)) {
356       fd->msglen = Mmsg(&fd->msg, "%s", buf);
357       bnet_send(fd);       
358    }
359    bnet_sig(fd, BNET_EOD);
360    fclose(bs);
361    if (!response(fd, OKbootstrap, "Bootstrap")) {
362       jcr->JobStatus = JS_ErrorTerminated;
363       return 0;
364    }
365    return 1;
366 }