/*
- *
* Bacula Director -- restore.c -- responsible for restoring files
*
* Kern Sibbald, November MM
*
- * This routine is run as a separate thread. There may be more
- * work to be done to make it totally reentrant!!!!
+ * This routine is run as a separate thread.
*
* Current implementation is Catalog verification only (i.e. no
* verification versus tape).
*/
/*
- Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+ Copyright (C) 2000-2004 Kern Sibbald and John Walker
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
#include "dird.h"
/* Commands sent to File daemon */
-static char restorecmd[] = "restore where=%s\n";
-static char storaddr[] = "storage address=%s port=%d\n";
+static char restorecmd[] = "restore replace=%c prelinks=%d where=%s\n";
+static char storaddr[] = "storage address=%s port=%d ssl=0\n";
static char sessioncmd[] = "session %s %ld %ld %ld %ld %ld %ld\n";
/* Responses received from File daemon */
*/
int do_restore(JCR *jcr)
{
- char dt[MAX_TIME_LENGTH];
BSOCK *fd;
JOB_DBR rjr; /* restore job record */
-
if (!get_or_create_client_record(jcr)) {
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
}
memset(&rjr, 0, sizeof(rjr));
- jcr->jr.Level = 'F'; /* Full restore */
- jcr->jr.StartTime = jcr->start_time;
- if (!db_update_job_start_record(jcr->db, &jcr->jr)) {
+ jcr->jr.JobLevel = L_FULL; /* Full restore */
+ if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
Dmsg1(20, "RestoreJobId=%d\n", jcr->job->RestoreJobId);
- /*
- * Find Job Record for Files to be restored
- */
- if (jcr->RestoreJobId != 0) {
- rjr.JobId = jcr->RestoreJobId; /* specified by UA */
- } else {
- rjr.JobId = jcr->job->RestoreJobId; /* specified by Job Resource */
- }
- if (!db_get_job_record(jcr->db, &rjr)) {
- Jmsg2(jcr, M_FATAL, 0, _("Cannot get job record id=%d %s"), rjr.JobId,
- db_strerror(jcr->db));
- restore_cleanup(jcr, JS_ErrorTerminated);
- return 0;
- }
- Dmsg3(20, "Got JobId=%d VolSessId=%ld, VolSesTime=%ld\n",
- rjr.JobId, rjr.VolSessionId, rjr.VolSessionTime);
- Dmsg4(20, "StartFile=%ld, EndFile=%ld StartBlock=%ld EndBlock=%ld\n",
- rjr.StartFile, rjr.EndFile, rjr.StartBlock, rjr.EndBlock);
-
- /*
- * Now find the Volumes we will need for the Restore
+ /*
+ * The following code is kept temporarily for compatibility.
+ * It is the predecessor to the Bootstrap file.
+ * DEPRECATED
*/
- jcr->VolumeName[0] = 0;
- if (!db_get_job_volume_names(jcr->db, rjr.JobId, jcr->VolumeName) ||
- jcr->VolumeName[0] == 0) {
- Jmsg(jcr, M_FATAL, 0, _("Cannot find Volume Name for restore Job %d. %s"),
- rjr.JobId, db_strerror(jcr->db));
- restore_cleanup(jcr, JS_ErrorTerminated);
- return 0;
+ if (!jcr->RestoreBootstrap) {
+ /*
+ * Find Job Record for Files to be restored
+ */
+ if (jcr->RestoreJobId != 0) {
+ rjr.JobId = jcr->RestoreJobId; /* specified by UA */
+ } else {
+ rjr.JobId = jcr->job->RestoreJobId; /* specified by Job Resource */
+ }
+ if (!db_get_job_record(jcr, jcr->db, &rjr)) {
+ Jmsg2(jcr, M_FATAL, 0, _("Cannot get job record id=%d %s"), rjr.JobId,
+ db_strerror(jcr->db));
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ return 0;
+ }
+
+ /*
+ * Now find the Volumes we will need for the Restore
+ */
+ jcr->VolumeName[0] = 0;
+ if (!db_get_job_volume_names(jcr, jcr->db, rjr.JobId, &jcr->VolumeName) ||
+ jcr->VolumeName[0] == 0) {
+ Jmsg(jcr, M_FATAL, 0, _("Cannot find Volume names for restore Job %d. %s"),
+ rjr.JobId, db_strerror(jcr->db));
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ return 0;
+ }
+ Dmsg1(20, "Got job Volume Names: %s\n", jcr->VolumeName);
}
- Dmsg1(20, "Got job Volume Names: %s\n", jcr->VolumeName);
/* Print Job Start message */
- bstrftime(dt, sizeof(dt), jcr->start_time);
- Jmsg(jcr, M_INFO, 0, _("%s Start Restore Job %s Name=%s, Client=%s, FileSet=%s\n"),
- dt, jcr->Job, jcr->job->hdr.name, jcr->client->hdr.name,
- jcr->fileset->hdr.name);
+ Jmsg(jcr, M_INFO, 0, _("Start Restore Job %s\n"), jcr->Job);
/*
* Open a message channel connection with the Storage
*
*/
Dmsg0(10, "Open connection with storage daemon\n");
- jcr->JobStatus = JS_Blocked;
+ set_jcr_job_status(jcr, JS_WaitSD);
/*
* Start conversation with Storage daemon
*/
/*
* Start conversation with File daemon
*/
+ set_jcr_job_status(jcr, JS_WaitFD);
if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
}
fd = jcr->file_bsock;
- jcr->JobStatus = JS_Running;
+ set_jcr_job_status(jcr, JS_Running);
if (!send_include_list(jcr)) {
restore_cleanup(jcr, JS_ErrorTerminated);
* then wait for File daemon to make connection
* with Storage daemon.
*/
- jcr->JobStatus = JS_Blocked;
if (jcr->store->SDDport == 0) {
jcr->store->SDDport = jcr->store->SDport;
}
bnet_fsend(fd, storaddr, jcr->store->address, jcr->store->SDDport);
Dmsg1(6, "dird>filed: %s\n", fd->msg);
- if (!response(fd, OKstore, "Storage")) {
+ if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
}
- jcr->JobStatus = JS_Running;
- /*
- * Pass the VolSessionId, VolSessionTime, Start and
- * end File and Blocks on the session command.
+ /*
+ * Send the bootstrap file -- what Volumes/files to restore
+ */
+ if (!send_bootstrap_file(jcr)) {
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ return 0;
+ }
+
+ /*
+ * The following code is deprecated
*/
- bnet_fsend(fd, sessioncmd,
- jcr->VolumeName,
- rjr.VolSessionId, rjr.VolSessionTime,
- rjr.StartFile, rjr.EndFile, rjr.StartBlock,
- rjr.EndBlock);
- if (!response(fd, OKsession, "Session")) {
+ if (!jcr->RestoreBootstrap) {
+ /*
+ * Pass the VolSessionId, VolSessionTime, Start and
+ * end File and Blocks on the session command.
+ */
+ bnet_fsend(fd, sessioncmd,
+ jcr->VolumeName,
+ rjr.VolSessionId, rjr.VolSessionTime,
+ rjr.StartFile, rjr.EndFile, rjr.StartBlock,
+ rjr.EndBlock);
+ if (!response(jcr, fd, OKsession, "Session", DISPLAY_ERROR)) {
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ return 0;
+ }
+ }
+
+ if (!send_run_before_and_after_commands(jcr)) {
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
}
/* Send restore command */
- if (jcr->RestoreWhere) {
- bnet_fsend(fd, restorecmd, jcr->RestoreWhere);
+ char replace, *where;
+ char empty = '\0';
+
+ if (jcr->replace != 0) {
+ replace = jcr->replace;
+ } else if (jcr->job->replace != 0) {
+ replace = jcr->job->replace;
+ } else {
+ replace = REPLACE_ALWAYS; /* always replace */
+ }
+ if (jcr->where) {
+ where = jcr->where; /* override */
+ } else if (jcr->job->RestoreWhere) {
+ where = jcr->job->RestoreWhere; /* no override take from job */
} else {
- bnet_fsend(fd, restorecmd,
- jcr->job->RestoreWhere==NULL ? "" : jcr->job->RestoreWhere);
+ where = ∅ /* None */
}
- if (!response(fd, OKrestore, "Restore")) {
+ jcr->prefix_links = jcr->job->PrefixLinks;
+ bash_spaces(where);
+ bnet_fsend(fd, restorecmd, replace, jcr->prefix_links, where);
+ unbash_spaces(where);
+
+ if (!response(jcr, fd, OKrestore, "Restore", DISPLAY_ERROR)) {
restore_cleanup(jcr, JS_ErrorTerminated);
return 0;
}
/* Wait for Job Termination */
- /*** ****FIXME**** get job termination data */
- Dmsg0(20, "wait for job termination\n");
- while (bget_msg(fd, 0) > 0) {
- Dmsg1(0, "dird<filed: %s\n", fd->msg);
- }
-
- restore_cleanup(jcr, JS_Terminated);
+ int stat = wait_for_job_termination(jcr);
+ restore_cleanup(jcr, stat);
return 1;
}
* Release resources allocated during restore.
*
*/
-static void restore_cleanup(JCR *jcr, int status)
+static void restore_cleanup(JCR *jcr, int TermCode)
{
- char dt[MAX_TIME_LENGTH];
+ char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
+ char ec1[30], ec2[30], ec3[30];
+ char term_code[100], fd_term_msg[100], sd_term_msg[100];
+ const char *term_msg;
+ int msg_type;
+ double kbps;
Dmsg0(20, "In restore_cleanup\n");
- jcr->JobStatus = status;
+ dequeue_messages(jcr); /* display any queued messages */
+ set_jcr_job_status(jcr, TermCode);
update_job_end_record(jcr);
- bstrftime(dt, sizeof(dt), jcr->jr.EndTime);
- Jmsg(jcr, M_INFO, 0, _("%s End Restore Job %s.\n\n"),
- dt, jcr->Job);
+ msg_type = M_INFO; /* by default INFO message */
+ switch (TermCode) {
+ case JS_Terminated:
+ if (jcr->ExpectedFiles > jcr->jr.JobFiles) {
+ term_msg = _("Restore OK -- warning file count mismatch");
+ } else {
+ term_msg = _("Restore OK");
+ }
+ break;
+ case JS_FatalError:
+ case JS_ErrorTerminated:
+ term_msg = _("*** Restore Error ***");
+ msg_type = M_ERROR; /* Generate error message */
+ if (jcr->store_bsock) {
+ bnet_sig(jcr->store_bsock, BNET_TERMINATE);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
+ }
+ break;
+ case JS_Canceled:
+ term_msg = _("Restore Canceled");
+ if (jcr->store_bsock) {
+ bnet_sig(jcr->store_bsock, BNET_TERMINATE);
+ if (jcr->SD_msg_chan) {
+ pthread_cancel(jcr->SD_msg_chan);
+ }
+ }
+ break;
+ default:
+ term_msg = term_code;
+ sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode);
+ break;
+ }
+ bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
+ bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
+ if (jcr->jr.EndTime - jcr->jr.StartTime > 0) {
+ kbps = (double)jcr->jr.JobBytes / (1000 * (jcr->jr.EndTime - jcr->jr.StartTime));
+ } else {
+ kbps = 0;
+ }
+ if (kbps < 0.05) {
+ kbps = 0;
+ }
+
+ jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
+ jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
+
+ Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\
+JobId: %d\n\
+Job: %s\n\
+Client: %s\n\
+Start time: %s\n\
+End time: %s\n\
+Files Expected: %s\n\
+Files Restored: %s\n\
+Bytes Restored: %s\n\
+Rate: %.1f KB/s\n\
+FD Errors: %d\n\
+FD termination status: %s\n\
+SD termination status: %s\n\
+Termination: %s\n\n"),
+ edt,
+ jcr->jr.JobId,
+ jcr->jr.Job,
+ jcr->client->hdr.name,
+ sdt,
+ edt,
+ edit_uint64_with_commas((uint64_t)jcr->ExpectedFiles, ec1),
+ edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec2),
+ edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
+ (float)kbps,
+ jcr->Errors,
+ fd_term_msg,
+ sd_term_msg,
+ term_msg);
Dmsg0(20, "Leaving restore_cleanup\n");
}