1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2006-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2017 Bareos GmbH & Co. KG
7
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
11 in the file LICENSE.
12
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 Affero General Public License for more details.
17
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
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, January MMVI
25 */
26 /**
27 * @file
28 * responsible for doing
29 * migration, archive, copy, and virtual backup jobs.
30 */
31
32 #include "include/bareos.h"
33 #include "stored/stored.h"
34 #include "stored/stored_globals.h"
35 #include "stored/acquire.h"
36 #include "stored/bsr.h"
37 #include "stored/append.h"
38 #include "stored/device.h"
39 #include "stored/jcr_private.h"
40 #include "stored/label.h"
41 #include "stored/mount.h"
42 #include "stored/read_record.h"
43 #include "stored/sd_stats.h"
44 #include "stored/spool.h"
45 #include "lib/bget_msg.h"
46 #include "lib/bnet.h"
47 #include "lib/bsock.h"
48 #include "lib/edit.h"
49 #include "include/jcr.h"
50
51 namespace storagedaemon {
52
53 /**
54 * Responses sent to the Director
55 */
56 static char Job_end[] =
57 "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u\n";
58
59 /**
60 * Responses received from Storage Daemon
61 */
62 static char OK_start_replicate[] = "3000 OK start replicate ticket = %d\n";
63 static char OK_data[] = "3000 OK data\n";
64 static char OK_replicate[] = "3000 OK replicate data\n";
65 static char OK_end_replicate[] = "3000 OK end replicate\n";
66
67 /**
68 * Commands sent to Storage Daemon
69 */
70 static char start_replicate[] = "start replicate\n";
71 static char ReplicateData[] = "replicate data %d\n";
72 static char end_replicate[] = "end replicate\n";
73
74
75 /* get information from first original SOS label for our job */
76 static bool found_first_sos_label = false;
77
78
79 /**
80 * Get response from Storage daemon to a command we sent.
81 * Check that the response is OK.
82 *
83 * Returns: false on failure
84 * true on success
85 */
response(JobControlRecord * jcr,BareosSocket * sd,char * resp,const char * cmd)86 static bool response(JobControlRecord* jcr,
87 BareosSocket* sd,
88 char* resp,
89 const char* cmd)
90 {
91 if (sd->errors) { return false; }
92 if (BgetMsg(sd) > 0) {
93 Dmsg1(110, "<stored: %s", sd->msg);
94 if (bstrcmp(sd->msg, resp)) { return true; }
95 }
96 if (JobCanceled(jcr)) {
97 return false; /* if canceled avoid useless error messages */
98 }
99 if (IsBnetError(sd)) {
100 Jmsg2(jcr, M_FATAL, 0,
101 _("Comm error with SD. bad response to %s. ERR=%s\n"), cmd,
102 BnetStrerror(sd));
103 } else {
104 Jmsg3(jcr, M_FATAL, 0, _("Bad response to %s command. Wanted %s, got %s\n"),
105 cmd, resp, sd->msg);
106 }
107 return false;
108 }
109
110 /**
111 * Called here for each record from ReadRecords()
112 * This function is used when we do a internal clone of a Job e.g.
113 * this SD is both the reading and writing SD.
114 *
115 * Returns: true if OK
116 * false if error
117 */
CloneRecordInternally(DeviceControlRecord * dcr,DeviceRecord * rec)118 static bool CloneRecordInternally(DeviceControlRecord* dcr, DeviceRecord* rec)
119 {
120 bool retval = false;
121 bool translated_record = false;
122 JobControlRecord* jcr = dcr->jcr;
123 Device* dev = jcr->impl->dcr->dev;
124 char buf1[100], buf2[100];
125
126 /*
127 * If label, discard it as we create our SOS and EOS Labels
128 * However, we still want the first Start Of Session label as that contains
129 * the timestamps from the first original Job.
130 *
131 * We need to put this info in our own SOS and EOS Labels so that
132 * bscan-ing of virtual and always incremental consolidated jobs
133 * works.
134 */
135
136 if (rec->FileIndex < 0) {
137 if (rec->FileIndex == SOS_LABEL) {
138 if (!found_first_sos_label) {
139 Dmsg0(200, "Found first SOS_LABEL and adopting job info\n");
140 found_first_sos_label = true;
141
142 static Session_Label the_label;
143 static Session_Label* label = &the_label;
144
145 struct date_time dt;
146 struct tm tm;
147
148 UnserSessionLabel(label, rec);
149
150 if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
151 bstrncpy(jcr->Job, label->Job, sizeof(jcr->Job));
152 PmStrcpy(jcr->impl->job_name, label->JobName);
153 PmStrcpy(jcr->client_name, label->ClientName);
154 PmStrcpy(jcr->impl->fileset_name, label->FileSetName);
155 PmStrcpy(jcr->impl->fileset_md5, label->FileSetMD5);
156 }
157 jcr->setJobType(label->JobType);
158 jcr->setJobLevel(label->JobLevel);
159 Dmsg1(200, "joblevel from SOS_LABEL is now %c\n", label->JobLevel);
160
161 if (label->VerNum >= 11) {
162 jcr->sched_time = BtimeToUnix(label->write_btime);
163
164 } else {
165 dt.julian_day_number = label->write_date;
166 dt.julian_day_fraction = label->write_time;
167 TmDecode(&dt, &tm);
168 tm.tm_isdst = 0;
169 jcr->sched_time = mktime(&tm);
170 }
171 jcr->start_time = jcr->sched_time;
172
173 /* write the SOS Label with the existing timestamp infos */
174 if (!WriteSessionLabel(jcr->impl->dcr, SOS_LABEL)) {
175 Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
176 dev->bstrerror());
177 jcr->setJobStatus(JS_ErrorTerminated);
178 retval = false;
179 goto bail_out;
180 }
181 } else {
182 Dmsg0(200, "Found additional SOS_LABEL, ignoring! \n");
183 }
184 }
185 /* Other label than SOS -> skip */
186 retval = true;
187 goto bail_out;
188 }
189
190 /*
191 * For normal migration jobs, FileIndex values are sequential because
192 * we are dealing with one job. However, for Vbackup (consolidation),
193 * we will be getting records from multiple jobs and writing them back
194 * out, so we need to ensure that the output FileIndex is sequential.
195 * We do so by detecting a FileIndex change and incrementing the
196 * JobFiles, which we then use as the output FileIndex.
197 */
198 if (rec->FileIndex >= 0) {
199 /*
200 * If something changed, increment FileIndex
201 */
202 if (rec->VolSessionId != rec->last_VolSessionId ||
203 rec->VolSessionTime != rec->last_VolSessionTime ||
204 rec->FileIndex != rec->last_FileIndex) {
205 jcr->JobFiles++;
206 rec->last_VolSessionId = rec->VolSessionId;
207 rec->last_VolSessionTime = rec->VolSessionTime;
208 rec->last_FileIndex = rec->FileIndex;
209 }
210 rec->FileIndex = jcr->JobFiles; /* set sequential output FileIndex */
211 }
212
213 /*
214 * Modify record SessionId and SessionTime to correspond to output.
215 */
216 rec->VolSessionId = jcr->VolSessionId;
217 rec->VolSessionTime = jcr->VolSessionTime;
218
219 Dmsg5(200, "before write JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
220 jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
221 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
222
223 /*
224 * Perform record translations.
225 */
226 jcr->impl->dcr->before_rec = rec;
227 jcr->impl->dcr->after_rec = NULL;
228 if (GeneratePluginEvent(jcr, bsdEventWriteRecordTranslation,
229 jcr->impl->dcr) != bRC_OK) {
230 goto bail_out;
231 }
232
233 /*
234 * The record got translated when we got an after_rec pointer after calling
235 * the bsdEventWriteRecordTranslation plugin event. If no translation has
236 * taken place we just point the after_rec pointer to same DeviceRecord as in
237 * the before_rec pointer.
238 */
239 if (!jcr->impl->dcr->after_rec) {
240 jcr->impl->dcr->after_rec = jcr->impl->dcr->before_rec;
241 } else {
242 translated_record = true;
243 }
244
245 while (!WriteRecordToBlock(jcr->impl->dcr, jcr->impl->dcr->after_rec)) {
246 Dmsg4(200, "!WriteRecordToBlock blkpos=%u:%u len=%d rem=%d\n", dev->file,
247 dev->block_num, jcr->impl->dcr->after_rec->data_len,
248 jcr->impl->dcr->after_rec->remainder);
249 if (!jcr->impl->dcr->WriteBlockToDevice()) {
250 Dmsg2(90, "Got WriteBlockToDev error on device %s. %s\n",
251 dev->print_name(), dev->bstrerror());
252 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
253 dev->print_name(), dev->bstrerror());
254 goto bail_out;
255 }
256 Dmsg2(200, "===== Wrote block new pos %u:%u\n", dev->file, dev->block_num);
257 }
258
259 /*
260 * Restore packet
261 */
262 jcr->impl->dcr->after_rec->VolSessionId =
263 jcr->impl->dcr->after_rec->last_VolSessionId;
264 jcr->impl->dcr->after_rec->VolSessionTime =
265 jcr->impl->dcr->after_rec->last_VolSessionTime;
266
267 if (jcr->impl->dcr->after_rec->FileIndex < 0) {
268 retval = true; /* don't send LABELs to Dir */
269 goto bail_out;
270 }
271
272 jcr->JobBytes +=
273 jcr->impl->dcr->after_rec->data_len; /* increment bytes of this job */
274
275 Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
276 jcr->JobId, FI_to_ascii(buf1, jcr->impl->dcr->after_rec->FileIndex),
277 jcr->impl->dcr->after_rec->VolSessionId,
278 stream_to_ascii(buf2, jcr->impl->dcr->after_rec->Stream,
279 jcr->impl->dcr->after_rec->FileIndex),
280 jcr->impl->dcr->after_rec->data_len);
281
282 SendAttrsToDir(jcr, jcr->impl->dcr->after_rec);
283
284 retval = true;
285
286 bail_out:
287 if (translated_record) {
288 FreeRecord(jcr->impl->dcr->after_rec);
289 jcr->impl->dcr->after_rec = NULL;
290 }
291
292 return retval;
293 }
294
295 /**
296 * Called here for each record from ReadRecords()
297 * This function is used when we do a external clone of a Job e.g.
298 * this SD is the reading SD. And a remote SD is the writing SD.
299 *
300 * Returns: true if OK
301 * false if error
302 */
CloneRecordToRemoteSd(DeviceControlRecord * dcr,DeviceRecord * rec)303 static bool CloneRecordToRemoteSd(DeviceControlRecord* dcr, DeviceRecord* rec)
304 {
305 POOLMEM* msgsave;
306 JobControlRecord* jcr = dcr->jcr;
307 char buf1[100], buf2[100];
308 BareosSocket* sd = jcr->store_bsock;
309 bool send_eod, send_header;
310
311 /*
312 * If label discard it
313 */
314 if (rec->FileIndex < 0) { return true; }
315
316 /*
317 * See if this is the first record being processed.
318 */
319 if (rec->last_FileIndex == 0) {
320 /*
321 * Initialize the last counters so we can compare
322 * things in the next run through here.
323 */
324 rec->last_VolSessionId = rec->VolSessionId;
325 rec->last_VolSessionTime = rec->VolSessionTime;
326 rec->last_FileIndex = rec->FileIndex;
327 rec->last_Stream = rec->Stream;
328 jcr->JobFiles = 1;
329
330 /*
331 * Need to send both a new header only.
332 */
333 send_eod = false;
334 send_header = true;
335 } else {
336 /*
337 * See if we are changing file or stream type.
338 */
339 if (rec->VolSessionId != rec->last_VolSessionId ||
340 rec->VolSessionTime != rec->last_VolSessionTime ||
341 rec->FileIndex != rec->last_FileIndex ||
342 rec->Stream != rec->last_Stream) {
343 /*
344 * See if we are changing the FileIndex e.g.
345 * start processing the next file in the backup stream.
346 */
347 if (rec->FileIndex != rec->last_FileIndex) { jcr->JobFiles++; }
348
349 /*
350 * Keep track of the new state.
351 */
352 rec->last_VolSessionId = rec->VolSessionId;
353 rec->last_VolSessionTime = rec->VolSessionTime;
354 rec->last_FileIndex = rec->FileIndex;
355 rec->last_Stream = rec->Stream;
356
357 /*
358 * Need to send both a EOD and a new header.
359 */
360 send_eod = true;
361 send_header = true;
362 } else {
363 send_eod = false;
364 send_header = false;
365 }
366 }
367
368 /*
369 * Send a EOD when needed.
370 */
371 if (send_eod) {
372 if (!sd->signal(BNET_EOD)) { /* indicate end of file data */
373 if (!jcr->IsJobCanceled()) {
374 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
375 sd->bstrerror());
376 }
377 return false;
378 }
379 }
380
381 /*
382 * Send a header when needed.
383 */
384 if (send_header) {
385 if (!sd->fsend("%ld %d 0", rec->FileIndex, rec->Stream)) {
386 if (!jcr->IsJobCanceled()) {
387 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
388 sd->bstrerror());
389 }
390 return false;
391 }
392 }
393
394 /*
395 * Send the record data.
396 * We don't want to copy the data from the record to the socket structure
397 * so we save the original msg pointer and use the record data pointer for
398 * sending and restore the original msg pointer when done.
399 */
400 msgsave = sd->msg;
401 sd->msg = rec->data;
402 sd->message_length = rec->data_len;
403
404 if (!sd->send()) {
405 sd->msg = msgsave;
406 sd->message_length = 0;
407 if (!jcr->IsJobCanceled()) {
408 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
409 sd->bstrerror());
410 }
411 return false;
412 }
413
414 jcr->JobBytes += sd->message_length;
415 sd->msg = msgsave;
416
417 Dmsg5(200, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
418 jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
419 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
420
421 return true;
422 }
423
424 /**
425 * Check autoinflation/autodeflation settings.
426 */
CheckAutoXflation(JobControlRecord * jcr)427 static inline void CheckAutoXflation(JobControlRecord* jcr)
428 {
429 /*
430 * See if the autoxflateonreplication flag is set to true then we allow
431 * autodeflation and autoinflation to take place.
432 */
433 if (me->autoxflateonreplication) { return; }
434
435 /*
436 * Check autodeflation.
437 */
438 switch (jcr->impl->read_dcr->autodeflate) {
439 case IO_DIRECTION_IN:
440 case IO_DIRECTION_INOUT:
441 Dmsg0(200, "Clearing autodeflate on read_dcr\n");
442 jcr->impl->read_dcr->autodeflate = IO_DIRECTION_NONE;
443 break;
444 default:
445 break;
446 }
447
448 if (jcr->impl->dcr) {
449 switch (jcr->impl->dcr->autodeflate) {
450 case IO_DIRECTION_OUT:
451 case IO_DIRECTION_INOUT:
452 Dmsg0(200, "Clearing autodeflate on write dcr\n");
453 jcr->impl->dcr->autodeflate = IO_DIRECTION_NONE;
454 break;
455 default:
456 break;
457 }
458 }
459
460 /*
461 * Check autoinflation.
462 */
463 switch (jcr->impl->read_dcr->autoinflate) {
464 case IO_DIRECTION_IN:
465 case IO_DIRECTION_INOUT:
466 Dmsg0(200, "Clearing autoinflate on read_dcr\n");
467 jcr->impl->read_dcr->autoinflate = IO_DIRECTION_NONE;
468 break;
469 default:
470 break;
471 }
472
473 if (jcr->impl->dcr) {
474 switch (jcr->impl->dcr->autoinflate) {
475 case IO_DIRECTION_OUT:
476 case IO_DIRECTION_INOUT:
477 Dmsg0(200, "Clearing autoinflate on write dcr\n");
478 jcr->impl->dcr->autoinflate = IO_DIRECTION_NONE;
479 break;
480 default:
481 break;
482 }
483 }
484 }
485
486 /**
487 * Read Data and commit to new job.
488 */
DoMacRun(JobControlRecord * jcr)489 bool DoMacRun(JobControlRecord* jcr)
490 {
491 utime_t now;
492 char ec1[50];
493 const char* Type;
494 bool ok = true;
495 bool acquire_fail = false;
496 BareosSocket* dir = jcr->dir_bsock;
497 Device* dev = jcr->impl->dcr->dev;
498
499 switch (jcr->getJobType()) {
500 case JT_MIGRATE:
501 Type = "Migration";
502 break;
503 case JT_ARCHIVE:
504 Type = "Archive";
505 break;
506 case JT_COPY:
507 Type = "Copy";
508 break;
509 case JT_BACKUP:
510 Type = "Virtual Backup";
511 break;
512 default:
513 Type = "Unknown";
514 break;
515 }
516
517 Dmsg0(20, "Start read data.\n");
518
519 if (jcr->impl->NumReadVolumes == 0) {
520 Jmsg(jcr, M_FATAL, 0, _("No Volume names found for %s.\n"), Type);
521 goto bail_out;
522 }
523
524 /*
525 * Check autoinflation/autodeflation settings.
526 */
527 CheckAutoXflation(jcr);
528
529 /*
530 * See if we perform both read and write or read only.
531 */
532 if (jcr->impl->remote_replicate) {
533 BareosSocket* sd;
534
535 if (!jcr->impl->read_dcr) {
536 Jmsg(jcr, M_FATAL, 0, _("Read device not properly initialized.\n"));
537 goto bail_out;
538 }
539
540 Dmsg1(100, "read_dcr=%p\n", jcr->impl->read_dcr);
541 Dmsg3(200, "Found %d volumes names for %s. First=%s\n",
542 jcr->impl->NumReadVolumes, Type, jcr->impl->VolList->VolumeName);
543
544 /*
545 * Ready devices for reading.
546 */
547 if (!AcquireDeviceForRead(jcr->impl->read_dcr)) {
548 ok = false;
549 acquire_fail = true;
550 goto bail_out;
551 }
552
553 Dmsg2(200, "===== After acquire pos %u:%u\n",
554 jcr->impl->read_dcr->dev->file, jcr->impl->read_dcr->dev->block_num);
555
556 jcr->sendJobStatus(JS_Running);
557
558 /*
559 * Set network buffering.
560 */
561 sd = jcr->store_bsock;
562 if (!sd->SetBufferSize(me->max_network_buffer_size, BNET_SETBUF_WRITE)) {
563 Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size SD->SD.\n"));
564 ok = false;
565 goto bail_out;
566 }
567
568 /*
569 * Let the remote SD know we are about to start the replication.
570 */
571 sd->fsend(start_replicate);
572 Dmsg1(110, ">stored: %s", sd->msg);
573
574 /*
575 * Expect to receive back the Ticket number.
576 */
577 if (BgetMsg(sd) >= 0) {
578 Dmsg1(110, "<stored: %s", sd->msg);
579 if (sscanf(sd->msg, OK_start_replicate, &jcr->impl->Ticket) != 1) {
580 Jmsg(jcr, M_FATAL, 0, _("Bad response to start replicate: %s\n"),
581 sd->msg);
582 goto bail_out;
583 }
584 Dmsg1(110, "Got Ticket=%d\n", jcr->impl->Ticket);
585 } else {
586 Jmsg(jcr, M_FATAL, 0,
587 _("Bad response from stored to start replicate command\n"));
588 goto bail_out;
589 }
590
591 /*
592 * Let the remote SD know we are now really going to send the data.
593 */
594 sd->fsend(ReplicateData, jcr->impl->Ticket);
595 Dmsg1(110, ">stored: %s", sd->msg);
596
597 /*
598 * Expect to get response to the replicate data cmd from Storage daemon
599 */
600 if (!response(jcr, sd, OK_data, "replicate data")) {
601 ok = false;
602 goto bail_out;
603 }
604
605 /*
606 * Update the initial Job Statistics.
607 */
608 now = (utime_t)time(NULL);
609 UpdateJobStatistics(jcr, now);
610
611 /*
612 * Read all data and send it to remote SD.
613 */
614 ok = ReadRecords(jcr->impl->read_dcr, CloneRecordToRemoteSd,
615 MountNextReadVolume);
616
617 /*
618 * Send the last EOD to close the last data transfer and a next EOD to
619 * signal the remote we are done.
620 */
621 if (!sd->signal(BNET_EOD) || !sd->signal(BNET_EOD)) {
622 if (!jcr->IsJobCanceled()) {
623 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
624 sd->bstrerror());
625 }
626 goto bail_out;
627 }
628
629 /*
630 * Expect to get response that the replicate data succeeded.
631 */
632 if (!response(jcr, sd, OK_replicate, "replicate data")) {
633 ok = false;
634 goto bail_out;
635 }
636
637 /*
638 * End replicate session.
639 */
640 sd->fsend(end_replicate);
641 Dmsg1(110, ">stored: %s", sd->msg);
642
643 /*
644 * Expect to get response to the end replicate cmd from Storage daemon
645 */
646 if (!response(jcr, sd, OK_end_replicate, "end replicate")) {
647 ok = false;
648 goto bail_out;
649 }
650
651 /* Inform Storage daemon that we are done */
652 sd->signal(BNET_TERMINATE);
653 } else {
654 if (!jcr->impl->read_dcr) {
655 Jmsg(jcr, M_FATAL, 0, _("Read device not properly initialized.\n"));
656 goto bail_out;
657 }
658
659 Dmsg2(100, "read_dcr=%p write_dcr=%p\n", jcr->impl->read_dcr,
660 jcr->impl->dcr);
661 Dmsg3(200, "Found %d volumes names for %s. First=%s\n",
662 jcr->impl->NumReadVolumes, Type, jcr->impl->VolList->VolumeName);
663
664 /*
665 * Ready devices for reading and writing.
666 */
667 if (!AcquireDeviceForAppend(jcr->impl->dcr) ||
668 !AcquireDeviceForRead(jcr->impl->read_dcr)) {
669 ok = false;
670 acquire_fail = true;
671 goto bail_out;
672 }
673
674 Dmsg2(200, "===== After acquire pos %u:%u\n", jcr->impl->dcr->dev->file,
675 jcr->impl->dcr->dev->block_num);
676
677 jcr->sendJobStatus(JS_Running);
678
679 /*
680 * Update the initial Job Statistics.
681 */
682 now = (utime_t)time(NULL);
683 UpdateJobStatistics(jcr, now);
684
685 if (!BeginDataSpool(jcr->impl->dcr)) {
686 ok = false;
687 goto bail_out;
688 }
689
690 if (!BeginAttributeSpool(jcr)) {
691 ok = false;
692 goto bail_out;
693 }
694
695 jcr->impl->dcr->VolFirstIndex = jcr->impl->dcr->VolLastIndex = 0;
696 jcr->run_time = time(NULL);
697 SetStartVolPosition(jcr->impl->dcr);
698 jcr->JobFiles = 0;
699
700 /*
701 * Read all data and make a local clone of it.
702 */
703 ok = ReadRecords(jcr->impl->read_dcr, CloneRecordInternally,
704 MountNextReadVolume);
705 }
706
707 bail_out:
708 if (!ok) { jcr->setJobStatus(JS_ErrorTerminated); }
709
710 if (!acquire_fail && !jcr->impl->remote_replicate && jcr->impl->dcr) {
711 /*
712 * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
713 * and the subsequent Jmsg() editing will break
714 */
715 int32_t job_elapsed;
716
717 Dmsg1(100, "ok=%d\n", ok);
718
719 if (ok || dev->CanWrite()) {
720 /*
721 memorize current JobStatus and set to
722 JS_Terminated to write into EOS_LABEL
723 */
724 char currentJobStatus = jcr->JobStatus;
725 jcr->setJobStatus(JS_Terminated);
726
727 /*
728 * Write End Of Session Label
729 */
730 DeviceControlRecord* dcr = jcr->impl->dcr;
731 if (!WriteSessionLabel(dcr, EOS_LABEL)) {
732 /*
733 * Print only if ok and not cancelled to avoid spurious messages
734 */
735
736 if (ok && !jcr->IsJobCanceled()) {
737 Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
738 dev->bstrerror());
739 }
740 jcr->setJobStatus(JS_ErrorTerminated);
741 ok = false;
742 } else {
743 /* restore JobStatus */
744 jcr->setJobStatus(currentJobStatus);
745 }
746 /*
747 * Flush out final partial block of this session
748 */
749 if (!jcr->impl->dcr->WriteBlockToDevice()) {
750 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
751 dev->print_name(), dev->bstrerror());
752 Dmsg0(100, _("Set ok=FALSE after WriteBlockToDevice.\n"));
753 ok = false;
754 }
755 Dmsg2(200, "Flush block to device pos %u:%u\n", dev->file,
756 dev->block_num);
757 }
758
759
760 if (!ok) {
761 DiscardDataSpool(jcr->impl->dcr);
762 } else {
763 /*
764 * Note: if commit is OK, the device will remain blocked
765 */
766 CommitDataSpool(jcr->impl->dcr);
767 }
768
769 job_elapsed = time(NULL) - jcr->run_time;
770 if (job_elapsed <= 0) { job_elapsed = 1; }
771
772 Jmsg(jcr, M_INFO, 0,
773 _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
774 job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
775 edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec1));
776
777 /*
778 * Release the device -- and send final Vol info to DIR
779 */
780 ReleaseDevice(jcr->impl->dcr);
781
782 if (!ok || JobCanceled(jcr)) {
783 DiscardAttributeSpool(jcr);
784 } else {
785 CommitAttributeSpool(jcr);
786 }
787 }
788
789 if (jcr->impl->read_dcr) {
790 if (!ReleaseDevice(jcr->impl->read_dcr)) { ok = false; }
791 }
792
793 jcr->sendJobStatus(); /* update director */
794
795 Dmsg0(30, "Done reading.\n");
796 jcr->end_time = time(NULL);
797 DequeueMessages(jcr); /* send any queued messages */
798 if (ok) { jcr->setJobStatus(JS_Terminated); }
799
800 GeneratePluginEvent(jcr, bsdEventJobEnd);
801 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
802 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
803 Dmsg4(100, Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles, ec1);
804
805 dir->signal(BNET_EOD); /* send EOD to Director daemon */
806 FreePlugins(jcr); /* release instantiated plugins */
807
808 return false; /* Continue DIR session ? */
809 }
810
811 } /* namespace storagedaemon */
812