General:
Changes to 1.38.3:
-20Dec05
+22Dec05
+- Simplify code in askdir.c that waits for creating an appendable
+ volume so that it can handle multiple returns from the wait
+ code.
+- Modify the wait code to permit multiple returns.
+- Return a zero when "autochanger drives" is called and
+ it is not an autochanger.
+- Make rewind_dev() a method taking a DCR as an argument.
+ This permits closing and reopening the drive if the
+ rewind fails as happens if the drive was loaded while the
+ file descriptor was open. This refreshes the file descriptor.
+- Remove the ST_OPENED flag and always rely on fd < 0 for knowing
+ if the device is open or not. This should eliminate
+ Arnos problem.
+- Return error if reserve cannot find at least one suitable device.
+- Make wait_for_sysop() return correct state information.
+- Fix Win32 state file problem. write was not using compat
+ code. This should fix bug #500.
+21Dec05
- Modify gui on command to set only GUI mode and not batch.
- Modify .messages command to always print messages regardless
- of the mode.
+ of the mode.
- If GUI mode is on, suppress automatic printing of
You have messages.
- Delete old bnet packet code.
General:
Changes to 1.39.3:
-20Dec05
+22Dec05
+- Simplify code in askdir.c that waits for creating an appendable
+ volume so that it can handle multiple returns from the wait
+ code.
+- Modify the wait code to permit multiple returns.
+- Return a zero when "autochanger drives" is called and
+ it is not an autochanger.
+- Make rewind_dev() a method taking a DCR as an argument.
+ This permits closing and reopening the drive if the
+ rewind fails as happens if the drive was loaded while the
+ file descriptor was open. This refreshes the file descriptor.
+- Remove the ST_OPENED flag and always rely on fd < 0 for knowing
+ if the device is open or not. This should eliminate
+ Arnos problem.
+- Return error if reserve cannot find at least one suitable device.
+- Make wait_for_sysop() return correct state information.
+- Fix Win32 state file problem. write was not using compat
+ code. This should fix bug #500.
+21Dec05
- Modify gui on command to set only GUI mode and not batch.
- Modify .messages command to always print messages regardless
of the mode.
&vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
&vol.LabelType);
if (n != 21) {
- Dmsg2(100, "Bad response from Dir fields=%d: %s\n", n, dir->msg);
- Mmsg(jcr->errmsg, _("Error getting Volume info: %s\n"), dir->msg);
+ Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
+ Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
return false;
}
vol.InChanger = InChanger; /* bool in structure */
bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
writing==GET_VOL_INFO_FOR_WRITE?1:0);
Dmsg1(100, ">dird: %s", dir->msg);
- bool OK = do_get_volume_info(dcr);
- return OK;
+ bool ok = do_get_volume_info(dcr);
+ return ok;
}
/*
unbash_spaces(dcr->media_type);
unbash_spaces(dcr->pool_name);
Dmsg1(100, ">dird: %s", dir->msg);
- bool OK = do_get_volume_info(dcr);
- if (OK) {
+ bool ok = do_get_volume_info(dcr);
+ if (ok) {
if (dcr->any_volume || !is_volume_in_use(dcr)) {
found = true;
break;
*/
bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
{
- int stat = 0, jstat;
- bool unmounted;
- bool first = true;
+ int stat = W_TIMEOUT;
DEVICE *dev = dcr->dev;
JCR *jcr = dcr->jcr;
- bool OK = false;
+ bool got_vol = false;
Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
ASSERT(dev->dev_blocked);
Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
return false;
}
- /* First pass, we *know* there are no appendable volumes, so no need to call */
- if (!first) {
- P(dev->mutex);
- OK = dir_find_next_appendable_volume(dcr); /* get suggested volume */
- V(dev->mutex);
- }
- if (!first && OK) {
- unmounted = is_device_unmounted(dev);
- /*
- * If we have a valid volume name and we are not
- * removable media, return now, or if we have a
- * Slot for an autochanger, otherwise wait
- * for the operator to mount the media.
- */
- if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
- dev_cap(dev, CAP_LABEL)) ||
- (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
- Dmsg0(400, "Return 1 from mount without wait.\n");
- return true;
- }
- jstat = JS_WaitMount;
- if (!dev->poll) {
- Jmsg(jcr, M_MOUNT, 0, _(
-"Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
-"Use \"mount\" command to release Job.\n"),
- dcr->VolumeName, dev->print_name(), jcr->Job);
- Dmsg3(400, "Mount %s on %s for Job %s\n",
- dcr->VolumeName, dcr->dev_name, jcr->Job);
- }
+ P(dev->mutex);
+ got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
+ V(dev->mutex);
+ if (got_vol) {
+ return true;
} else {
- jstat = JS_WaitMedia;
- if (!dev->poll) {
+ if (stat == W_TIMEOUT || stat == W_MOUNT) {
Jmsg(jcr, M_MOUNT, 0, _(
"Job %s waiting. Cannot find any appendable volumes.\n"
"Please use the \"label\" command to create a new Volume for:\n"
dcr->pool_name);
}
}
- first = false;
- jcr->JobStatus = jstat;
+ jcr->JobStatus = JS_WaitMedia;
dir_send_job_status(jcr);
stat = wait_for_sysop(dcr);
+ Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
if (dev->poll) {
Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
continue;
}
- if (stat == ETIMEDOUT) {
+ if (stat == W_TIMEOUT) {
if (!double_dev_wait_time(dev)) {
Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
dev->print_name(), jcr->Job);
}
continue;
}
- if (stat == EINVAL) {
+ if (stat == W_ERROR) {
berrno be;
- Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
- stat, be.strerror(stat));
+ Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
return false;
}
- if (stat != 0) {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
- be.strerror(stat));
- }
Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
-
- /* If no VolumeName, and cannot get one, try again */
- P(dev->mutex);
- if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
- !dir_find_next_appendable_volume(dcr)) {
- V(dev->mutex);
- Jmsg(jcr, M_MOUNT, 0, _(
-"Someone woke me up, but I cannot find any appendable\n"
-"volumes for Job=%s.\n"), jcr->Job);
- /* Restart wait counters after user interaction */
- init_device_wait_timers(dcr);
- continue;
- }
- V(dev->mutex);
- unmounted = is_device_unmounted(dev);
- if (unmounted) {
- Dmsg0(400, "Device is unmounted. Must wait.\n");
- continue; /* continue to wait */
- }
-
- /*
- * Device mounted, we have a volume, break and return
- */
- break;
}
set_jcr_job_status(jcr, JS_Running);
dir_send_job_status(jcr);
*/
bool dir_ask_sysop_to_mount_volume(DCR *dcr)
{
- int stat = 0;
+ int stat = W_TIMEOUT;
DEVICE *dev = dcr->dev;
JCR *jcr = dcr->jcr;
if (dev->is_dvd()) {
unmount_dev(dev, 0);
}
- if (!dev->poll) {
+
+ /*
+ * If we are not polling, and the wait timeout or the
+ * user explicitly did a mount, send him the message.
+ * Otherwise skip it.
+ */
+ if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
dcr->VolumeName, dev->print_name(), jcr->Job);
Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
jcr->JobStatus = JS_WaitMount;
dir_send_job_status(jcr);
- stat = wait_for_sysop(dcr); ; /* wait on device */
+ stat = wait_for_sysop(dcr); /* wait on device */
+ Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
if (dev->poll) {
Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
return true;
}
- if (stat == ETIMEDOUT) {
+ if (stat == W_TIMEOUT) {
if (!double_dev_wait_time(dev)) {
Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
dev->print_name(), jcr->Job);
}
continue;
}
- if (stat == EINVAL) {
+ if (stat == W_ERROR) {
berrno be;
- Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
- stat, be.strerror(stat));
+ Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
return false;
}
- if (stat != 0) {
- berrno be;
- Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
- be.strerror(stat));
- }
Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
break;
}
if (!dev->is_autochanger() || !dcr->device->changer_name ||
!dcr->device->changer_command) {
+ if (strcmp(cmd, "drives") == 0) {
+ bnet_fsend(dir, "drives=1\n");
+ }
bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
dev->print_name());
return false;
Pmsg1(0, _("Device open failed. ERR=%s\n"), strerror_dev(dev));
}
}
- rewind_dev(dev);
+ dev->rewind(dcr);
write_new_volume_label_to_dev(dcr, cmd, "Default");
Pmsg1(-1, _("Wrote Volume label for volume \"%s\".\n"), cmd);
}
*/
static void rewindcmd()
{
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
clrerror_dev(dev, -1);
} else {
"This is an *essential* feature ...\n\n"));
block = dcr->block;
rec = new_record();
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
goto bail_out;
}
if (dev_cap(dev, CAP_TWOEOF)) {
weofcmd();
}
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
goto bail_out;
} else {
"This is an *essential* feature ...\n\n"));
empty_block(block);
rec = new_record();
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
goto bail_out;
}
if (dev_cap(dev, CAP_TWOEOF)) {
weofcmd();
}
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
goto bail_out;
} else {
* a failure.
*/
bmicrosleep(sleep_time, 0);
- if (!rewind_dev(dev) || weof_dev(dev,1) < 0) {
+ if (!dev->rewind(dcr) || weof_dev(dev,1) < 0) {
Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), strerror_dev(dev));
clrerror_dev(dev, -1);
Pmsg0(-1, _("\nThe test failed, probably because you need to put\n"
dev->min_block_size = dev->max_block_size;
set_volume_name("TestVolume1", 1);
- if (!dev->rewind()) {
+ if (!dev->rewind(dcr)) {
Pmsg0(000, _("Rewind failed.\n"));
}
if (!dev->weof()) {
* loose track of where we are (block number unknown).
*/
Pmsg0(-1, _("Rewinding.\n"));
- if (!rewind_dev(dev)) { /* get to a known place on tape */
+ if (!dev->rewind(dcr)) { /* get to a known place on tape */
goto bail_out;
}
/* Read the first 10000 records */
Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
}
- dev->fd = -1;
+ dev->clear_opened();
dev->attached_dcrs = New(dlist(dcr, &dcr->dev_link));
Dmsg2(29, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name);
return fd;
} else {
::close(fd); /* use system close so correct mode will be used on open */
- fd = -1;
clear_opened();
Dmsg0(100, "Close fd for mode change.\n");
}
}
/*
- * If the flage open_nowait is set, which is the case
- * when the daemon is initially trying to open the device,
- * we open it with O_NONBLOCK set and O_RONLY, which will
- * allow us to open normal Linux tape drives with no tape
- * in the drive without blocking. We then immediately
- * set blocking status so that if we read from the device they
- * will be normal blocking reads.
- *
- * If later, we want to write on the device, it will be freed and
- * reopened, but hopefully there will be a tape in the drive so
- * we will not block.
*/
void DEVICE::open_tape_device(DCR *dcr, int omode)
{
set_blocking();
Dmsg2(100, "openmode=%d %s\n", openmode, mode_to_str(openmode));
dev_errno = 0;
- set_opened();
- use_count = 1;
update_pos_dev(this); /* update position */
set_os_device_parameters(this); /* do system dependent stuff */
} else {
if (VolCatInfo.VolCatName[0] == 0) {
Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
print_name());
- fd = -1;
clear_opened();
return;
}
dev_errno = errno;
Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(),
be.strerror());
- clear_opened();
Dmsg1(29, "open failed: %s", errmsg);
Emsg0(M_FATAL, 0, errmsg);
} else {
dev_errno = 0;
- set_opened();
- use_count = 1;
update_pos_dev(this); /* update position */
}
Dmsg5(29, "open dev: %s fd=%d opened, part=%d/%d, part_size=%u\n",
print_name());
Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
print_name());
- fd = -1;
clear_opened();
return;
}
Mmsg(errmsg, _("The media in the device %s is not empty, please blank it before writing anything to it.\n"), print_name());
Emsg0(M_FATAL, 0, errmsg);
unmount_dev(this, 1); /* Unmount the device, so the operator can change it. */
- fd = -1;
+ clear_opened();
return;
}
}
else {
Mmsg(errmsg, _("There is no valid media in the device %s.\n"), print_name());
Emsg0(M_FATAL, 0, errmsg);
- fd = -1;
+ clear_opened();
return;
}
}
else {
Mmsg(errmsg, _("Could not mount device %s.\n"), print_name());
Emsg0(M_FATAL, 0, errmsg);
- fd = -1;
+ clear_opened();
return;
}
}
Dmsg1(29, "open failed: %s", errmsg);
/* Use system close() */
::close(fd);
- fd = -1;
clear_opened();
} else {
part_size = filestat.st_size;
dev_errno = 0;
- set_opened();
- use_count = 1;
update_pos_dev(this); /* update position */
/* NB: It seems this code is wrong... part number is incremented in open_next_part, not here */
}
}*/
}
- } else {
- clear_opened();
}
}
-#ifdef debug_tracing
-#undef rewind_dev
-bool _rewind_dev(char *file, int line, DEVICE *dev)
-{
- Dmsg3(100, "rewind_dev fd=%d called from %s:%d\n", dev->fd, file, line);
- return rewind_dev(dev);
-}
-#endif
-
/*
* Rewind the device.
* Returns: true on success
* false on failure
*/
-bool rewind_dev(DEVICE *dev)
+bool DEVICE::rewind(DCR *dcr)
{
struct mtop mt_com;
unsigned int i;
+ bool first = true;
- Dmsg3(29, "rewind_dev res=%d fd=%d %s\n", dev->reserved_device,
- dev->fd, dev->print_name());
- if (dev->fd < 0) {
- if (!dev->is_dvd()) { /* In case of major error, the fd is not open on DVD, so we don't want to abort. */
- dev->dev_errno = EBADF;
- Mmsg1(dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"),
- dev->print_name());
- Emsg0(M_ABORT, 0, dev->errmsg);
+ Dmsg3(29, "rewind res=%d fd=%d %s\n", reserved_device, fd, print_name());
+ if (fd < 0) {
+ if (!is_dvd()) { /* In case of major error, the fd is not open on DVD, so we don't want to abort. */
+ dev_errno = EBADF;
+ Mmsg1(errmsg, _("Bad call to rewind. Device %s not open\n"),
+ print_name());
+ Emsg0(M_ABORT, 0, errmsg);
}
return false;
}
- dev->state &= ~(ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */
- dev->block_num = dev->file = 0;
- dev->file_size = 0;
- dev->file_addr = 0;
- if (dev->is_tape()) {
+ state &= ~(ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */
+ block_num = file = 0;
+ file_size = 0;
+ file_addr = 0;
+ if (is_tape()) {
mt_com.mt_op = MTREW;
mt_com.mt_count = 1;
/* If we get an I/O error on rewind, it is probably because
* the drive is actually busy. We loop for (about 5 minutes)
* retrying every 5 seconds.
*/
- for (i=dev->max_rewind_wait; ; i -= 5) {
- if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+ for (i=max_rewind_wait; ; i -= 5) {
+ if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
berrno be;
- clrerror_dev(dev, MTREW);
- if (i == dev->max_rewind_wait) {
+ clrerror_dev(this, MTREW);
+ if (i == max_rewind_wait) {
Dmsg1(200, "Rewind error, %s. retrying ...\n", be.strerror());
}
- if (dev->dev_errno == EIO && i > 0) {
+ /*
+ * This is a gross hack, because if the user has the
+ * device mounted (i.e. open), then uses mtx to load
+ * a tape, the current open file descriptor is invalid.
+ * So, we close the drive and re-open it.
+ */
+ if (first && dcr) {
+ int open_mode = openmode;
+ ::close(fd);
+ clear_opened();
+ open(dcr, open_mode);
+ if (fd < 0) {
+ return false;
+ }
+ first = false;
+ continue;
+ }
+ if (dev_errno == EIO && i > 0) {
Dmsg0(200, "Sleeping 5 seconds.\n");
bmicrosleep(5, 0);
continue;
}
- Mmsg2(dev->errmsg, _("Rewind error on %s. ERR=%s.\n"),
- dev->print_name(), be.strerror());
+ Mmsg2(errmsg, _("Rewind error on %s. ERR=%s.\n"),
+ print_name(), be.strerror());
return false;
}
break;
}
- } else if (dev->is_file()) {
- if (lseek_dev(dev, (off_t)0, SEEK_SET) < 0) {
+ } else if (is_file()) {
+ if (lseek_dev(this, (off_t)0, SEEK_SET) < 0) {
berrno be;
- dev->dev_errno = errno;
- Mmsg2(dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"),
- dev->print_name(), be.strerror());
+ dev_errno = errno;
+ Mmsg2(errmsg, _("lseek_dev error on %s. ERR=%s.\n"),
+ print_name(), be.strerror());
return false;
}
}
Dmsg0(100,"Using FAST FSF for EOM\n");
/* If unknown position, rewind */
if (!dev_get_os_pos(dev, &mt_stat)) {
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(NULL)) {
return false;
}
}
/*
* Rewind then use FSF until EOT reached
*/
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(NULL)) {
return false;
}
/*
* done, all future references to the drive get and I/O error.
*/
clrerror_dev(dev, MTREW);
- return rewind_dev(dev);
+ return dev->rewind(NULL);
}
}
Dmsg4(100, "reposition_dev from %u:%u to %u:%u\n",
dev->file, dev->block_num, file, block);
if (file < dev->file) {
- Dmsg0(100, "Rewind_dev\n");
- if (!rewind_dev(dev)) {
+ Dmsg0(100, "Rewind\n");
+ if (!dev->rewind(NULL)) {
return false;
}
}
Dmsg1(100, "really close_dev %s\n", dev->print_name());
if (dev->fd >= 0) {
- close(dev->fd);
+ ::close(dev->fd);
}
if (!unmount_dev(dev, 1)) {
}
/* Clean up device packet so it can be reused */
- dev->fd = -1;
- dev->state &= ~(ST_OPENED|ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF);
+ dev->clear_opened();
+ dev->state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF);
dev->label_type = B_BACULA_LABEL;
dev->file = dev->block_num = 0;
dev->file_size = 0;
stop_thread_timer(dev->tid);
dev->tid = 0;
}
- dev->use_count = 0;
dev->openmode = 0;
}
*/
void DEVICE::close()
{
- /*if (fd >= 0 && use_count == 1) {*/
- /* No need to check if fd >= 0: it is checked again
- * in do_close, and do_close MUST be called for volumes
- * split in parts, even if fd == -1.
- */
- if (use_count == 1) {
- do_close(this);
- } else if (use_count > 0) {
- use_count--;
- }
-
-#ifdef FULL_DEBUG
- ASSERT(use_count >= 0);
-#endif
+ do_close(this);
}
#define steal_device_lock(d, p, s) _steal_device_lock(__FILE__, __LINE__, (d), (p), s)
#define give_back_device_lock(d, p) _give_back_device_lock(__FILE__, __LINE__, (d), (p))
+/* Return values from wait_for_sysop() */
+enum {
+ W_ERROR = 1,
+ W_TIMEOUT,
+ W_POLL,
+ W_MOUNT,
+ W_WAKE
+};
+
/* Arguments to open_dev() */
enum {
CREATE_READ_WRITE = 1,
#define dev_state(dev, st_state) ((dev)->state & (st_state))
/* Device state bits */
-#define ST_OPENED (1<<0) /* set when device opened */
+#define ST_XXXXXX (1<<0) /* was ST_OPENED */
#define ST_TAPE (1<<1) /* is a tape device */
#define ST_FILE (1<<2) /* is a file device */
#define ST_FIFO (1<<3) /* is a fifo device */
BST_UNMOUNTED, /* User unmounted device */
BST_WAITING_FOR_SYSOP, /* Waiting for operator to mount tape */
BST_DOING_ACQUIRE, /* Opening/validating/moving tape */
- BST_WRITING_LABEL, /* Labeling a tape */
- BST_UNMOUNTED_WAITING_FOR_SYSOP, /* Closed by user during mount request */
- BST_MOUNT /* Mount request */
+ BST_WRITING_LABEL, /* Labeling a tape */
+ BST_UNMOUNTED_WAITING_FOR_SYSOP, /* User unmounted during wait for op */
+ BST_MOUNT /* Mount request */
};
/* Volume Catalog Information structure definition */
/* New access control in process of being implemented */
brwlock_t lock; /* New mutual exclusion lock */
- int use_count; /* usage count on this device 0 or 1 */
int fd; /* file descriptor */
int capabilities; /* capabilities mask */
int state; /* state mask */
int mode; /* read/write modes */
int openmode; /* parameter passed to open_dev (useful to reopen the device) */
bool autoselect; /* Autoselect in autochanger */
- bool open_nowait; /* If set, don't wait on open */
int label_type; /* Bacula/ANSI/IBM label types */
uint32_t drive_index; /* Autochanger drive index (base 0) */
int32_t Slot; /* Slot currently in drive (base 1) */
int is_file() const { return state & ST_FILE; }
int is_fifo() const { return state & ST_FIFO; }
int is_dvd() const { return state & ST_DVD; }
- int is_open() const { return state & ST_OPENED; }
+ int is_open() const { return fd >= 0; }
int is_offline() const { return state & ST_OFFLINE; }
int is_labeled() const { return state & ST_LABEL; }
int is_mounted() const { return state & ST_MOUNTED; }
bool weof() { return !weof_dev(this, 1); };
bool fsr(int num); /* in dev.c */
bool fsf(int num); /* in dev.c */
- bool rewind() { return rewind_dev(this); };
const char *strerror() const;
const char *archive_name() const;
const char *name() const;
void set_labeled() { state |= ST_LABEL; };
void set_read() { state |= ST_READ; };
void set_offline() { state |= ST_OFFLINE; };
- void set_opened() { state |= ST_OPENED; };
void set_mounted() { state |= ST_MOUNTED; };
void set_media() { state |= ST_MEDIA; };
void set_short_block() { state |= ST_SHORT; };
void clear_offline() { state &= ~ST_OFFLINE; };
void clear_eot() { state &= ~ST_EOT; };
void clear_eof() { state &= ~ST_EOF; };
- void clear_opened() { state &= ~ST_OPENED; };
+ void clear_opened() { fd = -1; };
void clear_mounted() { state &= ~ST_MOUNTED; };
void clear_media() { state &= ~ST_MEDIA; };
void clear_short_block() { state &= ~ST_SHORT; };
void unblock(); /* in dev.c */
void close(); /* in dev.c */
int open(DCR *dcr, int mode); /* in dev.c */
+ bool rewind(DCR *dcr); /* in dev.c */
void set_blocked(int block) { dev_blocked = block; };
mode = OPEN_READ_ONLY;
}
Dmsg0(129, "Opening device.\n");
- dev->open_nowait = true;
if (dev->open(dcr, mode) < 0) {
Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg);
ok = false;
Dmsg1(129, "open dev %s OK\n", dev->print_name());
bail_out:
- dev->open_nowait = false;
unlock_device(dev);
return ok;
}
}
/*
- * Used when unmounting the device, ignore use_count
*/
void force_close_device(DEVICE *dev)
{
return;
}
Dmsg1(29, "Force close_dev %s\n", dev->print_name());
- dev->use_count = 1;
+ free_volume(dev);
dev->close();
-
-#ifdef FULL_DEBUG
- ASSERT(dev->use_count >= 0);
-#endif
}
dev->no_wait_id = hold->no_wait_id;
Dmsg1(400, "return lock. new=%s\n", dev->print_blocked());
if (dev->num_waiting > 0) {
- Dmsg0(400, "Broadcast\n");
pthread_cond_broadcast(&dev->wait); /* wake them up */
}
}
if ((bnet_stat = bnet_recv(bs)) <= 0) {
break; /* connection terminated */
}
- Dmsg1(199, "<dird: %s\n", bs->msg);
+ Dmsg1(199, "<dird: %s", bs->msg);
/* Ensure that device initialization is complete */
while (!init_done) {
bmicrosleep(1, 0);
if (sscanf(dir->msg, "cancel Job=%127s", Job) == 1) {
if (!(jcr=get_jcr_by_full_name(Job))) {
- bnet_fsend(dir, _("3902 Job %s not found.\n"), Job);
+ bnet_fsend(dir, _("3904 Job %s not found.\n"), Job);
} else {
P(jcr->mutex);
oldStatus = jcr->JobStatus;
}
/* If thread waiting on mount, wake him */
if (jcr->dcr && jcr->dcr->dev && jcr->dcr->dev->waiting_for_mount()) {
- pthread_cond_broadcast(&jcr->dcr->dev->wait_next_vol);
- pthread_cond_broadcast(&wait_device_release);
+ pthread_cond_broadcast(&jcr->dcr->dev->wait_next_vol);
+ pthread_cond_broadcast(&wait_device_release);
}
if (jcr->read_dcr && jcr->read_dcr->dev && jcr->read_dcr->dev->waiting_for_mount()) {
- pthread_cond_broadcast(&jcr->read_dcr->dev->wait_next_vol);
- pthread_cond_broadcast(&wait_device_release);
+ pthread_cond_broadcast(&jcr->read_dcr->dev->wait_next_vol);
+ pthread_cond_broadcast(&wait_device_release);
}
bnet_fsend(dir, _("3000 Job %s marked to be canceled.\n"), jcr->Job);
free_jcr(jcr);
if (dcr) {
dev = dcr->dev;
P(dev->mutex); /* Use P to avoid indefinite block */
+ Dmsg1(100, "mount cmd blocked=%d\n", dev->dev_blocked);
switch (dev->dev_blocked) { /* device blocked? */
case BST_WAITING_FOR_SYSOP:
/* Someone is waiting, wake him */
case BST_UNMOUNTED_WAITING_FOR_SYSOP:
case BST_UNMOUNTED:
/* We freed the device, so reopen it and wake any waiting threads */
- dev->open_nowait = true;
if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
bnet_fsend(dir, _("3901 open device failed: ERR=%s\n"),
strerror_dev(dev));
- dev->open_nowait = false;
if (dev->dev_blocked == BST_UNMOUNTED) {
/* We blocked the device, so unblock it */
Dmsg0(100, "Unmounted. Unblocking device\n");
}
break;
}
- dev->open_nowait = false;
read_dev_volume_label(dcr);
if (dev->dev_blocked == BST_UNMOUNTED) {
/* We blocked the device, so unblock it */
dev->print_name());
}
} else if (dev->is_tape()) {
- dev->open_nowait = true;
if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
bnet_fsend(dir, _("3901 open device failed: ERR=%s\n"),
strerror_dev(dev));
- dev->open_nowait = false;
break;
}
- dev->open_nowait = false;
read_label(dcr);
if (dev->is_labeled()) {
bnet_fsend(dir, _("3001 Device %s is already mounted with Volume \"%s\"\n"),
dev->clear_read();
dev->label_type = B_BACULA_LABEL;
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"),
dev->print_name(), strerror_dev(dev));
Dmsg1(30, "return VOL_NO_MEDIA: %s", jcr->errmsg);
goto bail_out;
}
if (stat != VOL_OK) { /* Not an ANSI/IBM label, so re-read */
- rewind_dev(dev);
+ dev->rewind(dcr);
} else {
have_ansi_label = true;
}
Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
/* If we are a streaming device, we only get one chance to read */
if (!dev_cap(dev, CAP_STREAM)) {
- rewind_dev(dev);
+ dev->rewind(dcr);
if (have_ansi_label) {
stat = read_ansi_ibm_label(dcr);
/* If we want a label and didn't find it, return error */
bail_out:
empty_block(block);
- rewind_dev(dev);
+ dev->rewind(dcr);
Dmsg1(150, "return %d\n", stat);
return stat;
}
goto bail_out;
}
Dmsg1(150, "Label type=%d\n", dev->label_type);
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
free_volume(dev);
memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
Dmsg2(30, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), strerror_dev(dev));
*/
if (dev->label_type != B_BACULA_LABEL) {
if (read_ansi_ibm_label(dcr) != VOL_OK) {
- rewind_dev(dev);
+ dev->rewind(dcr);
goto bail_out;
}
} else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
* avoids re-writing the ANSI label, which we do not want to do.
*/
if (!dev_cap(dev, CAP_STREAM)) {
- if (!rewind_dev(dev)) {
+ if (!dev->rewind(dcr)) {
Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s: ERR=%s\n"),
dev->print_name(), strerror_dev(dev));
}
*/
if (dev->label_type != B_BACULA_LABEL) {
if (read_ansi_ibm_label(dcr) != VOL_OK) {
- rewind_dev(dev);
+ dev->rewind(dcr);
return false;
}
} else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) {
* Volume, ...)
*/
mount_next_vol:
+ Dmsg1(150, "mount_next_vol retry=%d\n", retry);
/* Ignore retry if this is poll request */
if (!dev->poll && retry++ > 4) {
/* Last ditch effort before giving up, force operator to respond */
/* NOTE! Fall-through wanted. */
case VOL_NO_MEDIA:
default:
+ Dmsg0(200, "VOL_NO_MEDIA or default.\n");
/* Send error message */
if (!dev->poll) {
} else {
stat = search_res_for_device(rctx);
if (stat == 1) { /* found available device */
rctx.suitable_device = true;
+ Dmsg1(100, "Suitable device found=%s\n", device_name);
ok = true;
break;
} else if (stat == 0) { /* device busy */
+ Dmsg1(100, "Suitable busy device found=%s\n", device_name);
rctx.suitable_device = true; /* but it is busy, so continue looking */
}
/* otherwise error */
}
}
}
- return 0; /* nothing found */
+ return -1; /* nothing found */
}
/*
/*
* Wait for SysOp to mount a tape on a specific device
*
- * Returns: status from pthread_cond_timedwait()
+ * Returns: W_ERROR, W_TIMEOUT, W_POLL, W_MOUNT, or W_WAKE
*/
int wait_for_sysop(DCR *dcr)
{
timeout.tv_nsec = tv.tv_usec * 1000;
timeout.tv_sec = tv.tv_sec + add_wait;
- Dmsg3(400, "I'm going to sleep on device %s. HB=%d wait=%d\n", dev->print_name(),
- (int)me->heartbeat_interval, dev->wait_sec);
+ Dmsg4(400, "I'm going to sleep on device %s. HB=%d wait=%d add_wait=%d\n",
+ dev->print_name(), (int)me->heartbeat_interval, dev->wait_sec, add_wait);
start = time(NULL);
/* Wait required time */
stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
}
}
- /*
- * Check if user unmounted the device while we were waiting
- */
- unmounted = is_device_unmounted(dev);
-
- if (stat != ETIMEDOUT) { /* we blocked the device */
- break; /* on error return */
+ if (stat == EINVAL) {
+ berrno be;
+ Dmsg1(000, "pthread stat=%d\n", stat);
+ Jmsg1(jcr, M_FATAL, 0, _("pthread timedwait error. ERR=%s\n"), be.strerror(stat));
+ stat = W_ERROR; /* error */
+ break;
}
+
+
if (dev->rem_wait_sec <= 0) { /* on exceeding wait time return */
Dmsg0(400, "Exceed wait time.\n");
+ stat = W_TIMEOUT;
break;
}
+ /*
+ * Check if user unmounted the device while we were waiting
+ */
+ unmounted = is_device_unmounted(dev);
+
if (!unmounted && dev->vol_poll_interval &&
(now - first_start >= dev->vol_poll_interval)) {
Dmsg1(400, "In wait blocked=%s\n", dev->print_blocked());
dev->poll = true; /* returning a poll event */
+ stat = W_POLL;
break;
}
/*
* Check if user mounted the device while we were waiting
*/
if (dev->get_blocked() == BST_MOUNT) { /* mount request ? */
- stat = 0;
+ stat = W_MOUNT;
break;
}
+ /*
+ * If we did not timeout, then some event happened, so
+ * return to check if state changed.
+ */
+ if (stat != 0) {
+ stat = W_WAKE; /* someone woke us */
+ break;
+ }
+
+ /*
+ * At this point, we know we woke up because of a timeout,
+ * that was due to a heartbeat, so we just update
+ * the wait counters and continue.
+ */
add_wait = dev->wait_sec - (now - start);
if (add_wait < 0) {
add_wait = 0;