/*
Bacula® - The Network Backup Solution
- Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
*
* Kern Sibbald, August MMII
*
- * Version $Id$
*/
#include "bacula.h" /* pull in global headers */
/* Forward referenced functions */
static void attach_dcr_to_dev(DCR *dcr);
+static void detach_dcr_from_dev(DCR *dcr);
+static void set_dcr_from_vol(DCR *dcr, VOL_LIST *vol);
/*********************************************************************
int vol_label_status;
int retry = 0;
- Dmsg1(950, "jcr->dcr=%p\n", jcr->dcr);
+ Dmsg2(950, "dcr=%p dev=%p\n", dcr, dcr->dev);
+ Dmsg2(950, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
dev->dblock(BST_DOING_ACQUIRE);
if (dev->num_writers > 0) {
jcr->NumReadVolumes, jcr->CurReadVolume);
goto get_out; /* should not happen */
}
- /*
- * Note, if we want to be able to work from a .bsr file only
- * for disaster recovery, we must "simulate" reading the catalog
- */
- bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
- bstrncpy(dcr->VolCatInfo.VolCatName, vol->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
- bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
- dcr->VolCatInfo.Slot = vol->Slot;
- dcr->VolCatInfo.InChanger = vol->Slot > 0;
+ set_dcr_from_vol(dcr, vol);
+
+ Dmsg2(100, "Want Vol=%s Slot=%d\n", vol->VolumeName, vol->Slot);
/*
* If the MediaType requested for this volume is not the
DIRSTORE *store;
int stat;
- Jmsg3(jcr, M_INFO, 0, _("Changing device. Want Media Type=\"%s\" have=\"%s\"\n"
+ Jmsg3(jcr, M_INFO, 0, _("Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
" device=%s\n"),
dcr->media_type, dev->device->media_type, dev->print_name());
- Dmsg3(50, "Changing device. Want Media Type=\"%s\" have=\"%s\"\n"
+ Dmsg3(50, "Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
" device=%s\n",
dcr->media_type, dev->device->media_type, dev->print_name());
lock_reservations();
memset(&rctx, 0, sizeof(RCTX));
rctx.jcr = jcr;
+ jcr->read_dcr = dcr;
jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
rctx.any_drive = true;
rctx.device_name = vol->device;
dev = dcr->dev; /* get new device pointer */
dev->dblock(BST_DOING_ACQUIRE);
dcr->VolumeName[0] = 0;
- Jmsg(jcr, M_INFO, 0, _("Media Type change. New device %s chosen.\n"),
+ Jmsg(jcr, M_INFO, 0, _("Media Type change. New read device %s chosen.\n"),
dev->print_name());
- Dmsg1(50, "Media Type change. New device %s chosen.\n", dev->print_name());
+ Dmsg1(50, "Media Type change. New read device %s chosen.\n", dev->print_name());
bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
- bstrncpy(dcr->VolCatInfo.VolCatName, vol->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
+ dcr->setVolCatName(vol->VolumeName);
bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
dcr->VolCatInfo.Slot = vol->Slot;
dcr->VolCatInfo.InChanger = vol->Slot > 0;
goto get_out;
}
}
+ Dmsg2(400, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
dev->clear_unload();
- if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
- Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
- dcr->dev->print_name());
- Jmsg2(jcr, M_FATAL, 0, _("Could not reserve volume %s on %s\n"), dcr->VolumeName,
- dcr->dev->print_name());
- goto get_out;
- }
if (dev->vol && dev->vol->is_swapping()) {
dev->vol->set_slot(vol->Slot);
Dmsg3(100, "swapping: slot=%d Vol=%s dev=%s\n", dev->vol->get_slot(),
dev->vol->vol_name, dev->print_name());
}
-
init_device_wait_timers(dcr);
tape_previously_mounted = dev->can_read() || dev->can_append() ||
if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n",
dcr->VolumeName, jcr->errmsg);
- Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
}
dev->set_load(); /* set to load volume */
goto get_out; /* error return */
}
- dcr->do_swapping(false/*is_writing*/);
+ dcr->do_unload();
+ dcr->do_swapping(false/*!is_writing*/);
+ dcr->do_load(false /*!is_writing*/);
+ set_dcr_from_vol(dcr, vol); /* refresh dcr with desired volume info */
/*
* This code ensures that the device is ready for
vol_label_status = read_dev_volume_label(dcr);
switch (vol_label_status) {
case VOL_OK:
+ Dmsg0(50, "Got correct volume.\n");
ok = true;
dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
break; /* got it */
case VOL_IO_ERROR:
+ Dmsg0(50, "IO Error\n");
/*
* Send error message generated by read_dev_volume_label()
* only we really had a tape mounted. This supresses superfluous
* error messages when nothing is mounted.
*/
if (tape_previously_mounted) {
- Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ Jmsg(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
}
goto default_path;
case VOL_NAME_ERROR:
+ Dmsg3(50, "Vol name=%s want=%s drv=%s.\n", dev->VolHdr.VolumeName,
+ dcr->VolumeName, dev->print_name());
if (dev->is_volume_to_unload()) {
goto default_path;
}
-// if (tape_initially_mounted) {
- tape_initially_mounted = false;
-// goto default_path;
-// }
dev->set_unload(); /* force unload of unwanted tape */
if (!unload_autochanger(dcr, -1)) {
/* at least free the device so we can re-open with correct volume */
dev->close();
+ free_volume(dev);
}
dev->set_load();
- ASSERT(0);
/* Fall through */
default:
- Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
default_path:
+ Dmsg0(50, "default path\n");
tape_previously_mounted = true;
/*
*/
if (dev->requires_mount()) {
dev->close();
+ free_volume(dev);
}
/* Call autochanger only once unless ask_sysop called */
try_autochanger = false;
continue; /* try reading volume mounted */
}
- /* Try closing and re-opening */
- dev->close();
- if (dev->open(dcr, OPEN_READ_ONLY) >= 0) {
- continue;
- }
- if (!dev->poll) {
- Jmsg3(jcr, M_WARNING, 0, _("Read open device %s Volume \"%s\" failed: ERR=%s\n"),
- dev->print_name(), dcr->VolumeName, dev->bstrerror());
- }
}
/* Mount a specific volume and no other */
if (!dir_ask_sysop_to_mount_volume(dcr, ST_READ)) {
goto get_out; /* error return */
}
- try_autochanger = true; /* permit using autochanger again */
+
+ /* Volume info is always needed because of VolParts */
+ Dmsg1(150, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
+ if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
+ Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n",
+ dcr->VolumeName, jcr->errmsg);
+ Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
+ }
+ dev->set_load(); /* set to load volume */
+
+ try_autochanger = true; /* permit trying the autochanger again */
+
continue; /* try reading again */
} /* end switch */
break;
} else {
dev->dunlock(); /* dunblock() unlock the device too */
}
- Dmsg1(950, "jcr->dcr=%p\n", jcr->dcr);
+ Dmsg2(950, "dcr=%p dev=%p\n", dcr, dcr->dev);
+ Dmsg2(950, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
return ok;
}
-
/*
* Acquire device for writing. We permit multiple writers.
* If this is the first one, we read the label.
init_device_wait_timers(dcr);
- dev->dblock(BST_DOING_ACQUIRE);
+ P(dev->acquire_mutex); /* only one job at a time */
+ dev->dlock();
Dmsg1(100, "acquire_append device is %s\n", dev->is_tape()?"tape":
(dev->is_dvd()?"DVD":"disk"));
* we need to recycle the tape.
*/
if (dev->num_writers == 0) {
- memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
+ dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */
}
have_vol = dcr->is_tape_position_ok();
}
if (!have_vol) {
+ dev->r_dlock(true);
+ block_device(dev, BST_DOING_ACQUIRE);
+ dev->dunlock();
Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId);
if (!dcr->mount_next_write_volume()) {
if (!job_canceled(jcr)) {
Dmsg1(200, "Could not ready device %s for append.\n",
dev->print_name());
}
+ dev->dlock();
+ unblock_device(dev);
goto get_out;
}
Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
+ dev->dlock();
+ unblock_device(dev);
}
dev->num_writers++; /* we are now a writer */
jcr->NumWriteVolumes = 1;
}
dev->VolCatInfo.VolCatJobs++; /* increment number of jobs on vol */
+ Dmsg4(100, "=== nwriters=%d nres=%d vcatjob=%d dev=%s\n",
+ dev->num_writers, dev->num_reserved(), dev->VolCatInfo.VolCatJobs,
+ dev->print_name());
dir_update_volume_info(dcr, false, false); /* send Volume info to Director */
ok = true;
get_out:
- dev->dlock();
dcr->clear_reserved();
- dev->dunblock(DEV_LOCKED);
+ dev->dunlock();
+ V(dev->acquire_mutex);
return ok ? dcr : NULL;
}
* This job is done, so release the device. From a Unix standpoint,
* the device remains open.
*
- * Note, if we are spooling, we may enter with the device locked.
- * However, in all cases, unlock the device when leaving.
+ * 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)
DEVICE *dev = dcr->dev;
bool ok = true;
char tbuf[100];
+ int was_blocked = BST_NOT_BLOCKED;
- /* lock only if not already locked by this thread */
- if (!dcr->is_dev_locked()) {
- dev->r_dlock();
+ dev->dlock();
+ 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");
dcr->clear_reserved();
if (dev->can_read()) {
+ VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
dev->clear_read(); /* clear read bit */
- Dmsg0(100, "dir_update_vol_info. Release0\n");
- dir_update_volume_info(dcr, false, false); /* send Volume info to Director */
- volume_unused(dcr);
-
+ 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
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->VolCatInfo.VolCatName, dev->print_name());
+ dev->getVolCatName(), dev->print_name());
if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) {
- Jmsg(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
- dcr->VolCatInfo.VolCatName, jcr->Job);
+ 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) {
/* 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->VolCatInfo.VolCatName, dev->print_name());
+ dev->getVolCatName(), dev->print_name());
}
if (dev->num_writers == 0) { /* if not being used */
volume_unused(dcr); /* we obviously are not using the volume */
*/
volume_unused(dcr);
}
- unlock_volumes();
Dmsg3(100, "%d writers, %d reserve, dev=%s\n", dev->num_writers, dev->num_reserved(),
dev->print_name());
- debug_list_volumes("acquire:release_device()");
-
/* If no writers, close if file or !CAP_ALWAYS_OPEN */
if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) {
dvd_remove_empty_part(dcr); /* get rid of any empty spool part */
dev->close();
+ free_volume(dev);
}
/* Fire off Alert command and include any output */
char line[MAXSTRING];
alert = get_pool_memory(PM_FNAME);
alert = edit_device_codes(dcr, alert, dcr->device->alert_command, "");
- bpipe = open_bpipe(alert, 0, "r");
+ /* 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);
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);
- dev->dunlock();
+ unlock_volumes();
+
+ /*
+ * 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->dunlock();
+ }
+
if (dcr->keep_dcr) {
detach_dcr_from_dev(dcr);
} else {
- if (jcr->read_dcr == dcr) {
- jcr->read_dcr = NULL;
- }
- if (jcr->dcr == dcr) {
- jcr->dcr = NULL;
- }
free_dcr(dcr);
}
Dmsg2(100, "===== Device %s released by JobId=%u\n", dev->print_name(),
DCR *new_dcr(JCR *jcr, DCR *dcr, DEVICE *dev)
{
if (!dcr) {
+ int errstat;
dcr = (DCR *)malloc(sizeof(DCR));
memset(dcr, 0, sizeof(DCR));
dcr->tid = pthread_self();
dcr->spool_fd = -1;
+ if ((errstat = pthread_mutex_init(&dcr->m_mutex, NULL)) != 0) {
+ berrno be;
+ dev->dev_errno = errstat;
+ Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
+ Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+ }
}
dcr->jcr = jcr; /* point back to jcr */
/* Set device information, possibly change device */
static void attach_dcr_to_dev(DCR *dcr)
{
- DEVICE *dev = dcr->dev;
- JCR *jcr = dcr->jcr;
+ DEVICE *dev;
+ JCR *jcr;
+ P(dcr->m_mutex);
+ dev = dcr->dev;
+ jcr = dcr->jcr;
if (jcr) Dmsg1(500, "JobId=%u enter attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
- if (!dcr->attached_to_dev && dev->initiated && jcr && jcr->JobType != JT_SYSTEM) {
+ /* ***FIXME*** return error if dev not initiated */
+ if (!dcr->attached_to_dev && dev->initiated && jcr && jcr->getJobType() != JT_SYSTEM) {
+ dev->dlock();
dev->attached_dcrs->append(dcr); /* attach dcr to device */
+ dev->dunlock();
dcr->attached_to_dev = true;
Dmsg1(500, "JobId=%u attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
}
+ V(dcr->m_mutex);
}
-void detach_dcr_from_dev(DCR *dcr)
+/*
+ * DCR is locked before calling this routine
+ */
+static void locked_detach_dcr_from_dev(DCR *dcr)
{
DEVICE *dev = dcr->dev;
Dmsg0(500, "Enter detach_dcr_from_dev\n"); /* jcr is NULL in some cases */
/* Detach this dcr only if attached */
if (dcr->attached_to_dev && dev) {
- dev->dlock();
dcr->unreserve_device();
+ dev->dlock();
dcr->dev->attached_dcrs->remove(dcr); /* detach dcr from device */
- dcr->attached_to_dev = false;
// remove_dcr_from_dcrs(dcr); /* remove dcr from jcr list */
dev->dunlock();
}
+ dcr->attached_to_dev = false;
+}
+
+
+static void detach_dcr_from_dev(DCR *dcr)
+{
+ P(dcr->m_mutex);
+ locked_detach_dcr_from_dev(dcr);
+ V(dcr->m_mutex);
}
/*
*/
void free_dcr(DCR *dcr)
{
- JCR *jcr = dcr->jcr;
+ JCR *jcr;
- detach_dcr_from_dev(dcr);
+ P(dcr->m_mutex);
+ jcr = dcr->jcr;
+ locked_detach_dcr_from_dev(dcr);
if (dcr->block) {
free_block(dcr->block);
if (jcr && jcr->dcr == dcr) {
jcr->dcr = NULL;
}
+ if (jcr && jcr->read_dcr == dcr) {
+ jcr->read_dcr = NULL;
+ }
+ V(dcr->m_mutex);
+ pthread_mutex_destroy(&dcr->m_mutex);
free(dcr);
}
+
+static void set_dcr_from_vol(DCR *dcr, VOL_LIST *vol)
+{
+ /*
+ * Note, if we want to be able to work from a .bsr file only
+ * for disaster recovery, we must "simulate" reading the catalog
+ */
+ bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
+ dcr->setVolCatName(vol->VolumeName);
+ bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
+ dcr->VolCatInfo.Slot = vol->Slot;
+ dcr->VolCatInfo.InChanger = vol->Slot > 0;
+}