2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2007 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 two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
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 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 John Walker.
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
39 /* Forward referenced subroutines */
40 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name);
41 static bool open_data_spool_file(DCR *dcr);
42 static bool close_data_spool_file(DCR *dcr);
43 static bool despool_data(DCR *dcr, bool commit);
44 static int read_block_from_spool_file(DCR *dcr);
45 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs);
46 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs);
47 static bool write_spool_header(DCR *dcr);
48 static bool write_spool_data(DCR *dcr);
50 struct spool_stats_t {
51 uint32_t data_jobs; /* current jobs spooling data */
53 uint32_t total_data_jobs; /* total jobs to have spooled data */
54 uint32_t total_attr_jobs;
55 int64_t max_data_size; /* max data size */
56 int64_t max_attr_size;
57 int64_t data_size; /* current data size (all jobs running) */
61 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
62 spool_stats_t spool_stats;
65 * Header for data spool record */
67 int32_t FirstIndex; /* FirstIndex for buffer */
68 int32_t LastIndex; /* LastIndex for buffer */
69 uint32_t len; /* length of next buffer */
78 void list_spool_stats(void sendit(const char *msg, int len, void *sarg), void *arg)
80 char ed1[30], ed2[30];
81 POOL_MEM msg(PM_MESSAGE);
84 len = Mmsg(msg, _("Spooling statistics:\n"));
86 if (spool_stats.data_jobs || spool_stats.max_data_size) {
87 len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
88 spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
89 spool_stats.total_data_jobs,
90 edit_uint64_with_commas(spool_stats.max_data_size, ed2));
92 sendit(msg.c_str(), len, arg);
94 if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
95 len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
96 spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
97 spool_stats.total_attr_jobs,
98 edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
100 sendit(msg.c_str(), len, arg);
102 len = Mmsg(msg, "====\n");
103 sendit(msg.c_str(), len, arg);
106 bool begin_data_spool(DCR *dcr)
109 if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
110 Dmsg0(100, "Turning on data spooling\n");
111 dcr->spool_data = true;
112 stat = open_data_spool_file(dcr);
114 dcr->spooling = true;
115 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
117 spool_stats.data_jobs++;
124 bool discard_data_spool(DCR *dcr)
127 Dmsg0(100, "Data spooling discarded\n");
128 return close_data_spool_file(dcr);
133 bool commit_data_spool(DCR *dcr)
138 Dmsg0(100, "Committing spooled data\n");
139 stat = despool_data(dcr, true /*commit*/);
141 Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
142 close_data_spool_file(dcr);
145 return close_data_spool_file(dcr);
150 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
153 if (dcr->dev->device->spool_directory) {
154 dir = dcr->dev->device->spool_directory;
156 dir = working_directory;
158 Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
159 dcr->jcr->Job, dcr->device->hdr.name);
163 static bool open_data_spool_file(DCR *dcr)
165 POOLMEM *name = get_pool_memory(PM_MESSAGE);
168 make_unique_data_spool_filename(dcr, &name);
169 if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
170 dcr->spool_fd = spool_fd;
171 dcr->jcr->spool_attributes = true;
174 Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
176 free_pool_memory(name);
179 Dmsg1(100, "Created spool file: %s\n", name);
180 free_pool_memory(name);
184 static bool close_data_spool_file(DCR *dcr)
186 POOLMEM *name = get_pool_memory(PM_MESSAGE);
189 spool_stats.data_jobs--;
190 spool_stats.total_data_jobs++;
191 if (spool_stats.data_size < dcr->job_spool_size) {
192 spool_stats.data_size = 0;
194 spool_stats.data_size -= dcr->job_spool_size;
196 dcr->job_spool_size = 0;
199 make_unique_data_spool_filename(dcr, &name);
200 close(dcr->spool_fd);
202 dcr->spooling = false;
204 Dmsg1(100, "Deleted spool file: %s\n", name);
205 free_pool_memory(name);
209 static const char *spool_name = "*spool*";
212 * NB! This routine locks the device, but if committing will
213 * not unlock it. If not committing, it will be unlocked.
215 static bool despool_data(DCR *dcr, bool commit)
225 Dmsg0(100, "Despooling data\n");
227 * Commit means that the job is done, so we commit, otherwise, we
228 * are despooling because of user spool size max or some error
229 * (e.g. filesystem full).
232 Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
233 jcr->dcr->VolumeName,
234 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
236 Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
237 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
239 dcr->despool_wait = true;
240 dcr->spooling = false;
242 dcr->despool_wait = false;
243 dcr->despooling = true;
244 dcr->dev_locked = true;
247 * This is really quite kludgy and should be fixed some time.
248 * We create a dev structure to read from the spool file
251 rdev = (DEVICE *)malloc(sizeof(DEVICE));
252 memset(rdev, 0, sizeof(DEVICE));
253 rdev->dev_name = get_memory(strlen(spool_name)+1);
254 bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
255 rdev->errmsg = get_pool_memory(PM_EMSG);
257 rdev->max_block_size = dcr->dev->max_block_size;
258 rdev->min_block_size = dcr->dev->min_block_size;
259 rdev->device = dcr->dev->device;
260 rdcr = new_dcr(jcr, rdev);
261 rdcr->spool_fd = dcr->spool_fd;
262 rdcr->jcr = jcr; /* set a valid jcr */
263 block = dcr->block; /* save block */
264 dcr->block = rdcr->block; /* make read and write block the same */
266 Dmsg1(800, "read/write block size = %d\n", block->buf_len);
267 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
269 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
270 posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
273 /* Add run time, to get current wait time */
274 time_t despool_start = time(NULL) - jcr->run_time;
277 if (job_canceled(jcr)) {
281 stat = read_block_from_spool_file(rdcr);
282 if (stat == RB_EOT) {
284 } else if (stat == RB_ERROR) {
288 ok = write_block_to_device(dcr);
290 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
291 dcr->dev->print_name(), dcr->dev->bstrerror());
293 Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
296 /* Subtracting run_time give us elapsed time - wait_time since we started despooling */
297 time_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
299 if (despool_elapsed <= 0) {
303 Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
304 despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
305 edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
307 dcr->block = block; /* reset block */
309 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
310 if (ftruncate(rdcr->spool_fd, 0) != 0) {
312 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
314 /* Note, try continuing despite ftruncate problem */
318 if (spool_stats.data_size < dcr->job_spool_size) {
319 spool_stats.data_size = 0;
321 spool_stats.data_size -= dcr->job_spool_size;
324 P(dcr->dev->spool_mutex);
325 dcr->dev->spool_size -= dcr->job_spool_size;
326 dcr->job_spool_size = 0; /* zap size in input dcr */
327 V(dcr->dev->spool_mutex);
328 free_memory(rdev->dev_name);
329 free_pool_memory(rdev->errmsg);
330 /* Be careful to NULL the jcr and free rdev after free_dcr() */
335 dcr->spooling = true; /* turn on spooling again */
336 dcr->despooling = false;
337 /* If doing a commit, leave the device locked -- unlocked in release_device() */
339 dcr->dev_locked = false;
346 * Read a block from the spool file
348 * Returns RB_OK on success
349 * RB_EOT when file done
352 static int read_block_from_spool_file(DCR *dcr)
357 DEV_BLOCK *block = dcr->block;
360 stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
362 Dmsg0(100, "EOT on spool read.\n");
364 } else if (stat != (ssize_t)rlen) {
367 Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
370 Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
371 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
376 if (rlen > block->buf_len) {
377 Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
378 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
381 stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
382 if (stat != (ssize_t)rlen) {
383 Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
384 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
387 /* Setup write pointers */
388 block->binbuf = rlen;
389 block->bufp = block->buf + block->binbuf;
390 block->FirstIndex = hdr.FirstIndex;
391 block->LastIndex = hdr.LastIndex;
392 block->VolSessionId = dcr->jcr->VolSessionId;
393 block->VolSessionTime = dcr->jcr->VolSessionTime;
394 Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
399 * Write a block to the spool file
401 * Returns: true on success or EOT
402 * false on hard error
404 bool write_block_to_spool_file(DCR *dcr)
406 uint32_t wlen, hlen; /* length to write */
407 bool despool = false;
408 DEV_BLOCK *block = dcr->block;
410 ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
411 if (block->binbuf <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
415 hlen = sizeof(spool_hdr);
416 wlen = block->binbuf;
417 P(dcr->dev->spool_mutex);
418 dcr->job_spool_size += hlen + wlen;
419 dcr->dev->spool_size += hlen + wlen;
420 if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
421 (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
424 V(dcr->dev->spool_mutex);
426 spool_stats.data_size += hlen + wlen;
427 if (spool_stats.data_size > spool_stats.max_data_size) {
428 spool_stats.max_data_size = spool_stats.data_size;
433 char ec1[30], ec2[30], ec3[30], ec4[30];
434 Dmsg4(100, "Despool in write_block_to_spool_file max_size=%s size=%s "
435 "max_job_size=%s job_size=%s\n",
436 edit_uint64_with_commas(dcr->max_job_spool_size, ec1),
437 edit_uint64_with_commas(dcr->job_spool_size, ec2),
438 edit_uint64_with_commas(dcr->dev->max_spool_size, ec3),
439 edit_uint64_with_commas(dcr->dev->spool_size, ec4));
441 Jmsg(dcr->jcr, M_INFO, 0, _("User specified spool size reached.\n"));
442 if (!despool_data(dcr, false)) {
443 Pmsg0(000, _("Bad return from despool in write_block.\n"));
446 /* Despooling cleared these variables so reset them */
447 P(dcr->dev->spool_mutex);
448 dcr->job_spool_size += hlen + wlen;
449 dcr->dev->spool_size += hlen + wlen;
450 V(dcr->dev->spool_mutex);
451 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
455 if (!write_spool_header(dcr)) {
458 if (!write_spool_data(dcr)) {
462 Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
467 static bool write_spool_header(DCR *dcr)
471 DEV_BLOCK *block = dcr->block;
473 hdr.FirstIndex = block->FirstIndex;
474 hdr.LastIndex = block->LastIndex;
475 hdr.len = block->binbuf;
478 for (int retry=0; retry<=1; retry++) {
479 stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
482 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
485 if (stat != (ssize_t)sizeof(hdr)) {
486 /* If we wrote something, truncate it, then despool */
488 #if defined(HAVE_WIN32)
489 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
491 boffset_t pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
493 if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
495 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
497 /* Note, try continuing despite ftruncate problem */
500 if (!despool_data(dcr, false)) {
501 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
504 continue; /* try again */
508 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
512 static bool write_spool_data(DCR *dcr)
515 DEV_BLOCK *block = dcr->block;
518 for (int retry=0; retry<=1; retry++) {
519 stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
522 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
525 if (stat != (ssize_t)block->binbuf) {
527 * If we wrote something, truncate it and the header, then despool
530 #if defined(HAVE_WIN32)
531 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
533 boffset_t pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
535 if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
537 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
539 /* Note, try continuing despite ftruncate problem */
542 if (!despool_data(dcr, false)) {
543 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
546 if (!write_spool_header(dcr)) {
549 continue; /* try again */
553 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
559 bool are_attributes_spooled(JCR *jcr)
561 return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
565 * Create spool file for attributes.
566 * This is done by "attaching" to the bsock, and when
567 * it is called, the output is written to a file.
568 * The actual spooling is turned on and off in
569 * append.c only during writing of the attributes.
571 bool begin_attribute_spool(JCR *jcr)
573 if (!jcr->no_attributes && jcr->spool_attributes) {
574 return open_attr_spool_file(jcr, jcr->dir_bsock);
579 bool discard_attribute_spool(JCR *jcr)
581 if (are_attributes_spooled(jcr)) {
582 return close_attr_spool_file(jcr, jcr->dir_bsock);
587 static void update_attr_spool_size(ssize_t size)
591 if ((spool_stats.attr_size - size) > 0) {
592 spool_stats.attr_size -= size;
594 spool_stats.attr_size = 0;
600 bool commit_attribute_spool(JCR *jcr)
605 if (are_attributes_spooled(jcr)) {
606 if (fseeko(jcr->dir_bsock->m_spool_fd, 0, SEEK_END) != 0) {
608 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
612 size = ftello(jcr->dir_bsock->m_spool_fd);
615 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
620 if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
621 spool_stats.max_attr_size = spool_stats.attr_size + size;
623 spool_stats.attr_size += size;
625 Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
626 edit_uint64_with_commas(size, ec1));
627 jcr->dir_bsock->despool(update_attr_spool_size, size);
628 return close_attr_spool_file(jcr, jcr->dir_bsock);
633 close_attr_spool_file(jcr, jcr->dir_bsock);
637 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
639 Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
644 bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
646 POOLMEM *name = get_pool_memory(PM_MESSAGE);
648 make_unique_spool_filename(jcr, &name, bs->m_fd);
649 bs->m_spool_fd = fopen(name, "w+b");
650 if (!bs->m_spool_fd) {
652 Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
654 free_pool_memory(name);
658 spool_stats.attr_jobs++;
660 free_pool_memory(name);
664 bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
668 if (!bs->m_spool_fd) {
671 name = get_pool_memory(PM_MESSAGE);
673 spool_stats.attr_jobs--;
674 spool_stats.total_attr_jobs++;
676 make_unique_spool_filename(jcr, &name, bs->m_fd);
677 fclose(bs->m_spool_fd);
679 free_pool_memory(name);
680 bs->m_spool_fd = NULL;