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