2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2010 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.
31 * Kern Sibbald, March 2004
38 /* Forward referenced subroutines */
39 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name);
40 static bool open_data_spool_file(DCR *dcr);
41 static bool close_data_spool_file(DCR *dcr);
42 static bool despool_data(DCR *dcr, bool commit);
43 static int read_block_from_spool_file(DCR *dcr);
44 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs);
45 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs);
46 static bool write_spool_header(DCR *dcr);
47 static bool write_spool_data(DCR *dcr);
49 struct spool_stats_t {
50 uint32_t data_jobs; /* current jobs spooling data */
52 uint32_t total_data_jobs; /* total jobs to have spooled data */
53 uint32_t total_attr_jobs;
54 int64_t max_data_size; /* max data size */
55 int64_t max_attr_size;
56 int64_t data_size; /* current data size (all jobs running) */
60 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
61 spool_stats_t spool_stats;
64 * Header for data spool record */
66 int32_t FirstIndex; /* FirstIndex for buffer */
67 int32_t LastIndex; /* LastIndex for buffer */
68 uint32_t len; /* length of next buffer */
77 void list_spool_stats(void sendit(const char *msg, int len, void *sarg), void *arg)
79 char ed1[30], ed2[30];
80 POOL_MEM msg(PM_MESSAGE);
83 len = Mmsg(msg, _("Spooling statistics:\n"));
85 if (spool_stats.data_jobs || spool_stats.max_data_size) {
86 len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
87 spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
88 spool_stats.total_data_jobs,
89 edit_uint64_with_commas(spool_stats.max_data_size, ed2));
91 sendit(msg.c_str(), len, arg);
93 if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
94 len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
95 spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
96 spool_stats.total_attr_jobs,
97 edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
99 sendit(msg.c_str(), len, arg);
103 bool begin_data_spool(DCR *dcr)
106 if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
107 Dmsg0(100, "Turning on data spooling\n");
108 dcr->spool_data = true;
109 stat = open_data_spool_file(dcr);
111 dcr->spooling = true;
112 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
114 spool_stats.data_jobs++;
121 bool discard_data_spool(DCR *dcr)
124 Dmsg0(100, "Data spooling discarded\n");
125 return close_data_spool_file(dcr);
130 bool commit_data_spool(DCR *dcr)
135 Dmsg0(100, "Committing spooled data\n");
136 stat = despool_data(dcr, true /*commit*/);
138 Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
139 close_data_spool_file(dcr);
142 return close_data_spool_file(dcr);
147 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
150 if (dcr->dev->device->spool_directory) {
151 dir = dcr->dev->device->spool_directory;
153 dir = working_directory;
155 Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
156 dcr->jcr->Job, dcr->device->hdr.name);
160 static bool open_data_spool_file(DCR *dcr)
162 POOLMEM *name = get_pool_memory(PM_MESSAGE);
165 make_unique_data_spool_filename(dcr, &name);
166 if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
167 dcr->spool_fd = spool_fd;
168 dcr->jcr->spool_attributes = true;
171 Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
173 free_pool_memory(name);
176 Dmsg1(100, "Created spool file: %s\n", name);
177 free_pool_memory(name);
181 static bool close_data_spool_file(DCR *dcr)
183 POOLMEM *name = get_pool_memory(PM_MESSAGE);
186 spool_stats.data_jobs--;
187 spool_stats.total_data_jobs++;
188 if (spool_stats.data_size < dcr->job_spool_size) {
189 spool_stats.data_size = 0;
191 spool_stats.data_size -= dcr->job_spool_size;
193 dcr->job_spool_size = 0;
196 make_unique_data_spool_filename(dcr, &name);
197 close(dcr->spool_fd);
199 dcr->spooling = false;
201 Dmsg1(100, "Deleted spool file: %s\n", name);
202 free_pool_memory(name);
206 static const char *spool_name = "*spool*";
209 * NB! This routine locks the device, but if committing will
210 * not unlock it. If not committing, it will be unlocked.
212 static bool despool_data(DCR *dcr, bool commit)
222 Dmsg0(100, "Despooling data\n");
223 if (jcr->dcr->job_spool_size == 0) {
224 Jmsg(jcr, M_WARNING, 0, _("Despooling zero bytes. Your disk is probably FULL!\n"));
228 * Commit means that the job is done, so we commit, otherwise, we
229 * are despooling because of user spool size max or some error
230 * (e.g. filesystem full).
233 Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
234 jcr->dcr->VolumeName,
235 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
236 set_jcr_job_status(jcr, JS_DataCommitting);
238 Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
239 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
240 set_jcr_job_status(jcr, JS_DataDespooling);
242 set_jcr_job_status(jcr, JS_DataDespooling);
243 dir_send_job_status(jcr);
244 dcr->despool_wait = true;
245 dcr->spooling = false;
247 * We work with device blocked, but not locked so that
248 * other threads -- e.g. reservations can lock the device
251 dcr->dblock(BST_DESPOOLING);
252 dcr->despool_wait = false;
253 dcr->despooling = true;
256 * This is really quite kludgy and should be fixed some time.
257 * We create a dev structure to read from the spool file
260 rdev = (DEVICE *)malloc(sizeof(DEVICE));
261 memset(rdev, 0, sizeof(DEVICE));
262 rdev->dev_name = get_memory(strlen(spool_name)+1);
263 bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
264 rdev->errmsg = get_pool_memory(PM_EMSG);
266 rdev->max_block_size = dcr->dev->max_block_size;
267 rdev->min_block_size = dcr->dev->min_block_size;
268 rdev->device = dcr->dev->device;
269 rdcr = new_dcr(jcr, NULL, rdev);
270 rdcr->spool_fd = dcr->spool_fd;
271 block = dcr->block; /* save block */
272 dcr->block = rdcr->block; /* make read and write block the same */
274 Dmsg1(800, "read/write block size = %d\n", block->buf_len);
275 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
277 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
278 posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
281 /* Add run time, to get current wait time */
282 int32_t despool_start = time(NULL) - jcr->run_time;
284 set_new_file_parameters(dcr);
287 if (job_canceled(jcr)) {
291 stat = read_block_from_spool_file(rdcr);
292 if (stat == RB_EOT) {
294 } else if (stat == RB_ERROR) {
298 ok = write_block_to_device(dcr);
300 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
301 dcr->dev->print_name(), dcr->dev->bstrerror());
302 Dmsg2(000, "Fatal append error on device %s: ERR=%s\n",
303 dcr->dev->print_name(), dcr->dev->bstrerror());
305 Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
308 if (!dir_create_jobmedia_record(dcr)) {
309 Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
310 dcr->getVolCatName(), jcr->Job);
312 /* Set new file/block parameters for current dcr */
313 set_new_file_parameters(dcr);
316 * Subtracting run_time give us elapsed time - wait_time since
317 * we started despooling. Note, don't use time_t as it is 32 or 64
318 * bits depending on the OS and doesn't edit with %d
320 int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
322 if (despool_elapsed <= 0) {
326 Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
327 despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
328 edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
330 dcr->block = block; /* reset block */
332 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
333 if (ftruncate(rdcr->spool_fd, 0) != 0) {
335 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
337 /* Note, try continuing despite ftruncate problem */
341 if (spool_stats.data_size < dcr->job_spool_size) {
342 spool_stats.data_size = 0;
344 spool_stats.data_size -= dcr->job_spool_size;
347 P(dcr->dev->spool_mutex);
348 dcr->dev->spool_size -= dcr->job_spool_size;
349 dcr->job_spool_size = 0; /* zap size in input dcr */
350 V(dcr->dev->spool_mutex);
351 free_memory(rdev->dev_name);
352 free_pool_memory(rdev->errmsg);
353 /* Be careful to NULL the jcr and free rdev after free_dcr() */
358 dcr->spooling = true; /* turn on spooling again */
359 dcr->despooling = false;
361 * Note, if committing we leave the device blocked. It will be removed in
365 dcr->dev->dunblock();
367 set_jcr_job_status(jcr, JS_Running);
368 dir_send_job_status(jcr);
373 * Read a block from the spool file
375 * Returns RB_OK on success
376 * RB_EOT when file done
379 static int read_block_from_spool_file(DCR *dcr)
384 DEV_BLOCK *block = dcr->block;
387 stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
389 Dmsg0(100, "EOT on spool read.\n");
391 } else if (stat != (ssize_t)rlen) {
394 Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
397 Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
398 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
403 if (rlen > block->buf_len) {
404 Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
405 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
408 stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
409 if (stat != (ssize_t)rlen) {
410 Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
411 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
414 /* Setup write pointers */
415 block->binbuf = rlen;
416 block->bufp = block->buf + block->binbuf;
417 block->FirstIndex = hdr.FirstIndex;
418 block->LastIndex = hdr.LastIndex;
419 block->VolSessionId = dcr->jcr->VolSessionId;
420 block->VolSessionTime = dcr->jcr->VolSessionTime;
421 Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
426 * Write a block to the spool file
428 * Returns: true on success or EOT
429 * false on hard error
431 bool write_block_to_spool_file(DCR *dcr)
433 uint32_t wlen, hlen; /* length to write */
434 bool despool = false;
435 DEV_BLOCK *block = dcr->block;
437 if (job_canceled(dcr->jcr)) {
440 ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
441 if (block->binbuf <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
445 hlen = sizeof(spool_hdr);
446 wlen = block->binbuf;
447 P(dcr->dev->spool_mutex);
448 dcr->job_spool_size += hlen + wlen;
449 dcr->dev->spool_size += hlen + wlen;
450 if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
451 (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
454 V(dcr->dev->spool_mutex);
456 spool_stats.data_size += hlen + wlen;
457 if (spool_stats.data_size > spool_stats.max_data_size) {
458 spool_stats.max_data_size = spool_stats.data_size;
463 char ec1[30], ec2[30], ec3[30], ec4[30];
464 Dmsg4(100, "Despool in write_block_to_spool_file max_size=%s size=%s "
465 "max_job_size=%s job_size=%s\n",
466 edit_uint64_with_commas(dcr->max_job_spool_size, ec1),
467 edit_uint64_with_commas(dcr->job_spool_size, ec2),
468 edit_uint64_with_commas(dcr->dev->max_spool_size, ec3),
469 edit_uint64_with_commas(dcr->dev->spool_size, ec4));
471 Jmsg(dcr->jcr, M_INFO, 0, _("User specified spool size reached.\n"));
472 if (!despool_data(dcr, false)) {
473 Pmsg0(000, _("Bad return from despool in write_block.\n"));
476 /* Despooling cleared these variables so reset them */
477 P(dcr->dev->spool_mutex);
478 dcr->job_spool_size += hlen + wlen;
479 dcr->dev->spool_size += hlen + wlen;
480 V(dcr->dev->spool_mutex);
481 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
485 if (!write_spool_header(dcr)) {
488 if (!write_spool_data(dcr)) {
492 Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
497 static bool write_spool_header(DCR *dcr)
501 DEV_BLOCK *block = dcr->block;
503 hdr.FirstIndex = block->FirstIndex;
504 hdr.LastIndex = block->LastIndex;
505 hdr.len = block->binbuf;
508 for (int retry=0; retry<=1; retry++) {
509 stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
512 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
515 if (stat != (ssize_t)sizeof(hdr)) {
516 Jmsg(dcr->jcr, M_ERROR, 0, _("Error writing header to spool file."
517 " Disk probably full. Attempting recovery. Wanted to write=%d got=%d\n"),
518 (int)stat, (int)sizeof(hdr));
519 /* If we wrote something, truncate it, then despool */
521 #if defined(HAVE_WIN32)
522 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
524 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
526 if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
528 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
530 /* Note, try continuing despite ftruncate problem */
533 if (!despool_data(dcr, false)) {
534 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
537 continue; /* try again */
541 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
545 static bool write_spool_data(DCR *dcr)
548 DEV_BLOCK *block = dcr->block;
551 for (int retry=0; retry<=1; retry++) {
552 stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
555 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
558 if (stat != (ssize_t)block->binbuf) {
560 * If we wrote something, truncate it and the header, then despool
563 #if defined(HAVE_WIN32)
564 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
566 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
568 if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
570 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
572 /* Note, try continuing despite ftruncate problem */
575 if (!despool_data(dcr, false)) {
576 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
579 if (!write_spool_header(dcr)) {
582 continue; /* try again */
586 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
592 bool are_attributes_spooled(JCR *jcr)
594 return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
598 * Create spool file for attributes.
599 * This is done by "attaching" to the bsock, and when
600 * it is called, the output is written to a file.
601 * The actual spooling is turned on and off in
602 * append.c only during writing of the attributes.
604 bool begin_attribute_spool(JCR *jcr)
606 if (!jcr->no_attributes && jcr->spool_attributes) {
607 return open_attr_spool_file(jcr, jcr->dir_bsock);
612 bool discard_attribute_spool(JCR *jcr)
614 if (are_attributes_spooled(jcr)) {
615 return close_attr_spool_file(jcr, jcr->dir_bsock);
620 static void update_attr_spool_size(ssize_t size)
624 if ((spool_stats.attr_size - size) > 0) {
625 spool_stats.attr_size -= size;
627 spool_stats.attr_size = 0;
633 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
635 Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
640 * Tell Director where to find the attributes spool file
641 * Note, if we are not on the same machine, the Director will
642 * return an error, and the higher level routine will transmit
643 * the data record by record -- using bsock->despool().
645 static bool blast_attr_spool_file(JCR *jcr, boffset_t size)
647 /* send full spool file name */
648 POOLMEM *name = get_pool_memory(PM_MESSAGE);
649 make_unique_spool_filename(jcr, &name, jcr->dir_bsock->m_fd);
651 jcr->dir_bsock->fsend("BlastAttr Job=%s File=%s\n", jcr->Job, name);
652 free_pool_memory(name);
654 if (jcr->dir_bsock->recv() <= 0) {
655 Jmsg(jcr, M_FATAL, 0, _("Network error on BlastAttributes.\n"));
659 if (!bstrcmp(jcr->dir_bsock->msg, "1000 OK BlastAttr\n")) {
665 bool commit_attribute_spool(JCR *jcr)
671 Dmsg1(100, "Commit attributes at %s\n", bstrftimes(tbuf, sizeof(tbuf),
672 (utime_t)time(NULL)));
673 if (are_attributes_spooled(jcr)) {
674 if (fseeko(jcr->dir_bsock->m_spool_fd, 0, SEEK_END) != 0) {
676 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
680 size = ftello(jcr->dir_bsock->m_spool_fd);
683 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
688 if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
689 spool_stats.max_attr_size = spool_stats.attr_size + size;
691 spool_stats.attr_size += size;
693 set_jcr_job_status(jcr, JS_AttrDespooling);
694 dir_send_job_status(jcr);
695 Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
696 edit_uint64_with_commas(size, ec1));
698 if (!blast_attr_spool_file(jcr, size)) {
699 /* Can't read spool file from director side,
700 * send content over network.
702 jcr->dir_bsock->despool(update_attr_spool_size, size);
704 return close_attr_spool_file(jcr, jcr->dir_bsock);
709 close_attr_spool_file(jcr, jcr->dir_bsock);
713 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
715 POOLMEM *name = get_pool_memory(PM_MESSAGE);
717 make_unique_spool_filename(jcr, &name, bs->m_fd);
718 bs->m_spool_fd = fopen(name, "w+b");
719 if (!bs->m_spool_fd) {
721 Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
723 free_pool_memory(name);
727 spool_stats.attr_jobs++;
729 free_pool_memory(name);
733 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
739 Dmsg1(100, "Close attr spool file at %s\n", bstrftimes(tbuf, sizeof(tbuf),
740 (utime_t)time(NULL)));
741 if (!bs->m_spool_fd) {
744 name = get_pool_memory(PM_MESSAGE);
746 spool_stats.attr_jobs--;
747 spool_stats.total_attr_jobs++;
749 make_unique_spool_filename(jcr, &name, bs->m_fd);
750 fclose(bs->m_spool_fd);
752 free_pool_memory(name);
753 bs->m_spool_fd = NULL;
754 bs->clear_spooling();