2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2004-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
23 * Kern Sibbald, March 2004
30 /* Forward referenced subroutines */
31 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name);
32 static bool open_data_spool_file(DCR *dcr);
33 static bool close_data_spool_file(DCR *dcr);
34 static bool despool_data(DCR *dcr, bool commit);
35 static int read_block_from_spool_file(DCR *dcr);
36 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs);
37 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs);
38 static bool write_spool_header(DCR *dcr);
39 static bool write_spool_data(DCR *dcr);
41 struct spool_stats_t {
42 uint32_t data_jobs; /* current jobs spooling data */
44 uint32_t total_data_jobs; /* total jobs to have spooled data */
45 uint32_t total_attr_jobs;
46 int64_t max_data_size; /* max data size */
47 int64_t max_attr_size;
48 int64_t data_size; /* current data size (all jobs running) */
52 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
53 spool_stats_t spool_stats;
56 * Header for data spool record */
58 int32_t FirstIndex; /* FirstIndex for buffer */
59 int32_t LastIndex; /* LastIndex for buffer */
60 uint32_t len; /* length of next buffer */
69 void list_spool_stats(void sendit(const char *msg, int len, void *sarg), void *arg)
71 char ed1[30], ed2[30];
72 POOL_MEM msg(PM_MESSAGE);
75 len = Mmsg(msg, _("Spooling statistics:\n"));
77 if (spool_stats.data_jobs || spool_stats.max_data_size) {
78 len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
79 spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
80 spool_stats.total_data_jobs,
81 edit_uint64_with_commas(spool_stats.max_data_size, ed2));
83 sendit(msg.c_str(), len, arg);
85 if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
86 len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
87 spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
88 spool_stats.total_attr_jobs,
89 edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
91 sendit(msg.c_str(), len, arg);
95 bool begin_data_spool(DCR *dcr)
98 if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
99 Dmsg0(100, "Turning on data spooling\n");
100 dcr->spool_data = true;
101 stat = open_data_spool_file(dcr);
103 dcr->spooling = true;
104 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
106 spool_stats.data_jobs++;
113 bool discard_data_spool(DCR *dcr)
116 Dmsg0(100, "Data spooling discarded\n");
117 return close_data_spool_file(dcr);
122 bool commit_data_spool(DCR *dcr)
127 Dmsg0(100, "Committing spooled data\n");
128 stat = despool_data(dcr, true /*commit*/);
130 Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
131 close_data_spool_file(dcr);
134 return close_data_spool_file(dcr);
139 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
142 if (dcr->dev->device->spool_directory) {
143 dir = dcr->dev->device->spool_directory;
145 dir = working_directory;
147 Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
148 dcr->jcr->Job, dcr->device->hdr.name);
152 static bool open_data_spool_file(DCR *dcr)
154 POOLMEM *name = get_pool_memory(PM_MESSAGE);
157 make_unique_data_spool_filename(dcr, &name);
158 if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
159 dcr->spool_fd = spool_fd;
160 dcr->jcr->spool_attributes = true;
163 Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
165 free_pool_memory(name);
168 Dmsg1(100, "Created spool file: %s\n", name);
169 free_pool_memory(name);
173 static const char *spool_name = "*spool*";
176 * NB! This routine locks the device, but if committing will
177 * not unlock it. If not committing, it will be unlocked.
179 static bool despool_data(DCR *dcr, bool commit)
189 Dmsg0(100, "Despooling data\n");
190 if (jcr->dcr->job_spool_size == 0) {
191 Jmsg(jcr, M_WARNING, 0, _("Despooling zero bytes. Your disk is probably FULL!\n"));
195 * Commit means that the job is done, so we commit, otherwise, we
196 * are despooling because of user spool size max or some error
197 * (e.g. filesystem full).
200 Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
201 jcr->dcr->VolumeName,
202 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
203 jcr->setJobStatus(JS_DataCommitting);
205 Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
206 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
207 jcr->setJobStatus(JS_DataDespooling);
209 jcr->sendJobStatus(JS_DataDespooling);
210 dcr->despool_wait = true;
211 dcr->spooling = false;
213 * We work with device blocked, but not locked so that
214 * other threads -- e.g. reservations can lock the device
217 dcr->dblock(BST_DESPOOLING);
218 dcr->despool_wait = false;
219 dcr->despooling = true;
222 * This is really quite kludgy and should be fixed some time.
223 * We create a dev structure to read from the spool file
226 rdev = (DEVICE *)malloc(sizeof(DEVICE));
227 memset(rdev, 0, sizeof(DEVICE));
228 rdev->dev_name = get_memory(strlen(spool_name)+1);
229 bstrncpy(rdev->dev_name, spool_name, strlen(spool_name)+1);
230 rdev->errmsg = get_pool_memory(PM_EMSG);
232 rdev->max_block_size = dcr->dev->max_block_size;
233 rdev->min_block_size = dcr->dev->min_block_size;
234 rdev->device = dcr->dev->device;
235 rdcr = new_dcr(jcr, NULL, rdev, SD_READ);
236 rdcr->spool_fd = dcr->spool_fd;
237 block = dcr->block; /* save block */
238 dcr->block = rdcr->block; /* make read and write block the same */
240 Dmsg1(800, "read/write block size = %d\n", block->buf_len);
241 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
243 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
244 posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
247 /* Add run time, to get current wait time */
248 int32_t despool_start = time(NULL) - jcr->run_time;
250 set_new_file_parameters(dcr);
253 if (job_canceled(jcr)) {
257 stat = read_block_from_spool_file(rdcr);
258 if (stat == RB_EOT) {
260 } else if (stat == RB_ERROR) {
264 ok = dcr->write_block_to_device();
266 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
267 dcr->dev->print_name(), dcr->dev->bstrerror());
268 Pmsg2(000, "Fatal append error on device %s: ERR=%s\n",
269 dcr->dev->print_name(), dcr->dev->bstrerror());
270 /* Force in case Incomplete set */
271 jcr->forceJobStatus(JS_FatalError);
273 Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
276 if (!dir_create_jobmedia_record(dcr)) {
277 Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
278 dcr->getVolCatName(), jcr->Job);
279 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
281 flush_jobmedia_queue(jcr);
282 /* Set new file/block parameters for current dcr */
283 set_new_file_parameters(dcr);
286 * Subtracting run_time give us elapsed time - wait_time since
287 * we started despooling. Note, don't use time_t as it is 32 or 64
288 * bits depending on the OS and doesn't edit with %d
290 int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
292 if (despool_elapsed <= 0) {
296 Jmsg(jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
297 despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
298 edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
300 dcr->block = block; /* reset block */
302 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
303 if (ftruncate(rdcr->spool_fd, 0) != 0) {
305 Jmsg(jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
307 /* Note, try continuing despite ftruncate problem */
311 if (spool_stats.data_size < dcr->job_spool_size) {
312 spool_stats.data_size = 0;
314 spool_stats.data_size -= dcr->job_spool_size;
317 P(dcr->dev->spool_mutex);
318 dcr->dev->spool_size -= dcr->job_spool_size;
319 dcr->job_spool_size = 0; /* zap size in input dcr */
320 V(dcr->dev->spool_mutex);
321 free_memory(rdev->dev_name);
322 free_pool_memory(rdev->errmsg);
323 /* Be careful to NULL the jcr and free rdev after free_dcr() */
328 dcr->spooling = true; /* turn on spooling again */
329 dcr->despooling = false;
331 * Note, if committing we leave the device blocked. It will be removed in
335 dcr->dev->dunblock();
337 jcr->sendJobStatus(JS_Running);
342 * Read a block from the spool file
344 * Returns RB_OK on success
345 * RB_EOT when file done
348 static int read_block_from_spool_file(DCR *dcr)
353 DEV_BLOCK *block = dcr->block;
357 stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
359 Dmsg0(100, "EOT on spool read.\n");
361 } else if (stat != (ssize_t)rlen) {
364 Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
367 Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
368 Jmsg2(jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
370 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
374 if (rlen > block->buf_len) {
375 Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
376 Jmsg2(jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
377 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
380 stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
381 if (stat != (ssize_t)rlen) {
382 Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
383 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
384 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
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 if (job_canceled(dcr->jcr)) {
413 ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
414 if (block->binbuf <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
418 hlen = sizeof(spool_hdr);
419 wlen = block->binbuf;
420 P(dcr->dev->spool_mutex);
421 dcr->job_spool_size += hlen + wlen;
422 dcr->dev->spool_size += hlen + wlen;
423 if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
424 (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
427 V(dcr->dev->spool_mutex);
429 spool_stats.data_size += hlen + wlen;
430 if (spool_stats.data_size > spool_stats.max_data_size) {
431 spool_stats.max_data_size = spool_stats.data_size;
435 char ec1[30], ec2[30];
436 if (dcr->max_job_spool_size > 0) {
437 Jmsg(dcr->jcr, M_INFO, 0, _("User specified Job spool size reached: "
438 "JobSpoolSize=%s MaxJobSpoolSize=%s\n"),
439 edit_uint64_with_commas(dcr->job_spool_size, ec1),
440 edit_uint64_with_commas(dcr->max_job_spool_size, ec2));
442 Jmsg(dcr->jcr, M_INFO, 0, _("User specified Device spool size reached: "
443 "DevSpoolSize=%s MaxDevSpoolSize=%s\n"),
444 edit_uint64_with_commas(dcr->dev->spool_size, ec1),
445 edit_uint64_with_commas(dcr->dev->max_spool_size, ec2));
448 if (!despool_data(dcr, false)) {
449 Pmsg0(000, _("Bad return from despool in write_block.\n"));
452 /* Despooling cleared these variables so reset them */
453 P(dcr->dev->spool_mutex);
454 dcr->job_spool_size += hlen + wlen;
455 dcr->dev->spool_size += hlen + wlen;
456 V(dcr->dev->spool_mutex);
457 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
461 if (!write_spool_header(dcr)) {
464 if (!write_spool_data(dcr)) {
468 Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
473 static bool write_spool_header(DCR *dcr)
477 DEV_BLOCK *block = dcr->block;
480 hdr.FirstIndex = block->FirstIndex;
481 hdr.LastIndex = block->LastIndex;
482 hdr.len = block->binbuf;
485 for (int retry=0; retry<=1; retry++) {
486 stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
489 Jmsg(jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
491 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
493 if (stat != (ssize_t)sizeof(hdr)) {
494 Jmsg(jcr, M_ERROR, 0, _("Error writing header to spool file."
495 " Disk probably full. Attempting recovery. Wanted to write=%d got=%d\n"),
496 (int)stat, (int)sizeof(hdr));
497 /* If we wrote something, truncate it, then despool */
499 #if defined(HAVE_WIN32)
500 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
502 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
504 if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
506 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
508 /* Note, try continuing despite ftruncate problem */
511 if (!despool_data(dcr, false)) {
512 Jmsg(jcr, M_FATAL, 0, _("Fatal despooling error."));
513 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
516 continue; /* try again */
520 Jmsg(jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
521 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
525 static bool write_spool_data(DCR *dcr)
528 DEV_BLOCK *block = dcr->block;
532 for (int retry=0; retry<=1; retry++) {
533 stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
536 Jmsg(jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
538 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
540 if (stat != (ssize_t)block->binbuf) {
542 * If we wrote something, truncate it and the header, then despool
545 #if defined(HAVE_WIN32)
546 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
548 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
550 if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
552 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
554 /* Note, try continuing despite ftruncate problem */
557 if (!despool_data(dcr, false)) {
558 Jmsg(jcr, M_FATAL, 0, _("Fatal despooling error."));
559 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
562 if (!write_spool_header(dcr)) {
565 continue; /* try again */
569 Jmsg(jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
570 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
574 static bool close_data_spool_file(DCR *dcr)
576 POOLMEM *name = get_pool_memory(PM_MESSAGE);
579 spool_stats.data_jobs--;
580 spool_stats.total_data_jobs++;
581 if (spool_stats.data_size < dcr->job_spool_size) {
582 spool_stats.data_size = 0;
584 spool_stats.data_size -= dcr->job_spool_size;
587 P(dcr->dev->spool_mutex);
588 dcr->job_spool_size = 0;
589 V(dcr->dev->spool_mutex);
591 make_unique_data_spool_filename(dcr, &name);
592 close(dcr->spool_fd);
594 dcr->spooling = false;
596 Dmsg1(100, "Deleted spool file: %s\n", name);
597 free_pool_memory(name);
601 bool are_attributes_spooled(JCR *jcr)
603 return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
607 * Create spool file for attributes.
608 * This is done by "attaching" to the bsock, and when
609 * it is called, the output is written to a file.
610 * The actual spooling is turned on and off in
611 * append.c only during writing of the attributes.
613 bool begin_attribute_spool(JCR *jcr)
615 if (!jcr->no_attributes && jcr->spool_attributes) {
616 return open_attr_spool_file(jcr, jcr->dir_bsock);
621 static void update_attr_spool_size(ssize_t size)
625 if ((spool_stats.attr_size - size) > 0) {
626 spool_stats.attr_size -= size;
628 spool_stats.attr_size = 0;
634 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
636 Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
641 * Tell Director where to find the attributes spool file
642 * Note, if we are not on the same machine, the Director will
643 * return an error, and the higher level routine will transmit
644 * the data record by record -- using bsock->despool().
646 static bool blast_attr_spool_file(JCR *jcr, boffset_t size)
648 /* send full spool file name */
649 POOLMEM *name = get_pool_memory(PM_MESSAGE);
650 make_unique_spool_filename(jcr, &name, jcr->dir_bsock->m_fd);
652 jcr->dir_bsock->fsend("BlastAttr Job=%s File=%s\n", jcr->Job, name);
653 free_pool_memory(name);
655 if (jcr->dir_bsock->recv() <= 0) {
656 Jmsg(jcr, M_FATAL, 0, _("Network error on BlastAttributes.\n"));
657 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
661 if (!bstrcmp(jcr->dir_bsock->msg, "1000 OK BlastAttr\n")) {
667 bool commit_attribute_spool(JCR *jcr)
669 boffset_t size, data_end;
674 Dmsg1(100, "Commit attributes at %s\n", bstrftimes(tbuf, sizeof(tbuf),
675 (utime_t)time(NULL)));
676 if (are_attributes_spooled(jcr)) {
677 dir = jcr->dir_bsock;
678 if (fseeko(dir->m_spool_fd, 0, SEEK_END) != 0) {
680 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
682 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
685 size = ftello(dir->m_spool_fd);
686 /* For Incomplete Job truncate spool file to last valid data_end if necssary */
687 if (jcr->is_JobStatus(JS_Incomplete)) {
688 data_end = dir->get_last_data_end();
689 if (size > data_end) {
690 if (ftruncate(fileno(dir->m_spool_fd), data_end) != 0) {
692 Jmsg(jcr, M_FATAL, 0, _("Truncate on attributes file failed: ERR=%s\n"),
694 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
697 Dmsg2(100, "=== Attrib spool truncated from %lld to %lld\n",
704 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
706 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
710 if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
711 spool_stats.max_attr_size = spool_stats.attr_size + size;
713 spool_stats.attr_size += size;
715 jcr->sendJobStatus(JS_AttrDespooling);
716 Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
717 edit_uint64_with_commas(size, ec1));
719 if (!blast_attr_spool_file(jcr, size)) {
720 /* Can't read spool file from director side,
721 * send content over network.
723 dir->despool(update_attr_spool_size, size);
725 return close_attr_spool_file(jcr, dir);
730 close_attr_spool_file(jcr, dir);
734 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
736 POOLMEM *name = get_pool_memory(PM_MESSAGE);
738 make_unique_spool_filename(jcr, &name, bs->m_fd);
739 bs->m_spool_fd = fopen(name, "w+b");
740 if (!bs->m_spool_fd) {
742 Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
744 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
745 free_pool_memory(name);
749 spool_stats.attr_jobs++;
751 free_pool_memory(name);
755 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
761 Dmsg1(100, "Close attr spool file at %s\n", bstrftimes(tbuf, sizeof(tbuf),
762 (utime_t)time(NULL)));
763 if (!bs->m_spool_fd) {
766 name = get_pool_memory(PM_MESSAGE);
768 spool_stats.attr_jobs--;
769 spool_stats.total_attr_jobs++;
771 make_unique_spool_filename(jcr, &name, bs->m_fd);
772 fclose(bs->m_spool_fd);
774 free_pool_memory(name);
775 bs->m_spool_fd = NULL;
776 bs->clear_spooling();
780 bool discard_attribute_spool(JCR *jcr)
782 if (are_attributes_spooled(jcr)) {
783 return close_attr_spool_file(jcr, jcr->dir_bsock);