2 Bacula® - The Network Backup Solution
4 Copyright (C) 2006-2012 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * SD -- mac.c -- responsible for doing
30 * migration, archive, copy, and virtual backup jobs.
32 * Kern Sibbald, January MMVI
39 /* Import functions */
40 extern char Job_end[];
42 /* Forward referenced subroutines */
43 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
47 * Read Data and send to File Daemon
48 * Returns: false on failure
54 BSOCK *dir = jcr->dir_bsock;
59 switch(jcr->getJobType()) {
70 Type = "Virtual Backup";
78 Dmsg0(20, "Start read data.\n");
80 if (!jcr->read_dcr || !jcr->dcr) {
81 Jmsg(jcr, M_FATAL, 0, _("Read and write devices not properly initialized.\n"));
84 Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->read_dcr, jcr->dcr);
86 if (jcr->NumReadVolumes == 0) {
87 Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
91 Dmsg3(200, "Found %d volumes names for %s. First=%s\n", jcr->NumReadVolumes,
92 jcr->VolList->VolumeName, Type);
94 /* Ready devices for reading and writing */
95 if (!acquire_device_for_read(jcr->read_dcr) ||
96 !acquire_device_for_append(jcr->dcr)) {
97 jcr->setJobStatus(JS_ErrorTerminated);
101 Dmsg2(200, "===== After acquire pos %u:%u\n", jcr->dcr->dev->file, jcr->dcr->dev->block_num);
103 jcr->sendJobStatus(JS_Running);
105 begin_data_spool(jcr->dcr);
106 begin_attribute_spool(jcr);
108 jcr->dcr->VolFirstIndex = jcr->dcr->VolLastIndex = 0;
109 jcr->run_time = time(NULL);
110 set_start_vol_position(jcr->dcr);
113 ok = read_records(jcr->read_dcr, record_cb, mount_next_read_volume);
122 Dmsg1(100, "ok=%d\n", ok);
123 if (ok || dev->can_write()) {
124 /* Flush out final partial block of this session */
125 if (!jcr->dcr->write_block_to_device()) {
126 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
127 dev->print_name(), dev->bstrerror());
128 Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
131 Dmsg2(200, "Flush block to device pos %u:%u\n", dev->file, dev->block_num);
135 discard_data_spool(jcr->dcr);
137 /* Note: if commit is OK, the device will remain blocked */
138 commit_data_spool(jcr->dcr);
142 * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
143 * and the subsequent Jmsg() editing will break
145 int32_t job_elapsed = time(NULL) - jcr->run_time;
147 if (job_elapsed <= 0) {
151 Jmsg(jcr, M_INFO, 0, _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
152 job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
153 edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec1));
155 if (ok && dev->is_dvd()) {
156 ok = dvd_close_job(jcr->dcr); /* do DVD cleanup if any */
158 /* Release the device -- and send final Vol info to DIR */
159 release_device(jcr->dcr);
161 if (!ok || job_canceled(jcr)) {
162 discard_attribute_spool(jcr);
164 commit_attribute_spool(jcr);
169 if (!release_device(jcr->read_dcr)) {
174 jcr->sendJobStatus(); /* update director */
176 Dmsg0(30, "Done reading.\n");
177 jcr->end_time = time(NULL);
178 dequeue_messages(jcr); /* send any queued messages */
180 jcr->setJobStatus(JS_Terminated);
182 generate_daemon_event(jcr, "JobEnd");
183 generate_plugin_event(jcr, bsdEventJobEnd);
184 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
185 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
186 Dmsg4(100, Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles, ec1);
188 dir->signal(BNET_EOD); /* send EOD to Director daemon */
189 free_plugins(jcr); /* release instantiated plugins */
195 * Called here for each record from read_records()
196 * Returns: true if OK
199 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
202 DEVICE *dev = jcr->dcr->dev;
203 char buf1[100], buf2[100];
206 Dmsg5(000, "on entry JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
208 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
209 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
211 /* If label and not for us, discard it */
212 if (rec->FileIndex < 0 && rec->match_stat <= 0) {
215 /* We want to write SOS_LABEL and EOS_LABEL discard all others */
216 switch (rec->FileIndex) {
221 return true; /* don't write vol labels */
223 // if (jcr->getJobType() == JT_BACKUP) {
225 * For normal migration jobs, FileIndex values are sequential because
226 * we are dealing with one job. However, for Vbackup (consolidation),
227 * we will be getting records from multiple jobs and writing them back
228 * out, so we need to ensure that the output FileIndex is sequential.
229 * We do so by detecting a FileIndex change and incrementing the
230 * JobFiles, which we then use as the output FileIndex.
232 if (rec->FileIndex >= 0) {
233 /* If something changed, increment FileIndex */
234 if (rec->VolSessionId != rec->last_VolSessionId ||
235 rec->VolSessionTime != rec->last_VolSessionTime ||
236 rec->FileIndex != rec->last_FileIndex) {
238 rec->last_VolSessionId = rec->VolSessionId;
239 rec->last_VolSessionTime = rec->VolSessionTime;
240 rec->last_FileIndex = rec->FileIndex;
242 rec->FileIndex = jcr->JobFiles; /* set sequential output FileIndex */
246 * Modify record SessionId and SessionTime to correspond to
249 rec->VolSessionId = jcr->VolSessionId;
250 rec->VolSessionTime = jcr->VolSessionTime;
251 Dmsg5(200, "before write JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
253 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
254 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
255 while (!write_record_to_block(jcr->dcr, rec)) {
256 Dmsg4(200, "!write_record_to_block blkpos=%u:%u len=%d rem=%d\n",
257 dev->file, dev->block_num, rec->data_len, rec->remainder);
258 if (!jcr->dcr->write_block_to_device()) {
259 Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
260 dev->print_name(), dev->bstrerror());
261 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
262 dev->print_name(), dev->bstrerror());
265 Dmsg2(200, "===== Wrote block new pos %u:%u\n", dev->file, dev->block_num);
268 rec->VolSessionId = rec->last_VolSessionId;
269 rec->VolSessionTime = rec->last_VolSessionTime;
270 if (rec->FileIndex < 0) {
271 return true; /* don't send LABELs to Dir */
273 jcr->JobBytes += rec->data_len; /* increment bytes this job */
274 Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
276 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
277 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
279 send_attrs_to_dir(jcr, rec);