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