2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Kern Sibbald, March 2004
29 /* Forward referenced subroutines */
30 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name);
31 static bool open_data_spool_file(DCR *dcr);
32 static bool close_data_spool_file(DCR *dcr);
33 static bool despool_data(DCR *dcr, bool commit);
34 static int read_block_from_spool_file(DCR *dcr);
35 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs);
36 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs);
37 static bool write_spool_header(DCR *dcr);
38 static bool write_spool_data(DCR *dcr);
40 struct spool_stats_t {
41 uint32_t data_jobs; /* current jobs spooling data */
43 uint32_t total_data_jobs; /* total jobs to have spooled data */
44 uint32_t total_attr_jobs;
45 int64_t max_data_size; /* max data size */
46 int64_t max_attr_size;
47 int64_t data_size; /* current data size (all jobs running) */
51 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
52 spool_stats_t spool_stats;
55 * Header for data spool record */
57 int32_t FirstIndex; /* FirstIndex for buffer */
58 int32_t LastIndex; /* LastIndex for buffer */
59 uint32_t len; /* length of next buffer */
68 void list_spool_stats(void sendit(const char *msg, int len, void *sarg), void *arg)
70 char ed1[30], ed2[30];
71 POOL_MEM msg(PM_MESSAGE);
74 len = Mmsg(msg, _("Spooling statistics:\n"));
76 if (spool_stats.data_jobs || spool_stats.max_data_size) {
77 len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
78 spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
79 spool_stats.total_data_jobs,
80 edit_uint64_with_commas(spool_stats.max_data_size, ed2));
82 sendit(msg.c_str(), len, arg);
84 if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
85 len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
86 spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
87 spool_stats.total_attr_jobs,
88 edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
90 sendit(msg.c_str(), len, arg);
94 bool begin_data_spool(DCR *dcr)
97 if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
98 Dmsg0(100, "Turning on data spooling\n");
99 dcr->spool_data = true;
100 stat = open_data_spool_file(dcr);
102 dcr->spooling = true;
103 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
105 spool_stats.data_jobs++;
112 bool discard_data_spool(DCR *dcr)
115 Dmsg0(100, "Data spooling discarded\n");
116 return close_data_spool_file(dcr);
121 bool commit_data_spool(DCR *dcr)
126 Dmsg0(100, "Committing spooled data\n");
127 stat = despool_data(dcr, true /*commit*/);
129 Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
130 close_data_spool_file(dcr);
133 return close_data_spool_file(dcr);
138 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
141 if (dcr->dev->device->spool_directory) {
142 dir = dcr->dev->device->spool_directory;
144 dir = working_directory;
146 Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
147 dcr->jcr->Job, dcr->device->hdr.name);
151 static bool open_data_spool_file(DCR *dcr)
153 POOLMEM *name = get_pool_memory(PM_MESSAGE);
156 make_unique_data_spool_filename(dcr, &name);
157 if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
158 dcr->spool_fd = spool_fd;
159 dcr->jcr->spool_attributes = true;
162 Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
164 free_pool_memory(name);
167 Dmsg1(100, "Created spool file: %s\n", name);
168 free_pool_memory(name);
172 static const char *spool_name = "*spool*";
175 * NB! This routine locks the device, but if committing will
176 * not unlock it. If not committing, it will be unlocked.
178 static bool despool_data(DCR *dcr, bool commit)
188 Dmsg0(100, "Despooling data\n");
189 if (jcr->dcr->job_spool_size == 0) {
190 Jmsg(jcr, M_WARNING, 0, _("Despooling zero bytes. Your disk is probably FULL!\n"));
194 * Commit means that the job is done, so we commit, otherwise, we
195 * are despooling because of user spool size max or some error
196 * (e.g. filesystem full).
199 Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
200 jcr->dcr->VolumeName,
201 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
202 jcr->setJobStatus(JS_DataCommitting);
204 Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
205 edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
206 jcr->setJobStatus(JS_DataDespooling);
208 jcr->sendJobStatus(JS_DataDespooling);
209 dcr->despool_wait = true;
210 dcr->spooling = false;
212 * We work with device blocked, but not locked so that
213 * other threads -- e.g. reservations can lock the device
216 dcr->dblock(BST_DESPOOLING);
217 dcr->despool_wait = false;
218 dcr->despooling = true;
221 * This is really quite kludgy and should be fixed some time.
222 * We create a dev structure to read from the spool file
225 rdev = (DEVICE *)malloc(sizeof(DEVICE));
226 memset(rdev, 0, sizeof(DEVICE));
227 rdev->dev_name = get_memory(strlen(spool_name)+1);
228 bstrncpy(rdev->dev_name, spool_name, strlen(spool_name)+1);
229 rdev->errmsg = get_pool_memory(PM_EMSG);
231 rdev->max_block_size = dcr->dev->max_block_size;
232 rdev->min_block_size = dcr->dev->min_block_size;
233 rdev->device = dcr->dev->device;
234 rdcr = new_dcr(jcr, NULL, rdev, SD_READ);
235 rdcr->spool_fd = dcr->spool_fd;
236 block = dcr->block; /* save block */
237 dcr->block = rdcr->block; /* make read and write block the same */
239 Dmsg1(800, "read/write block size = %d\n", block->buf_len);
240 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
242 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
243 posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
246 /* Add run time, to get current wait time */
247 int32_t despool_start = time(NULL) - jcr->run_time;
249 set_new_file_parameters(dcr);
252 if (job_canceled(jcr)) {
256 stat = read_block_from_spool_file(rdcr);
257 if (stat == RB_EOT) {
259 } else if (stat == RB_ERROR) {
263 ok = dcr->write_block_to_device();
265 Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
266 dcr->dev->print_name(), dcr->dev->bstrerror());
267 Pmsg2(000, "Fatal append error on device %s: ERR=%s\n",
268 dcr->dev->print_name(), dcr->dev->bstrerror());
269 /* Force in case Incomplete set */
270 jcr->forceJobStatus(JS_FatalError);
272 Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
275 if (!dir_create_jobmedia_record(dcr)) {
276 Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
277 dcr->getVolCatName(), jcr->Job);
278 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
280 flush_jobmedia_queue(jcr);
281 /* Set new file/block parameters for current dcr */
282 set_new_file_parameters(dcr);
285 * Subtracting run_time give us elapsed time - wait_time since
286 * we started despooling. Note, don't use time_t as it is 32 or 64
287 * bits depending on the OS and doesn't edit with %d
289 int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
291 if (despool_elapsed <= 0) {
295 Jmsg(jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
296 despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
297 edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
299 dcr->block = block; /* reset block */
301 lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
302 if (ftruncate(rdcr->spool_fd, 0) != 0) {
304 Jmsg(jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
306 /* Note, try continuing despite ftruncate problem */
310 if (spool_stats.data_size < dcr->job_spool_size) {
311 spool_stats.data_size = 0;
313 spool_stats.data_size -= dcr->job_spool_size;
316 P(dcr->dev->spool_mutex);
317 dcr->dev->spool_size -= dcr->job_spool_size;
318 dcr->job_spool_size = 0; /* zap size in input dcr */
319 V(dcr->dev->spool_mutex);
320 free_memory(rdev->dev_name);
321 free_pool_memory(rdev->errmsg);
322 /* Be careful to NULL the jcr and free rdev after free_dcr() */
327 dcr->spooling = true; /* turn on spooling again */
328 dcr->despooling = false;
330 * Note, if committing we leave the device blocked. It will be removed in
334 dcr->dev->dunblock();
336 jcr->sendJobStatus(JS_Running);
341 * Read a block from the spool file
343 * Returns RB_OK on success
344 * RB_EOT when file done
347 static int read_block_from_spool_file(DCR *dcr)
352 DEV_BLOCK *block = dcr->block;
356 stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
358 Dmsg0(100, "EOT on spool read.\n");
360 } else if (stat != (ssize_t)rlen) {
363 Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
366 Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
367 Jmsg2(jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
369 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
373 if (rlen > block->buf_len) {
374 Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
375 Jmsg2(jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
376 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
379 stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
380 if (stat != (ssize_t)rlen) {
381 Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
382 Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
383 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
386 /* Setup write pointers */
387 block->binbuf = rlen;
388 block->bufp = block->buf + block->binbuf;
389 block->FirstIndex = hdr.FirstIndex;
390 block->LastIndex = hdr.LastIndex;
391 block->VolSessionId = dcr->jcr->VolSessionId;
392 block->VolSessionTime = dcr->jcr->VolSessionTime;
393 Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
398 * Write a block to the spool file
400 * Returns: true on success or EOT
401 * false on hard error
403 bool write_block_to_spool_file(DCR *dcr)
405 uint32_t wlen, hlen; /* length to write */
406 bool despool = false;
407 DEV_BLOCK *block = dcr->block;
409 if (job_canceled(dcr->jcr)) {
412 ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
413 if (block->binbuf <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
417 hlen = sizeof(spool_hdr);
418 wlen = block->binbuf;
419 P(dcr->dev->spool_mutex);
420 dcr->job_spool_size += hlen + wlen;
421 dcr->dev->spool_size += hlen + wlen;
422 if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
423 (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
426 V(dcr->dev->spool_mutex);
428 spool_stats.data_size += hlen + wlen;
429 if (spool_stats.data_size > spool_stats.max_data_size) {
430 spool_stats.max_data_size = spool_stats.data_size;
434 char ec1[30], ec2[30];
435 if (dcr->max_job_spool_size > 0) {
436 Jmsg(dcr->jcr, M_INFO, 0, _("User specified Job spool size reached: "
437 "JobSpoolSize=%s MaxJobSpoolSize=%s\n"),
438 edit_uint64_with_commas(dcr->job_spool_size, ec1),
439 edit_uint64_with_commas(dcr->max_job_spool_size, ec2));
441 Jmsg(dcr->jcr, M_INFO, 0, _("User specified Device spool size reached: "
442 "DevSpoolSize=%s MaxDevSpoolSize=%s\n"),
443 edit_uint64_with_commas(dcr->dev->spool_size, ec1),
444 edit_uint64_with_commas(dcr->dev->max_spool_size, ec2));
447 if (!despool_data(dcr, false)) {
448 Pmsg0(000, _("Bad return from despool in write_block.\n"));
451 /* Despooling cleared these variables so reset them */
452 P(dcr->dev->spool_mutex);
453 dcr->job_spool_size += hlen + wlen;
454 dcr->dev->spool_size += hlen + wlen;
455 V(dcr->dev->spool_mutex);
456 Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
460 if (!write_spool_header(dcr)) {
463 if (!write_spool_data(dcr)) {
467 Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
472 static bool write_spool_header(DCR *dcr)
476 DEV_BLOCK *block = dcr->block;
479 hdr.FirstIndex = block->FirstIndex;
480 hdr.LastIndex = block->LastIndex;
481 hdr.len = block->binbuf;
484 for (int retry=0; retry<=1; retry++) {
485 stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
488 Jmsg(jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
490 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
492 if (stat != (ssize_t)sizeof(hdr)) {
493 Jmsg(jcr, M_ERROR, 0, _("Error writing header to spool file."
494 " Disk probably full. Attempting recovery. Wanted to write=%d got=%d\n"),
495 (int)stat, (int)sizeof(hdr));
496 /* If we wrote something, truncate it, then despool */
498 #if defined(HAVE_WIN32)
499 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
501 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
503 if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
505 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
507 /* Note, try continuing despite ftruncate problem */
510 if (!despool_data(dcr, false)) {
511 Jmsg(jcr, M_FATAL, 0, _("Fatal despooling error."));
512 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
515 continue; /* try again */
519 Jmsg(jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
520 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
524 static bool write_spool_data(DCR *dcr)
527 DEV_BLOCK *block = dcr->block;
531 for (int retry=0; retry<=1; retry++) {
532 stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
535 Jmsg(jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
537 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
539 if (stat != (ssize_t)block->binbuf) {
541 * If we wrote something, truncate it and the header, then despool
544 #if defined(HAVE_WIN32)
545 boffset_t pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
547 boffset_t pos = lseek(dcr->spool_fd, 0, SEEK_CUR);
549 if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
551 Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
553 /* Note, try continuing despite ftruncate problem */
556 if (!despool_data(dcr, false)) {
557 Jmsg(jcr, M_FATAL, 0, _("Fatal despooling error."));
558 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
561 if (!write_spool_header(dcr)) {
564 continue; /* try again */
568 Jmsg(jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
569 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
573 static bool close_data_spool_file(DCR *dcr)
575 POOLMEM *name = get_pool_memory(PM_MESSAGE);
578 spool_stats.data_jobs--;
579 spool_stats.total_data_jobs++;
580 if (spool_stats.data_size < dcr->job_spool_size) {
581 spool_stats.data_size = 0;
583 spool_stats.data_size -= dcr->job_spool_size;
586 P(dcr->dev->spool_mutex);
587 dcr->job_spool_size = 0;
588 V(dcr->dev->spool_mutex);
590 make_unique_data_spool_filename(dcr, &name);
591 close(dcr->spool_fd);
593 dcr->spooling = false;
595 Dmsg1(100, "Deleted spool file: %s\n", name);
596 free_pool_memory(name);
600 bool are_attributes_spooled(JCR *jcr)
602 return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
606 * Create spool file for attributes.
607 * This is done by "attaching" to the bsock, and when
608 * it is called, the output is written to a file.
609 * The actual spooling is turned on and off in
610 * append.c only during writing of the attributes.
612 bool begin_attribute_spool(JCR *jcr)
614 if (!jcr->no_attributes && jcr->spool_attributes) {
615 return open_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"));
656 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
660 if (!bstrcmp(jcr->dir_bsock->msg, "1000 OK BlastAttr\n")) {
666 bool commit_attribute_spool(JCR *jcr)
668 boffset_t size, data_end;
673 Dmsg1(100, "Commit attributes at %s\n", bstrftimes(tbuf, sizeof(tbuf),
674 (utime_t)time(NULL)));
675 if (are_attributes_spooled(jcr)) {
676 dir = jcr->dir_bsock;
677 if (fseeko(dir->m_spool_fd, 0, SEEK_END) != 0) {
679 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
681 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
684 size = ftello(dir->m_spool_fd);
685 /* For Incomplete Job truncate spool file to last valid data_end if necssary */
686 if (jcr->is_JobStatus(JS_Incomplete)) {
687 data_end = dir->get_last_data_end();
688 if (size > data_end) {
689 if (ftruncate(fileno(dir->m_spool_fd), data_end) != 0) {
691 Jmsg(jcr, M_FATAL, 0, _("Truncate on attributes file failed: ERR=%s\n"),
693 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
696 Dmsg2(100, "=== Attrib spool truncated from %lld to %lld\n",
703 Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
705 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
709 if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
710 spool_stats.max_attr_size = spool_stats.attr_size + size;
712 spool_stats.attr_size += size;
714 jcr->sendJobStatus(JS_AttrDespooling);
715 Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
716 edit_uint64_with_commas(size, ec1));
718 if (!blast_attr_spool_file(jcr, size)) {
719 /* Can't read spool file from director side,
720 * send content over network.
722 dir->despool(update_attr_spool_size, size);
724 return close_attr_spool_file(jcr, dir);
729 close_attr_spool_file(jcr, dir);
733 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
735 POOLMEM *name = get_pool_memory(PM_MESSAGE);
737 make_unique_spool_filename(jcr, &name, bs->m_fd);
738 bs->m_spool_fd = fopen(name, "w+b");
739 if (!bs->m_spool_fd) {
741 Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
743 jcr->forceJobStatus(JS_FatalError); /* override any Incomplete */
744 free_pool_memory(name);
748 spool_stats.attr_jobs++;
750 free_pool_memory(name);
754 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
760 Dmsg1(100, "Close attr spool file at %s\n", bstrftimes(tbuf, sizeof(tbuf),
761 (utime_t)time(NULL)));
762 if (!bs->m_spool_fd) {
765 name = get_pool_memory(PM_MESSAGE);
767 spool_stats.attr_jobs--;
768 spool_stats.total_attr_jobs++;
770 make_unique_spool_filename(jcr, &name, bs->m_fd);
771 fclose(bs->m_spool_fd);
773 free_pool_memory(name);
774 bs->m_spool_fd = NULL;
775 bs->clear_spooling();
779 bool discard_attribute_spool(JCR *jcr)
781 if (are_attributes_spooled(jcr)) {
782 return close_attr_spool_file(jcr, jcr->dir_bsock);