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 if (spool_stats.data_jobs || spool_stats.max_data_size) {
85 len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
86 spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
87 spool_stats.total_data_jobs,
88 edit_uint64_with_commas(spool_stats.max_data_size, ed2));
90 sendit(msg.c_str(), len, arg);
92 if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
93 len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
94 spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
95 spool_stats.total_attr_jobs,
96 edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
98 sendit(msg.c_str(), len, arg);
102 bool begin_data_spool(DCR *dcr)
105 if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
106 Dmsg0(100, "Turning on data spooling\n");
107 dcr->spool_data = true;
108 stat = open_data_spool_file(dcr);
110 dcr->spooling = true;
111 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
113 spool_stats.data_jobs++;
120 bool discard_data_spool(DCR *dcr)
123 Dmsg0(100, "Data spooling discarded\n");
124 return close_data_spool_file(dcr);
129 bool commit_data_spool(DCR *dcr)
134 Dmsg0(100, "Committing spooled data\n");
135 stat = despool_data(dcr, true /*commit*/);
137 Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
138 close_data_spool_file(dcr);
141 return close_data_spool_file(dcr);
146 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
149 if (dcr->dev->device->spool_directory) {
150 dir = dcr->dev->device->spool_directory;
152 dir = working_directory;
154 Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
155 dcr->jcr->Job, dcr->device->hdr.name);
159 static bool open_data_spool_file(DCR *dcr)
161 POOLMEM *name = get_pool_memory(PM_MESSAGE);
164 make_unique_data_spool_filename(dcr, &name);
165 if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
166 dcr->spool_fd = spool_fd;
167 dcr->jcr->spool_attributes = true;
170 Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
172 free_pool_memory(name);
175 Dmsg1(100, "Created spool file: %s\n", name);
176 free_pool_memory(name);
180 static bool close_data_spool_file(DCR *dcr)
182 POOLMEM *name = get_pool_memory(PM_MESSAGE);
185 spool_stats.data_jobs--;
186 spool_stats.total_data_jobs++;
187 if (spool_stats.data_size < dcr->job_spool_size) {
188 spool_stats.data_size = 0;
190 spool_stats.data_size -= dcr->job_spool_size;
192 dcr->job_spool_size = 0;
195 make_unique_data_spool_filename(dcr, &name);
196 close(dcr->spool_fd);
198 dcr->spooling = false;
200 Dmsg1(100, "Deleted spool file: %s\n", name);
201 free_pool_memory(name);
205 static const char *spool_name = "*spool*";
207 static bool despool_data(DCR *dcr, bool commit)
217 Dmsg0(100, "Despooling data\n");
218 /* Commit means that the job is done, so we commit, otherwise, we
219 * are despooling because of user spool size max or some error
220 * (e.g. filesystem full).
223 Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
224 jcr->dcr->VolumeName,
225 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
227 Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
228 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
230 dcr->despool_wait = true;
231 dcr->spooling = false;
232 lock_device(dcr->dev);
233 dcr->despool_wait = false;
234 dcr->despooling = true;
235 dcr->dev_locked = true;
238 * This is really quite kludgy and should be fixed some time.
239 * We create a dev structure to read from the spool file
242 rdev = (DEVICE *)malloc(sizeof(DEVICE));
243 memset(rdev, 0, sizeof(DEVICE));
244 rdev->dev_name = get_memory(strlen(spool_name)+1);
245 bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
246 rdev->errmsg = get_pool_memory(PM_EMSG);
248 rdev->max_block_size = dcr->dev->max_block_size;
249 rdev->min_block_size = dcr->dev->min_block_size;
250 rdev->device = dcr->dev->device;
251 rdcr = new_dcr(NULL, rdev);
252 rdcr->spool_fd = dcr->spool_fd;
253 rdcr->jcr = jcr; /* set a valid jcr */
254 block = dcr->block; /* save block */
255 dcr->block = rdcr->block; /* make read and write block the same */
257 Dmsg1(800, "read/write block size = %d\n", block->buf_len);
258 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
260 /* Add run time, to get current wait time */
261 time_t despool_start = time(NULL) - jcr->run_time;
264 if (job_canceled(jcr)) {
268 stat = read_block_from_spool_file(rdcr);
269 if (stat == RB_EOT) {
271 } else if (stat == RB_ERROR) {
275 ok = write_block_to_device(dcr);
277 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
278 dcr->dev->print_name(), dcr->dev->bstrerror());
280 Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
283 /* Subtracting run_time give us elapsed time - wait_time since we started despooling */
284 time_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
286 if (despool_elapsed <= 0) {
290 Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
291 despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
292 edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
294 dcr->block = block; /* reset block */
296 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
297 if (ftruncate(rdcr->spool_fd, 0) != 0) {
299 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
301 Pmsg1(000, _("Bad return from ftruncate. ERR=%s\n"), be.strerror());
306 if (spool_stats.data_size < dcr->job_spool_size) {
307 spool_stats.data_size = 0;
309 spool_stats.data_size -= dcr->job_spool_size;
312 P(dcr->dev->spool_mutex);
313 dcr->dev->spool_size -= dcr->job_spool_size;
314 dcr->job_spool_size = 0; /* zap size in input dcr */
315 V(dcr->dev->spool_mutex);
316 free_memory(rdev->dev_name);
317 free_pool_memory(rdev->errmsg);
318 /* Be careful to NULL the jcr and free rdev after free_dcr() */
323 dcr->spooling = true; /* turn on spooling again */
324 dcr->despooling = false;
325 /* If doing a commit, leave the device locked -- unlocked in release_device() */
327 dcr->dev_locked = false;
328 unlock_device(dcr->dev);
334 * Read a block from the spool file
336 * Returns RB_OK on success
337 * RB_EOT when file done
340 static int read_block_from_spool_file(DCR *dcr)
345 DEV_BLOCK *block = dcr->block;
348 stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
350 Dmsg0(100, "EOT on spool read.\n");
352 } else if (stat != (ssize_t)rlen) {
355 Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
358 Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
359 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
364 if (rlen > block->buf_len) {
365 Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
366 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
369 stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
370 if (stat != (ssize_t)rlen) {
371 Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
372 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
375 /* Setup write pointers */
376 block->binbuf = rlen;
377 block->bufp = block->buf + block->binbuf;
378 block->FirstIndex = hdr.FirstIndex;
379 block->LastIndex = hdr.LastIndex;
380 block->VolSessionId = dcr->jcr->VolSessionId;
381 block->VolSessionTime = dcr->jcr->VolSessionTime;
382 Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
387 * Write a block to the spool file
389 * Returns: true on success or EOT
390 * false on hard error
392 bool write_block_to_spool_file(DCR *dcr)
394 uint32_t wlen, hlen; /* length to write */
395 bool despool = false;
396 DEV_BLOCK *block = dcr->block;
398 ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
399 if (block->binbuf <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
403 hlen = sizeof(spool_hdr);
404 wlen = block->binbuf;
405 P(dcr->dev->spool_mutex);
406 dcr->job_spool_size += hlen + wlen;
407 dcr->dev->spool_size += hlen + wlen;
408 if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
409 (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
412 V(dcr->dev->spool_mutex);
414 spool_stats.data_size += hlen + wlen;
415 if (spool_stats.data_size > spool_stats.max_data_size) {
416 spool_stats.max_data_size = spool_stats.data_size;
421 char ec1[30], ec2[30], ec3[30], ec4[30];
422 Dmsg4(100, "Despool in write_block_to_spool_file max_size=%s size=%s "
423 "max_job_size=%s job_size=%s\n",
424 edit_uint64_with_commas(dcr->max_job_spool_size, ec1),
425 edit_uint64_with_commas(dcr->job_spool_size, ec2),
426 edit_uint64_with_commas(dcr->dev->max_spool_size, ec3),
427 edit_uint64_with_commas(dcr->dev->spool_size, ec4));
429 Jmsg(dcr->jcr, M_INFO, 0, _("User specified spool size reached.\n"));
430 if (!despool_data(dcr, false)) {
431 Pmsg0(000, _("Bad return from despool in write_block.\n"));
434 /* Despooling cleared these variables so reset them */
435 P(dcr->dev->spool_mutex);
436 dcr->job_spool_size += hlen + wlen;
437 dcr->dev->spool_size += hlen + wlen;
438 V(dcr->dev->spool_mutex);
439 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
443 if (!write_spool_header(dcr)) {
446 if (!write_spool_data(dcr)) {
450 Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
455 static bool write_spool_header(DCR *dcr)
459 DEV_BLOCK *block = dcr->block;
461 hdr.FirstIndex = block->FirstIndex;
462 hdr.LastIndex = block->LastIndex;
463 hdr.len = block->binbuf;
466 for (int retry=0; retry<=1; retry++) {
467 stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
470 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
473 if (stat != (ssize_t)sizeof(hdr)) {
474 /* If we wrote something, truncate it, then despool */
476 #if defined(HAVE_WIN32)
477 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
479 boffset_t pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
481 if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
483 Jmsg(dcr->jcr, M_FATAL, 0, _("Ftruncate spool file failed: ERR=%s\n"),
488 if (!despool_data(dcr, false)) {
489 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
492 continue; /* try again */
496 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
500 static bool write_spool_data(DCR *dcr)
503 DEV_BLOCK *block = dcr->block;
506 for (int retry=0; retry<=1; retry++) {
507 stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
510 Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
513 if (stat != (ssize_t)block->binbuf) {
515 * If we wrote something, truncate it and the header, then despool
518 #if defined(HAVE_WIN32)
519 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
521 boffset_t pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
523 if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
525 Jmsg(dcr->jcr, M_FATAL, 0, _("Ftruncate spool file failed: ERR=%s\n"),
530 if (!despool_data(dcr, false)) {
531 Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
534 if (!write_spool_header(dcr)) {
537 continue; /* try again */
541 Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
547 bool are_attributes_spooled(JCR *jcr)
549 return jcr->spool_attributes && jcr->dir_bsock->spool_fd;
553 * Create spool file for attributes.
554 * This is done by "attaching" to the bsock, and when
555 * it is called, the output is written to a file.
556 * The actual spooling is turned on and off in
557 * append.c only during writing of the attributes.
559 bool begin_attribute_spool(JCR *jcr)
561 if (!jcr->no_attributes && jcr->spool_attributes) {
562 return open_attr_spool_file(jcr, jcr->dir_bsock);
567 bool discard_attribute_spool(JCR *jcr)
569 if (are_attributes_spooled(jcr)) {
570 return close_attr_spool_file(jcr, jcr->dir_bsock);
575 static void update_attr_spool_size(ssize_t size)
579 if ((spool_stats.attr_size - size) > 0) {
580 spool_stats.attr_size -= size;
582 spool_stats.attr_size = 0;
588 bool commit_attribute_spool(JCR *jcr)
593 if (are_attributes_spooled(jcr)) {
594 if (fseeko(jcr->dir_bsock->spool_fd, 0, SEEK_END) != 0) {
596 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
600 size = ftello(jcr->dir_bsock->spool_fd);
603 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
608 if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
609 spool_stats.max_attr_size = spool_stats.attr_size + size;
611 spool_stats.attr_size += size;
613 Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
614 edit_uint64_with_commas(size, ec1));
615 jcr->dir_bsock->despool(update_attr_spool_size, size);
616 return close_attr_spool_file(jcr, jcr->dir_bsock);
621 close_attr_spool_file(jcr, jcr->dir_bsock);
625 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
627 Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
632 bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
634 POOLMEM *name = get_pool_memory(PM_MESSAGE);
636 make_unique_spool_filename(jcr, &name, bs->fd);
637 bs->spool_fd = fopen(name, "w+b");
640 Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
642 free_pool_memory(name);
646 spool_stats.attr_jobs++;
648 free_pool_memory(name);
652 bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
659 name = get_pool_memory(PM_MESSAGE);
661 spool_stats.attr_jobs--;
662 spool_stats.total_attr_jobs++;
664 make_unique_spool_filename(jcr, &name, bs->fd);
665 fclose(bs->spool_fd);
667 free_pool_memory(name);