/* Forward referenced functions */
static void lock_changer(DCR *dcr);
static void unlock_changer(DCR *dcr);
+static bool unload_other_drive(DCR *dcr, int slot);
/*
* Called here to do an autoload using the autochanger, if
loaded = get_autochanger_loaded_slot(dcr);
- /* If tape we want is not loaded, load it. */
if (loaded != slot) {
+ /* Unload anything in our drive */
if (!unload_autochanger(dcr, loaded)) {
goto bail_out;
}
+
+ /* Make sure desired slot is unloaded */
+ if (!unload_other_drive(dcr, slot)) {
+ goto bail_out;
+ }
+
/*
* Load the desired cassette
*/
return ok;
}
+/*
+ * Unload the slot if mounted in a different drive
+ */
+static bool unload_other_drive(DCR *dcr, int slot)
+{
+ DEVICE *dev, *save_dev;
+ JCR *jcr = dcr->jcr;
+ int save_slot;
+ uint32_t timeout = dcr->device->max_changer_wait;
+ bool ok = true;
+ AUTOCHANGER *changer = dcr->dev->device->changer_res;
+ DEVRES *device;
+ bool found = false;
+
+ if (!changer) {
+ return false;
+ }
+ if (changer->device->size() == 1) {
+ return true;
+ }
+
+ foreach_alist(device, changer->device) {
+ if (device->dev && device->dev->Slot == slot) {
+ found = true;
+ dev = device->dev;
+ break;
+ }
+ }
+ if (!found) {
+ return true;
+ }
+ if (dev->is_busy()) {
+ Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
+ dcr->VolumeName, dev->print_name());
+ Dmsg2(200, "Volume %s is in use by device %s\n",
+ dcr->VolumeName, dev->print_name());
+
+ return false;
+ }
+
+ offline_or_rewind_dev(dev);
+ /* We are going to load a new tape, so close the device */
+ force_close_device(dev);
+
+ POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
+ Jmsg(jcr, M_INFO, 0,
+ _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
+ slot, dev->drive_index);
+
+ Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
+ slot, dev->drive_index);
+
+ save_slot = dcr->VolCatInfo.Slot;
+ save_dev = dcr->dev;
+ dcr->dev = dev;
+ dcr->VolCatInfo.Slot = slot;
+ changer_cmd = edit_device_codes(dcr, changer_cmd,
+ dcr->device->changer_command, "unload");
+ lock_changer(dcr);
+ Dmsg1(200, "Run program=%s\n", changer_cmd);
+ int stat = run_program(changer_cmd, timeout, NULL);
+ unlock_changer(dcr);
+ dcr->VolCatInfo.Slot = save_slot;
+ dcr->dev = save_dev;
+ if (stat != 0) {
+ berrno be;
+ be.set_errno(stat);
+ Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
+ slot, dev->drive_index, be.strerror());
+
+ Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
+ slot, dev->drive_index, be.strerror());
+ ok = false;
+ } else {
+ dev->Slot = 0; /* nothing loaded */
+ Dmsg0(200, "Slot unloaded\n");
+ }
+ free_pool_memory(changer_cmd);
+ return ok;
+}
+
+
/*
* List the Volumes that are in the autoloader possibly
bool PreferMountedVols;
bool exact_match;
bool have_volume;
+ bool do_not_wait;
+ bool available_autochanger;
char VolumeName[MAX_NAME_LENGTH];
};
*/
if (ok) {
/*
- * Make up to two passes. The first with PreferMountedVols possibly
- * set to true. In that case, we look only for an available
- * drive with something mounted. If that fails, then we
- * do a second pass with PerferMountedVols set false.
+ * First look for an exact match of Volume name as the
+ * tape may already be mounted.
*/
+ rctx.do_not_wait = true;
rctx.exact_match = true;
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
goto done;
}
rctx.exact_match = false;
+
+ /* Now search if an unused autochanger slot is available */
+ rctx.available_autochanger = true;
+ if ((ok = find_suitable_device_for_job(jcr, rctx))) {
+ goto done;
+ }
+ rctx.available_autochanger = false;
+
+
+ /*
+ * Make up to two passes. The first with PreferMountedVols possibly
+ * set to true. In that case, we look only for an available
+ * drive with something mounted. If that fails, then we
+ * do a second pass with PerferMountedVols set false.
+ */
rctx.PreferMountedVols = jcr->PreferMountedVols;
- ok = find_suitable_device_for_job(jcr, rctx);
- if (ok) {
+ if (!rctx.PreferMountedVols) {
+ rctx.do_not_wait = false;
+ }
+ if ((ok = find_suitable_device_for_job(jcr, rctx))) {
goto done;
}
if (rctx.PreferMountedVols) {
rctx.PreferMountedVols = false;
- ok = find_suitable_device_for_job(jcr, rctx);
- if (ok) {
+ rctx.do_not_wait = false;
+ if ((ok = find_suitable_device_for_job(jcr, rctx))) {
goto done;
}
}
* if there is some device for which we can wait, then
* wait and try again until the wait time expires
*/
- if (!can_wait || !wait_for_device(jcr, first)) {
+ if (rctx.do_not_wait || !can_wait || !wait_for_device(jcr, first)) {
break;
}
first = false; /* first wait complete */
if (dcr) {
free_dcr(dcr);
}
-
return ok;
}
int stat;
Dmsg1(100, "Search res for %s\n", rctx.device_name);
- foreach_res(rctx.device, R_DEVICE) {
- Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name);
- /* Find resource, and make sure we were able to open it */
- if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
- stat = reserve_device(rctx);
- if (stat != 1) {
- return stat;
+ if (!rctx.available_autochanger) {
+ foreach_res(rctx.device, R_DEVICE) {
+ Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name);
+ /* Find resource, and make sure we were able to open it */
+ if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
+ stat = reserve_device(rctx);
+ if (stat != 1) {
+ return stat;
+ }
+ Dmsg1(220, "Got: %s", dir->msg);
+ bash_spaces(rctx.device_name);
+ ok = bnet_fsend(dir, OK_device, rctx.device_name);
+ Dmsg1(100, ">dird dev: %s", dir->msg);
+ return ok ? 1 : -1;
}
- Dmsg1(220, "Got: %s", dir->msg);
- bash_spaces(rctx.device_name);
- ok = bnet_fsend(dir, OK_device, rctx.device_name);
- Dmsg1(100, ">dird dev: %s", dir->msg);
- return ok ? 1 : -1;
}
}
foreach_res(changer, R_AUTOCHANGER) {
return 0;
}
+ /* Check for unused autochanger drive */
+ if (rctx.available_autochanger && dev->num_writers == 0 &&
+ dev->VolHdr.VolumeName[0] == 0) {
+ /* Device is available but not yet reserved, reserve it for us */
+ bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
+ bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
+ return 1; /* reserve drive */
+ }
+
/*
* Handle the case that there are no writers
*/