3 * Bacula Director -- restore.c -- responsible for restoring files
5 * Kern Sibbald, November MM
7 * This routine is run as a separate thread.
9 * Current implementation is Catalog verification only (i.e. no
10 * verification versus tape).
12 * Basic tasks done here:
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
17 * Update the DB according to what files where restored????
23 Copyright (C) 2000-2003 Kern Sibbald and John Walker
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.
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.
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,
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";
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 static char EndRestore[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld " Errors=%u\n";
57 /* Forward referenced functions */
58 static void restore_cleanup(JCR *jcr, int status);
59 static int send_bootstrap_file(JCR *jcr);
61 /* External functions */
64 * Do a restore of the specified files
66 * Returns: 0 on failure
69 int do_restore(JCR *jcr)
72 JOB_DBR rjr; /* restore job record */
76 if (!get_or_create_client_record(jcr)) {
77 restore_cleanup(jcr, JS_ErrorTerminated);
81 memset(&rjr, 0, sizeof(rjr));
82 jcr->jr.Level = 'F'; /* Full restore */
83 jcr->jr.StartTime = jcr->start_time;
84 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
85 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
86 restore_cleanup(jcr, JS_ErrorTerminated);
89 Dmsg0(20, "Updated job start record\n");
90 jcr->fname = (char *) get_pool_memory(PM_FNAME);
92 Dmsg1(20, "RestoreJobId=%d\n", jcr->job->RestoreJobId);
95 * The following code is kept temporarily for compatibility.
96 * It is the predecessor to the Bootstrap file.
98 if (!jcr->RestoreBootstrap) {
100 * Find Job Record for Files to be restored
102 if (jcr->RestoreJobId != 0) {
103 rjr.JobId = jcr->RestoreJobId; /* specified by UA */
105 rjr.JobId = jcr->job->RestoreJobId; /* specified by Job Resource */
107 if (!db_get_job_record(jcr, jcr->db, &rjr)) {
108 Jmsg2(jcr, M_FATAL, 0, _("Cannot get job record id=%d %s"), rjr.JobId,
109 db_strerror(jcr->db));
110 restore_cleanup(jcr, JS_ErrorTerminated);
115 * Now find the Volumes we will need for the Restore
117 jcr->VolumeName[0] = 0;
118 if (!db_get_job_volume_names(jcr, jcr->db, rjr.JobId, &jcr->VolumeName) ||
119 jcr->VolumeName[0] == 0) {
120 Jmsg(jcr, M_FATAL, 0, _("Cannot find Volume Name for restore Job %d. %s"),
121 rjr.JobId, db_strerror(jcr->db));
122 restore_cleanup(jcr, JS_ErrorTerminated);
125 Dmsg1(20, "Got job Volume Names: %s\n", jcr->VolumeName);
129 /* Print Job Start message */
130 Jmsg(jcr, M_INFO, 0, _("Start Restore Job %s\n"), jcr->Job);
133 * Open a message channel connection with the Storage
134 * daemon. This is to let him know that our client
135 * will be contacting him for a backup session.
138 Dmsg0(10, "Open connection with storage daemon\n");
139 set_jcr_job_status(jcr, JS_Blocked);
141 * Start conversation with Storage daemon
143 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
144 restore_cleanup(jcr, JS_ErrorTerminated);
148 * Now start a job with the Storage daemon
150 if (!start_storage_daemon_job(jcr)) {
151 restore_cleanup(jcr, JS_ErrorTerminated);
155 * Now start a Storage daemon message thread
157 if (!start_storage_daemon_message_thread(jcr)) {
158 restore_cleanup(jcr, JS_ErrorTerminated);
161 Dmsg0(50, "Storage daemon connection OK\n");
164 * Start conversation with File daemon
166 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
167 restore_cleanup(jcr, JS_ErrorTerminated);
171 fd = jcr->file_bsock;
172 set_jcr_job_status(jcr, JS_Running);
174 if (!send_include_list(jcr)) {
175 restore_cleanup(jcr, JS_ErrorTerminated);
179 if (!send_exclude_list(jcr)) {
180 restore_cleanup(jcr, JS_ErrorTerminated);
185 * send Storage daemon address to the File daemon,
186 * then wait for File daemon to make connection
187 * with Storage daemon.
189 set_jcr_job_status(jcr, JS_Blocked);
190 if (jcr->store->SDDport == 0) {
191 jcr->store->SDDport = jcr->store->SDport;
193 bnet_fsend(fd, storaddr, jcr->store->address, jcr->store->SDDport);
194 Dmsg1(6, "dird>filed: %s\n", fd->msg);
195 if (!response(fd, OKstore, "Storage", 1)) {
196 restore_cleanup(jcr, JS_ErrorTerminated);
199 set_jcr_job_status(jcr, JS_Running);
202 * Send the bootstrap file -- what Volumes/files to restore
204 if (!send_bootstrap_file(jcr)) {
205 restore_cleanup(jcr, JS_ErrorTerminated);
210 * The following code is deprecated
212 if (!jcr->RestoreBootstrap) {
214 * Pass the VolSessionId, VolSessionTime, Start and
215 * end File and Blocks on the session command.
217 bnet_fsend(fd, sessioncmd,
219 rjr.VolSessionId, rjr.VolSessionTime,
220 rjr.StartFile, rjr.EndFile, rjr.StartBlock,
222 if (!response(fd, OKsession, "Session", 1)) {
223 restore_cleanup(jcr, JS_ErrorTerminated);
228 /* Send restore command */
229 char replace, *where;
231 if (jcr->replace != 0) {
232 replace = jcr->replace;
233 } else if (jcr->job->replace != 0) {
234 replace = jcr->job->replace;
236 replace = REPLACE_ALWAYS; /* always replace */
238 if (jcr->RestoreWhere) {
239 where = jcr->RestoreWhere; /* override */
240 } else if (jcr->job->RestoreWhere) {
241 where = jcr->job->RestoreWhere; /* no override take from job */
243 where = ""; /* None */
246 bnet_fsend(fd, restorecmd, replace, where);
247 unbash_spaces(where);
249 if (!response(fd, OKrestore, "Restore", 1)) {
250 restore_cleanup(jcr, JS_ErrorTerminated);
254 /* Wait for Job Termination */
255 Dmsg0(20, "wait for job termination\n");
256 while (bget_dirmsg(fd) >= 0) {
257 Dmsg1(100, "dird<filed: %s\n", fd->msg);
258 if (sscanf(fd->msg, EndRestore, &jcr->FDJobStatus, &jcr->JobFiles,
259 &jcr->JobBytes, &jcr->Errors) == 4) {
264 if (is_bnet_error(fd)) {
265 Jmsg(jcr, M_FATAL, 0, _("Network error during RESTORE command. ERR=%s\n"),
268 bnet_sig(fd, BNET_TERMINATE); /* tell Client we are terminating */
271 restore_cleanup(jcr, ok?jcr->FDJobStatus:JS_ErrorTerminated);
277 * Release resources allocated during restore.
280 static void restore_cleanup(JCR *jcr, int TermCode)
282 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
283 char ec1[30], ec2[30];
284 char term_code[100], fd_term_msg[100];
289 Dmsg0(20, "In restore_cleanup\n");
290 set_jcr_job_status(jcr, TermCode);
292 update_job_end_record(jcr);
294 msg_type = M_INFO; /* by default INFO message */
297 term_msg = _("Restore OK");
300 case JS_ErrorTerminated:
301 term_msg = _("*** Restore Error ***");
302 msg_type = M_ERROR; /* Generate error message */
303 if (jcr->store_bsock) {
304 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
305 pthread_cancel(jcr->SD_msg_chan);
309 term_msg = _("Restore Canceled");
310 if (jcr->store_bsock) {
311 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
312 pthread_cancel(jcr->SD_msg_chan);
316 term_msg = term_code;
317 sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode);
320 bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
321 bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
322 if (jcr->jr.EndTime - jcr->jr.StartTime > 0) {
323 kbps = (double)jcr->jr.JobBytes / (1000 * (jcr->jr.EndTime - jcr->jr.StartTime));
331 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
333 Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\
339 Files Restored: %s\n\
340 Bytes Restored: %s\n\
342 Non-fatal FD Errors: %d\n\
343 FD termination status: %s\n\
344 Termination: %s\n\n"),
348 jcr->client->hdr.name,
351 edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec1),
352 edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
358 Dmsg0(20, "Leaving restore_cleanup\n");
361 static int send_bootstrap_file(JCR *jcr)
365 BSOCK *fd = jcr->file_bsock;
366 char *bootstrap = "bootstrap\n";
368 Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
369 if (!jcr->RestoreBootstrap) {
372 bs = fopen(jcr->RestoreBootstrap, "r");
374 Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
375 jcr->RestoreBootstrap, strerror(errno));
376 set_jcr_job_status(jcr, JS_ErrorTerminated);
379 strcpy(fd->msg, bootstrap);
380 fd->msglen = strlen(fd->msg);
382 while (fgets(buf, sizeof(buf), bs)) {
383 fd->msglen = Mmsg(&fd->msg, "%s", buf);
386 bnet_sig(fd, BNET_EOD);
388 if (!response(fd, OKbootstrap, "Bootstrap", 1)) {
389 set_jcr_job_status(jcr, JS_ErrorTerminated);