/* * NB! This routine locks the device, but if committing will * not unlock it. If not committing, it will be unlocked. */ static bool despool_data(DCR *dcr, bool commit) { DEVICE *rdev; DCR *rdcr; bool ok = true; DEV_BLOCK *block; JCR *jcr = dcr->jcr; int stat; char ec1[50]; Dmsg0(100, "Despooling data\n"); /* * Commit means that the job is done, so we commit, otherwise, we * are despooling because of user spool size max or some error * (e.g. filesystem full). */ if (commit) { Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"), jcr->dcr->VolumeName, edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1)); set_jcr_job_status(jcr, JS_DataCommitting); } else { Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"), edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1)); set_jcr_job_status(jcr, JS_DataDespooling); } set_jcr_job_status(jcr, JS_DataDespooling); dir_send_job_status(jcr); dcr->despool_wait = true; dcr->spooling = false; /* * We work with device blocked, but not locked so that * other threads -- e.g. reservations can lock the device * structure. */ dcr->dblock(BST_DESPOOLING); dcr->despool_wait = false; dcr->despooling = true; /* * This is really quite kludgy and should be fixed some time. * We create a dev structure to read from the spool file * in rdev and rdcr. */ rdev = (DEVICE *)malloc(sizeof(DEVICE)); memset(rdev, 0, sizeof(DEVICE)); rdev->dev_name = get_memory(strlen(spool_name)+1); bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name)); rdev->errmsg = get_pool_memory(PM_EMSG); *rdev->errmsg = 0; rdev->max_block_size = dcr->dev->max_block_size; rdev->min_block_size = dcr->dev->min_block_size; rdev->device = dcr->dev->device; rdcr = new_dcr(jcr, NULL, rdev); rdcr->spool_fd = dcr->spool_fd; block = dcr->block; /* save block */ dcr->block = rdcr->block; /* make read and write block the same */ Dmsg1(800, "read/write block size = %d\n", block->buf_len); lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */ #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED); #endif /* Add run time, to get current wait time */ int32_t despool_start = time(NULL) - jcr->run_time; set_new_file_parameters(dcr); for ( ; ok; ) { if (job_canceled(jcr)) { ok = false; break; } stat = read_block_from_spool_file(rdcr); if (stat == RB_EOT) { break; } else if (stat == RB_ERROR) { ok = false; break; } ok = write_block_to_device(dcr); if (!ok) { Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"), dcr->dev->print_name(), dcr->dev->bstrerror()); Dmsg2(000, "Fatal append error on device %s: ERR=%s\n", dcr->dev->print_name(), dcr->dev->bstrerror()); } Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex); } if (!dir_create_jobmedia_record(dcr)) { Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), dcr->VolCatInfo.VolCatName, jcr->Job); } /* Set new file/block parameters for current dcr */ set_new_file_parameters(dcr); /* * Subtracting run_time give us elapsed time - wait_time since * we started despooling. Note, don't use time_t as it is 32 or 64 * bits depending on the OS and doesn't edit with %d */ int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time; if (despool_elapsed <= 0) { despool_elapsed = 1; } Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"), despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60, edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1)); dcr->block = block; /* reset block */ lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */ if (ftruncate(rdcr->spool_fd, 0) != 0) { berrno be; Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"), be.bstrerror()); /* Note, try continuing despite ftruncate problem */ } P(mutex); if (spool_stats.data_size < dcr->job_spool_size) { spool_stats.data_size = 0; } else { spool_stats.data_size -= dcr->job_spool_size; } V(mutex); P(dcr->dev->spool_mutex); dcr->dev->spool_size -= dcr->job_spool_size; dcr->job_spool_size = 0; /* zap size in input dcr */ V(dcr->dev->spool_mutex); free_memory(rdev->dev_name); free_pool_memory(rdev->errmsg); /* Be careful to NULL the jcr and free rdev after free_dcr() */ rdcr->jcr = NULL; rdcr->dev = NULL; free_dcr(rdcr); free(rdev); dcr->spooling = true; /* turn on spooling again */ dcr->despooling = false; /* * We are done, so unblock the device, but if we have done a * commit, leave it locked so that the job cleanup does not * need to wait to release the device (no re-acquire of the lock). */ dcr->dlock(); unblock_device(dcr->dev); /* If doing a commit, leave the device locked -- unlocked in release_device() */ if (!commit) { dcr->dunlock(); } set_jcr_job_status(jcr, JS_Running); dir_send_job_status(jcr); return ok; }
/* * Write a volume label. This is ONLY called if we have a valid Bareos * label of type PRE_LABEL or we are recyling an existing Volume. * * Returns: true if OK * false if unable to write it */ bool DCR::rewrite_volume_label(bool recycle) { DCR *dcr = this; if (!dev->open(dcr, OPEN_READ_WRITE)) { Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"), dev->print_name(), dcr->VolumeName, dev->bstrerror()); return false; } Dmsg2(190, "set append found freshly labeled volume. fd=%d dev=%x\n", dev->fd(), dev); /* * Let any stored plugin know that we are (re)writing the label. */ if (generate_plugin_event(jcr, bsdEventLabelWrite, dcr) != bRC_OK) { Dmsg0(200, "Error from bsdEventLabelWrite plugin event.\n"); return false; } dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */ dev->set_append(); if (!write_volume_label_to_block(dcr)) { Dmsg0(200, "Error from write volume label.\n"); return false; } Dmsg1(150, "wrote vol label to block. Vol=%s\n", dcr->VolumeName); dev->setVolCatInfo(false); dev->VolCatInfo.VolCatBytes = 0; /* reset byte count */ /* * If we are not dealing with a streaming device, * write the block now to ensure we have write permission. * It is better to find out now rather than later. * We do not write the block now if this is an ANSI label. This * avoids re-writing the ANSI label, which we do not want to do. */ if (!dev->has_cap(CAP_STREAM)) { if (!dev->rewind(dcr)) { Jmsg2(jcr, M_FATAL, 0, _("Rewind error on device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); return false; } if (recycle) { Dmsg1(150, "Doing recycle. Vol=%s\n", dcr->VolumeName); if (!dev->truncate(dcr)) { Jmsg2(jcr, M_FATAL, 0, _("Truncate error on device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); return false; } if (!dev->open(dcr, OPEN_READ_WRITE)) { Jmsg2(jcr, M_FATAL, 0, _("Failed to re-open after truncate on device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); return false; } } /* * If we have already detected an ANSI label, re-read it * to skip past it. Otherwise, we write a new one if * so requested. */ if (dev->label_type != B_BAREOS_LABEL) { if (read_ansi_ibm_label(dcr) != VOL_OK) { dev->rewind(dcr); return false; } } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) { return false; } /* Attempt write to check write permission */ Dmsg1(200, "Attempt to write to device fd=%d.\n", dev->fd()); if (!dcr->write_block_to_dev()) { Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); Dmsg0(200, "===ERROR write block to dev\n"); return false; } } dev->set_labeled(); /* Set or reset Volume statistics */ dev->VolCatInfo.VolCatJobs = 0; dev->VolCatInfo.VolCatFiles = 0; dev->VolCatInfo.VolCatErrors = 0; dev->VolCatInfo.VolCatBlocks = 0; dev->VolCatInfo.VolCatRBytes = 0; if (recycle) { dev->VolCatInfo.VolCatMounts++; dev->VolCatInfo.VolCatRecycles++; dir_create_jobmedia_record(dcr, true); } else { dev->VolCatInfo.VolCatMounts = 1; dev->VolCatInfo.VolCatRecycles = 0; dev->VolCatInfo.VolCatWrites = 1; dev->VolCatInfo.VolCatReads = 1; } Dmsg1(150, "dir_update_vol_info. Set Append vol=%s\n", dcr->VolumeName); dev->VolCatInfo.VolFirstWritten = time(NULL); bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus)); dev->setVolCatName(dcr->VolumeName); if (!dir_update_volume_info(dcr, true, true)) { /* indicate doing relabel */ return false; } if (recycle) { Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device %s, all previous data lost.\n"), dcr->VolumeName, dev->print_name()); } else { Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"), dcr->VolumeName, dev->print_name()); } /* * End writing real Volume label (from pre-labeled tape), or recycling * the volume. */ Dmsg1(150, "OK from rewrite vol label. Vol=%s\n", dcr->VolumeName); /* * Let any stored plugin know the label was rewritten and as such is verified . */ if (generate_plugin_event(jcr, bsdEventLabelVerified, dcr) != bRC_OK) { Dmsg0(200, "Error from bsdEventLabelVerified plugin event.\n"); return false; } return true; }
/* * This job is done, so release the device. From a Unix standpoint, * the device remains open. * * Note, if we were spooling, we may enter with the device blocked. * We unblock at the end, only if it was us who blocked the * device. * */ bool release_device(DCR *dcr) { JCR *jcr = dcr->jcr; DEVICE *dev = dcr->dev; bool ok = true; char tbuf[100]; int was_blocked = BST_NOT_BLOCKED; dev->Lock(); if (!dev->is_blocked()) { block_device(dev, BST_RELEASING); } else { was_blocked = dev->blocked(); dev->set_blocked(BST_RELEASING); } lock_volumes(); Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk"); /* if device is reserved, job never started, so release the reserve here */ dcr->clear_reserved(); if (dev->can_read()) { VOLUME_CAT_INFO *vol = &dev->VolCatInfo; generate_plugin_event(jcr, bsdEventDeviceClose, dcr); dev->clear_read(); /* clear read bit */ Dmsg2(150, "dir_update_vol_info. label=%d Vol=%s\n", dev->is_labeled(), vol->VolCatName); if (dev->is_labeled() && vol->VolCatName[0] != 0) { dir_update_volume_info(dcr, false, false); /* send Volume info to Director */ remove_read_volume(jcr, dcr->VolumeName); volume_unused(dcr); } } else if (dev->num_writers > 0) { /* * Note if WEOT is set, we are at the end of the tape * and may not be positioned correctly, so the * job_media_record and update_vol_info have already been * done, which means we skip them here. */ dev->num_writers--; Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers); if (dev->is_labeled()) { Dmsg2(200, "dir_create_jobmedia. Release vol=%s dev=%s\n", dev->getVolCatName(), dev->print_name()); if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) { Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), dcr->getVolCatName(), jcr->Job); } /* If no more writers, and no errors, and wrote something, write an EOF */ if (!dev->num_writers && dev->can_write() && dev->block_num > 0) { dev->weof(1); write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName); } if (!dev->at_weot()) { dev->VolCatInfo.VolCatFiles = dev->get_file(); /* set number of files */ /* Note! do volume update before close, which zaps VolCatInfo */ dir_update_volume_info(dcr, false, false); /* send Volume info to Director */ Dmsg2(200, "dir_update_vol_info. Release vol=%s dev=%s\n", dev->getVolCatName(), dev->print_name()); } if (dev->num_writers == 0) { /* if not being used */ volume_unused(dcr); /* we obviously are not using the volume */ generate_plugin_event(jcr, bsdEventDeviceClose, dcr); } } } else { /* * If we reach here, it is most likely because the job * has failed, since the device is not in read mode and * there are no writers. It was probably reserved. */ volume_unused(dcr); generate_plugin_event(jcr, bsdEventDeviceClose, dcr); } Dmsg3(100, "%d writers, %d reserve, dev=%s\n", dev->num_writers, dev->num_reserved(), dev->print_name()); /* If no writers, close if file or !CAP_ALWAYS_OPEN */ if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) { generate_plugin_event(jcr, bsdEventDeviceClose, dcr); if (!dev->close()) { Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); } free_volume(dev); } unlock_volumes(); /* Fire off Alert command and include any output */ if (!job_canceled(jcr) && dcr->device->alert_command) { POOLMEM *alert; int status = 1; BPIPE *bpipe; char line[MAXSTRING]; alert = get_pool_memory(PM_FNAME); alert = edit_device_codes(dcr, alert, dcr->device->alert_command, ""); /* Wait maximum 5 minutes */ bpipe = open_bpipe(alert, 60 * 5, "r"); if (bpipe) { while (fgets(line, sizeof(line), bpipe->rfd)) { Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line); } status = close_bpipe(bpipe); } else { status = errno; } if (status != 0) { berrno be; Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"), alert, be.bstrerror(status)); } Dmsg1(400, "alert status=%d\n", status); free_pool_memory(alert); } pthread_cond_broadcast(&dev->wait_next_vol); Dmsg2(100, "JobId=%u broadcast wait_device_release at %s\n", (uint32_t)jcr->JobId, bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL))); pthread_cond_broadcast(&wait_device_release); /* * If we are the thread that blocked the device, then unblock it */ if (pthread_equal(dev->no_wait_id, pthread_self())) { dev->dunblock(true); } else { /* Otherwise, reset the prior block status and unlock */ dev->set_blocked(was_blocked); dev->Unlock(); } if (dcr->keep_dcr) { dev->detach_dcr_from_dev(dcr); } else { free_dcr(dcr); } Dmsg2(100, "Device %s released by JobId=%u\n", dev->print_name(), (uint32_t)jcr->JobId); return ok; }