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";
56 /* Forward referenced functions */
57 static void restore_cleanup(JCR *jcr, int status);
58 static int send_bootstrap_file(JCR *jcr);
60 /* External functions */
63 * Do a restore of the specified files
65 * Returns: 0 on failure
68 int do_restore(JCR *jcr)
71 JOB_DBR rjr; /* restore job record */
73 if (!get_or_create_client_record(jcr)) {
74 restore_cleanup(jcr, JS_ErrorTerminated);
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);
86 Dmsg0(20, "Updated job start record\n");
87 jcr->fname = (char *) get_pool_memory(PM_FNAME);
89 Dmsg1(20, "RestoreJobId=%d\n", jcr->job->RestoreJobId);
92 * The following code is kept temporarily for compatibility.
93 * It is the predecessor to the Bootstrap file.
95 if (!jcr->RestoreBootstrap) {
97 * Find Job Record for Files to be restored
99 if (jcr->RestoreJobId != 0) {
100 rjr.JobId = jcr->RestoreJobId; /* specified by UA */
102 rjr.JobId = jcr->job->RestoreJobId; /* specified by Job Resource */
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);
112 * Now find the Volumes we will need for the Restore
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);
122 Dmsg1(20, "Got job Volume Names: %s\n", jcr->VolumeName);
126 /* Print Job Start message */
127 Jmsg(jcr, M_INFO, 0, _("Start Restore Job %s\n"), jcr->Job);
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.
135 Dmsg0(10, "Open connection with storage daemon\n");
136 set_jcr_job_status(jcr, JS_Blocked);
138 * Start conversation with Storage daemon
140 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
141 restore_cleanup(jcr, JS_ErrorTerminated);
145 * Now start a job with the Storage daemon
147 if (!start_storage_daemon_job(jcr)) {
148 restore_cleanup(jcr, JS_ErrorTerminated);
152 * Now start a Storage daemon message thread
154 if (!start_storage_daemon_message_thread(jcr)) {
155 restore_cleanup(jcr, JS_ErrorTerminated);
158 Dmsg0(50, "Storage daemon connection OK\n");
161 * Start conversation with File daemon
163 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
164 restore_cleanup(jcr, JS_ErrorTerminated);
168 fd = jcr->file_bsock;
169 set_jcr_job_status(jcr, JS_Running);
171 if (!send_include_list(jcr)) {
172 restore_cleanup(jcr, JS_ErrorTerminated);
176 if (!send_exclude_list(jcr)) {
177 restore_cleanup(jcr, JS_ErrorTerminated);
182 * send Storage daemon address to the File daemon,
183 * then wait for File daemon to make connection
184 * with Storage daemon.
186 set_jcr_job_status(jcr, JS_Blocked);
187 if (jcr->store->SDDport == 0) {
188 jcr->store->SDDport = jcr->store->SDport;
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);
196 set_jcr_job_status(jcr, JS_Running);
199 * Send the bootstrap file -- what Volumes/files to restore
201 if (!send_bootstrap_file(jcr)) {
202 restore_cleanup(jcr, JS_ErrorTerminated);
207 * The following code is deprecated
209 if (!jcr->RestoreBootstrap) {
211 * Pass the VolSessionId, VolSessionTime, Start and
212 * end File and Blocks on the session command.
214 bnet_fsend(fd, sessioncmd,
216 rjr.VolSessionId, rjr.VolSessionTime,
217 rjr.StartFile, rjr.EndFile, rjr.StartBlock,
219 if (!response(jcr, fd, OKsession, "Session", DISPLAY_ERROR)) {
220 restore_cleanup(jcr, JS_ErrorTerminated);
225 /* Send restore command */
226 char replace, *where;
228 if (jcr->replace != 0) {
229 replace = jcr->replace;
230 } else if (jcr->job->replace != 0) {
231 replace = jcr->job->replace;
233 replace = REPLACE_ALWAYS; /* always replace */
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 */
240 where = ""; /* None */
243 bnet_fsend(fd, restorecmd, replace, where);
244 unbash_spaces(where);
246 if (!response(jcr, fd, OKrestore, "Restore", DISPLAY_ERROR)) {
247 restore_cleanup(jcr, JS_ErrorTerminated);
251 /* Wait for Job Termination */
252 int stat = wait_for_job_termination(jcr);
253 restore_cleanup(jcr, stat);
259 * Release resources allocated during restore.
262 static void restore_cleanup(JCR *jcr, int TermCode)
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];
271 Dmsg0(20, "In restore_cleanup\n");
272 set_jcr_job_status(jcr, TermCode);
274 update_job_end_record(jcr);
276 msg_type = M_INFO; /* by default INFO message */
279 term_msg = _("Restore OK");
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);
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);
298 term_msg = term_code;
299 sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode);
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));
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));
316 Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\
322 Files Restored: %s\n\
323 Bytes Restored: %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"),
332 jcr->client->hdr.name,
335 edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec1),
336 edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
343 Dmsg0(20, "Leaving restore_cleanup\n");
346 static int send_bootstrap_file(JCR *jcr)
350 BSOCK *fd = jcr->file_bsock;
351 char *bootstrap = "bootstrap\n";
353 Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
354 if (!jcr->RestoreBootstrap) {
357 bs = fopen(jcr->RestoreBootstrap, "r");
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);
364 strcpy(fd->msg, bootstrap);
365 fd->msglen = strlen(fd->msg);
367 while (fgets(buf, sizeof(buf), bs)) {
368 fd->msglen = Mmsg(&fd->msg, "%s", buf);
371 bnet_sig(fd, BNET_EOD);
373 if (!response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
374 set_jcr_job_status(jcr, JS_ErrorTerminated);