/* * Create a new Device Control Record and attach * it to the device (if this is a real job). * Note, this has been updated so that it can be called first * without a DEVICE, then a second or third time with a DEVICE, * and each time, it should cleanup and point to the new device. * This should facilitate switching devices. * Note, each dcr must point to the controlling job (jcr). However, * a job can have multiple dcrs, so we must not store in the jcr's * structure as previously. The higher level routine must store * this dcr in the right place * */ DCR *new_dcr(JCR *jcr, DCR *dcr, DEVICE *dev, bool writing) { DEVICE *odev; if (!dcr) { dcr = (DCR *)malloc(sizeof(DCR)); memset(dcr, 0, sizeof(DCR)); dcr->tid = pthread_self(); dcr->spool_fd = -1; } dcr->jcr = jcr; /* point back to jcr */ odev = dcr->dev; if (dcr->attached_to_dev && odev) { Dmsg2(100, "Detach 0x%x from olddev %s\n", dcr, odev->print_name()); odev->detach_dcr_from_dev(dcr); } ASSERT2(!dcr->attached_to_dev, "DCR is attached. Wrong!"); /* Set device information, possibly change device */ if (dev) { dcr->free_blocks(); dcr->block = new_block(dev); dcr->ameta_block = dcr->block; if (dcr->rec) { free_record(dcr->rec); } dcr->rec = new_record(); /* Use job spoolsize prior to device spoolsize */ if (jcr && jcr->spool_size) { dcr->max_job_spool_size = jcr->spool_size; } else { dcr->max_job_spool_size = dev->device->max_job_spool_size; } dcr->device = dev->device; dcr->set_dev(dev); Dmsg2(100, "Attach 0x%x to dev %s\n", dcr, dev->print_name()); dev->attach_dcr_to_dev(dcr); } if (writing) { dcr->set_writing(); } else { dcr->clear_writing(); } return dcr; }
/* * 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; }