X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fautochanger.c;h=cac14b78316da460abf67746dca5e419caa6ab8e;hb=513c2c6cf9c7991273cf3330404575aafae6d8a2;hp=503282039cbd61d45f0a1f4081b9ac7f02532cdd;hpb=118517974fcd2c2cae193018ee35b6f2e16879ef;p=bacula%2Fbacula diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index 503282039c..cac14b7831 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -1,38 +1,82 @@ /* - * - * Routines for handling the autochanger. - * - * Kern Sibbald, August MMII - * - * Version $Id$ - */ -/* - Copyright (C) 2000-2005 Kern Sibbald + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2016 Kern Sibbald - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + This notice must be preserved when any source code is + conveyed and/or propagated. + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +/* + * + * Routines for handling the autochanger. + * + * Written by Kern Sibbald, August MMII + * */ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ +static const int dbglvl = 60; + /* Forward referenced functions */ -static int get_autochanger_loaded_slot(DCR *dcr); static void lock_changer(DCR *dcr); static void unlock_changer(DCR *dcr); +static bool unload_other_drive(DCR *dcr, int slot); + +bool DCR::is_virtual_autochanger() +{ + return device->changer_command && + (device->changer_command[0] == 0 || + strcmp(device->changer_command, "/dev/null") == 0); +} + +/* Init all the autochanger resources found */ +bool init_autochangers() +{ + bool OK = true; + AUTOCHANGER *changer; + /* Ensure that the media_type for each device is the same */ + foreach_res(changer, R_AUTOCHANGER) { + DEVRES *device; + foreach_alist(device, changer->device) { + /* + * If the device does not have a changer name or changer command + * defined, used the one from the Autochanger resource + */ + if (!device->changer_name && changer->changer_name) { + device->changer_name = bstrdup(changer->changer_name); + } + if (!device->changer_command && changer->changer_command) { + device->changer_command = bstrdup(changer->changer_command); + } + if (!device->changer_name) { + Jmsg(NULL, M_ERROR, 0, + _("No Changer Name given for device %s. Cannot continue.\n"), + device->hdr.name); + OK = false; + } + if (!device->changer_command) { + Jmsg(NULL, M_ERROR, 0, + _("No Changer Command given for device %s. Cannot continue.\n"), + device->hdr.name); + OK = false; + } + } + } + return OK; +} + /* * Called here to do an autoload using the autochanger, if @@ -48,146 +92,224 @@ static void unlock_changer(DCR *dcr); * 0 on failure (no changer available) * -1 on error on autochanger */ -int autoload_device(DCR *dcr, int writing, BSOCK *dir) +int autoload_device(DCR *dcr, bool writing, BSOCK *dir) { JCR *jcr = dcr->jcr; - DEVICE *dev = dcr->dev; + DEVICE * volatile dev = dcr->dev; int slot; - int drive = dev->device->drive_index; + int drive = dev->drive_index; int rtn_stat = -1; /* error status */ POOLMEM *changer; + if (!dev->is_autochanger()) { + Dmsg1(dbglvl, "Device %s is not an autochanger\n", dev->print_name()); + return 0; + } + + /* An empty ChangerCommand => virtual disk autochanger */ + if (dcr->is_virtual_autochanger()) { + Dmsg0(dbglvl, "ChangerCommand=0, virtual disk changer\n"); + return 1; /* nothing to load */ + } + slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0; /* * Handle autoloaders here. If we cannot autoload it, we - * will return FALSE to ask the sysop. + * will return 0 so that the sysop will be asked to load it. */ - if (writing && dev_cap(dev, CAP_AUTOCHANGER) && slot <= 0) { + if (writing && slot <= 0) { if (dir) { return 0; /* For user, bail out right now */ } + /* ***FIXME*** this really should not be here */ if (dir_find_next_appendable_volume(dcr)) { slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0; } else { slot = 0; + dev->clear_wait(); } } - Dmsg1(400, "Want changer slot=%d\n", slot); + Dmsg4(dbglvl, "Want slot=%d drive=%d InChgr=%d Vol=%s\n", + dcr->VolCatInfo.Slot, drive, + dcr->VolCatInfo.InChanger, dcr->getVolCatName()); changer = get_pool_memory(PM_FNAME); - if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) { + if (slot <= 0) { + /* Suppress info when polling */ + if (!dev->poll) { + Jmsg(jcr, M_INFO, 0, _("No slot defined in catalog (slot=%d) for Volume \"%s\" on %s.\n"), + slot, dcr->getVolCatName(), dev->print_name()); + Jmsg(jcr, M_INFO, 0, _("Cartridge change or \"update slots\" may be required.\n")); + } + rtn_stat = 0; + } else if (!dcr->device->changer_name) { + /* Suppress info when polling */ + if (!dev->poll) { + Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"), + dev->print_name()); + } + rtn_stat = 0; + } else if (!dcr->device->changer_command) { + /* Suppress info when polling */ + if (!dev->poll) { + Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"), + dev->print_name()); + } + rtn_stat = 0; + } else { + /* Attempt to load the Volume */ uint32_t timeout = dcr->device->max_changer_wait; int loaded, status; loaded = get_autochanger_loaded_slot(dcr); + if (loaded < 0) { /* Autochanger error, try again */ + loaded = get_autochanger_loaded_slot(dcr); + } + Dmsg2(dbglvl, "Found loaded=%d drive=%d\n", loaded, drive); - /* If tape we want is not loaded, load it. */ - if (loaded != slot) { - offline_or_rewind_dev(dev); - /* We are going to load a new tape, so close the device */ - force_close_dev(dev); - lock_changer(dcr); - if (loaded != 0 && loaded != -1) { /* must unload drive */ - Dmsg0(400, "Doing changer unload.\n"); - Jmsg(jcr, M_INFO, 0, - _("3303 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), - loaded, drive); - dcr->VolCatInfo.Slot = loaded; /* slot to be unloaded */ - changer = edit_device_codes(dcr, changer, - dcr->device->changer_command, "unload"); - status = run_program(changer, timeout, NULL); - if (status != 0) { - berrno be; - be.set_errno(status); - Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"), - slot, drive, be.strerror()); - goto bail_out; - } + if (loaded <= 0 || loaded != slot) { + POOL_MEM results(PM_MESSAGE); - Dmsg1(400, "unload status=%d\n", status); + /* 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 */ - Dmsg1(400, "Doing changer load slot %d\n", slot); + lock_changer(dcr); + Dmsg2(dbglvl, "Doing changer load slot %d %s\n", slot, dev->print_name()); Jmsg(jcr, M_INFO, 0, _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"), slot, drive); dcr->VolCatInfo.Slot = slot; /* slot to be loaded */ - changer = edit_device_codes(dcr, changer, - dcr->device->changer_command, "load"); - status = run_program(changer, timeout, NULL); + changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load"); + dev->close(); + Dmsg1(dbglvl, "Run program=%s\n", changer); + status = run_program_full_output(changer, timeout, results.addr()); if (status == 0) { Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"), slot, drive); + Dmsg2(dbglvl, "OK: load slot %d, drive %d.\n", slot, drive); + dev->set_slot(slot); /* set currently loaded slot */ + if (dev->vol) { + /* We just swapped this Volume so it cannot be swapping any more */ + dev->vol->clear_swapping(); + } } else { - berrno be; - be.set_errno(status); - Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"), - slot, drive, be.strerror()); - goto bail_out; + berrno be; + be.set_errno(status); + Dmsg4(dbglvl, "Error: load slot %d, drive %d, bad stats=%s.\nResults=%s\n", slot, drive, + be.bstrerror(), results.c_str()); + Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": " + "ERR=%s.\nResults=%s\n"), + slot, drive, be.bstrerror(), results.c_str()); + rtn_stat = -1; /* hard error */ + dev->clear_slot(); /* mark unknown */ } unlock_changer(dcr); - Dmsg2(400, "load slot %d status=%d\n", slot, status); } else { status = 0; /* we got what we want */ + dev->set_slot(slot); /* set currently loaded slot */ } - Dmsg1(400, "After changer, status=%d\n", status); + Dmsg1(dbglvl, "After changer, status=%d\n", status); if (status == 0) { /* did we succeed? */ rtn_stat = 1; /* tape loaded by changer */ } - } else { - rtn_stat = 0; /* no changer found */ } free_pool_memory(changer); return rtn_stat; bail_out: free_pool_memory(changer); - unlock_changer(dcr); return -1; } -static int get_autochanger_loaded_slot(DCR *dcr) +/* + * Returns: -1 if error from changer command + * slot otherwise + * Note, this is safe to do without releasing the drive + * since it does not attempt load/unload a slot. + */ +int get_autochanger_loaded_slot(DCR *dcr) { JCR *jcr = dcr->jcr; - POOLMEM *changer, *results; + DEVICE *dev = dcr->dev; int status, loaded; uint32_t timeout = dcr->device->max_changer_wait; - int drive = dcr->device->drive_index; + int drive = dcr->dev->drive_index; + POOL_MEM results(PM_MESSAGE); + POOLMEM *changer; - results = get_pool_memory(PM_MESSAGE); - changer = get_pool_memory(PM_FNAME); + if (!dev->is_autochanger()) { + return -1; + } + if (!dcr->device->changer_command) { + return -1; + } - lock_changer(dcr); + if (dev->get_slot() > 0 && dev->has_cap(CAP_ALWAYSOPEN)) { + Dmsg1(dbglvl, "Return cached slot=%d\n", dev->get_slot()); + return dev->get_slot(); + } + + /* Virtual disk autochanger */ + if (dcr->is_virtual_autochanger()) { + return 1; + } /* Find out what is loaded, zero means device is unloaded */ - Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"), - drive); - changer = edit_device_codes(dcr, changer, - dcr->device->changer_command, "loaded"); - status = run_program(changer, timeout, results); - Dmsg3(50, "run_prog: %s stat=%d result=%s", changer, status, results); + changer = get_pool_memory(PM_FNAME); + lock_changer(dcr); + /* Suppress info when polling */ + if (!dev->poll && chk_dbglvl(1)) { + Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"), + drive); + } + changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded"); + Dmsg1(dbglvl, "Run program=%s\n", changer); + status = run_program_full_output(changer, timeout, results.addr()); + Dmsg3(dbglvl, "run_prog: %s stat=%d result=%s", changer, status, results.c_str()); if (status == 0) { - loaded = atoi(results); + loaded = str_to_int32(results.c_str()); if (loaded > 0) { - Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"), - drive, loaded); + /* Suppress info when polling */ + if (!dev->poll && chk_dbglvl(1)) { + Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"), + drive, loaded); + } + dev->set_slot(loaded); } else { - Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"), - drive); + /* Suppress info when polling */ + if (!dev->poll && chk_dbglvl(1)) { + Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"), + drive); + } + if (loaded == 0) { /* no slot loaded */ + dev->set_slot(0); + } else { /* probably some error */ + dev->clear_slot(); /* unknown */ + } } } else { berrno be; be.set_errno(status); - Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"), - drive, be.strerror()); + Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: " + "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str()); + Dmsg3(dbglvl, "Error: autochanger loaded? drive %d " + "ERR=%s.\nResults=%s\n", drive, be.bstrerror(), results.c_str()); loaded = -1; /* force unload */ + dev->clear_slot(); /* slot unknown */ } unlock_changer(dcr); free_pool_memory(changer); - free_pool_memory(results); return loaded; } @@ -195,8 +317,13 @@ static void lock_changer(DCR *dcr) { AUTOCHANGER *changer_res = dcr->device->changer_res; if (changer_res) { - Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name); - P(changer_res->changer_mutex); /* Lock changer script */ + int errstat; + Dmsg1(dbglvl, "Locking changer %s\n", changer_res->hdr.name); + if ((errstat=rwl_writelock(&changer_res->changer_lock)) != 0) { + berrno be; + Jmsg(dcr->jcr, M_ERROR_TERM, 0, _("Lock failure on autochanger. ERR=%s\n"), + be.bstrerror(errstat)); + } } } @@ -204,106 +331,345 @@ static void unlock_changer(DCR *dcr) { AUTOCHANGER *changer_res = dcr->device->changer_res; if (changer_res) { - Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name); - V(changer_res->changer_mutex); /* Unlock changer script */ + int errstat; + Dmsg1(dbglvl, "Unlocking changer %s\n", changer_res->hdr.name); + if ((errstat=rwl_writeunlock(&changer_res->changer_lock)) != 0) { + berrno be; + Jmsg(dcr->jcr, M_ERROR_TERM, 0, _("Unlock failure on autochanger. ERR=%s\n"), + be.bstrerror(errstat)); + } + } +} + +/* + * Unload the volume, if any, in this drive + * On entry: loaded == 0 -- nothing to do + * loaded < 0 -- check if anything to do + * loaded > 0 -- load slot == loaded + */ +bool unload_autochanger(DCR *dcr, int loaded) +{ + DEVICE *dev = dcr->dev; + JCR *jcr = dcr->jcr; + int slot; + uint32_t timeout = dcr->device->max_changer_wait; + bool ok = true; + + if (loaded == 0) { + return true; + } + + if (!dev->is_autochanger() || !dcr->device->changer_name || + !dcr->device->changer_command) { + return false; + } + + /* Virtual disk autochanger */ + if (dcr->is_virtual_autochanger()) { + dev->clear_unload(); + return true; + } + + lock_changer(dcr); + if (loaded < 0) { + loaded = get_autochanger_loaded_slot(dcr); + if (loaded < 0) { /* try again, maybe autochanger error */ + loaded = get_autochanger_loaded_slot(dcr); + } } + + if (loaded > 0) { + POOL_MEM results(PM_MESSAGE); + POOLMEM *changer = get_pool_memory(PM_FNAME); + Jmsg(jcr, M_INFO, 0, + _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), + loaded, dev->drive_index); + slot = dcr->VolCatInfo.Slot; + dcr->VolCatInfo.Slot = loaded; + changer = edit_device_codes(dcr, changer, + dcr->device->changer_command, "unload"); + dev->close(); + Dmsg1(dbglvl, "Run program=%s\n", changer); + int stat = run_program_full_output(changer, timeout, results.addr()); + dcr->VolCatInfo.Slot = slot; + if (stat != 0) { + berrno be; + be.set_errno(stat); + Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": " + "ERR=%s\nResults=%s\n"), + loaded, dev->drive_index, be.bstrerror(), results.c_str()); + Dmsg4(dbglvl, "Error: load slot %d, drive %d, bad stats=%s.\nResults=%s\n", + loaded, dev->drive_index, + be.bstrerror(), results.c_str()); + ok = false; + dev->clear_slot(); /* unknown */ + } else { + dev->set_slot(0); /* unload is OK, mark nothing loaded */ + dev->clear_unload(); + } + free_pool_memory(changer); + } + unlock_changer(dcr); + + if (ok) { + free_volume(dev); + } + return ok; +} + +/* + * Unload the slot if mounted in a different drive + */ +static bool unload_other_drive(DCR *dcr, int slot) +{ + DEVICE *dev = NULL; + DEVICE *dev_save; + bool found = false; + AUTOCHANGER *changer = dcr->dev->device->changer_res; + DEVRES *device; + int retries = 0; /* wait for device retries */ + int loaded; + int i; + + if (!changer) { + return false; + } + if (changer->device->size() == 1) { + return true; + } + + /* + * We look for the slot number corresponding to the tape + * we want in other drives, and if possible, unload + * it. + */ + Dmsg1(dbglvl, "Begin wiffle through devices looking for slot=%d\n", slot); + /* + * foreach_alist(device, changer->device) { + * + * The above fails to loop through all devices. It is + * probably a compiler bug. + */ + for (i=0; i < changer->device->size(); i++) { + device = (DEVRES *)changer->device->get(i); + dev = device->dev; + if (!dev) { + Dmsg0(dbglvl, "No dev attached to device\n"); + continue; + } + + dev_save = dcr->dev; + dcr->set_dev(dev); + loaded = get_autochanger_loaded_slot(dcr); + dcr->set_dev(dev_save); + + if (loaded > 0) { + Dmsg4(dbglvl, "Want slot=%d, drive=%d loaded=%d dev=%s\n", + slot, dev->drive_index, loaded, dev->print_name()); + if (loaded == slot) { + found = true; + break; + } + } else { + Dmsg4(dbglvl, "After slot=%d drive=%d loaded=%d dev=%s\n", + slot, dev->drive_index, loaded, dev->print_name()); + } + } + Dmsg1(dbglvl, "End wiffle through devices looking for slot=%d\n", slot); + if (!found) { + Dmsg1(dbglvl, "Slot=%d not found in another device\n", slot); + return true; + } else { + Dmsg3(dbglvl, "Slot=%d drive=%d found in dev=%s\n", slot, dev->drive_index, dev->print_name()); + } + + /* The Volume we want is on another device. */ + if (dev->is_busy()) { + Dmsg4(dbglvl, "Vol %s for dev=%s in use dev=%s slot=%d\n", + dcr->VolumeName, dcr->dev->print_name(), + dev->print_name(), slot); + } + for (int i=0; i < 3; i++) { + if (dev->is_busy()) { + Dmsg0(40, "Device is busy. Calling wait_for_device()\n"); + wait_for_device(dcr, retries); + continue; + } + break; + } + if (dev->is_busy()) { + Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"), + dcr->VolumeName, dcr->dev->print_name(), dev->print_name()); + Dmsg4(dbglvl, "Vol %s for dev=%s is busy dev=%s slot=%d\n", + dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot()); + Dmsg2(dbglvl, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved()); + volume_unused(dcr); + return false; + } + return unload_dev(dcr, dev); +} + +/* + * Unconditionally unload a specified drive + */ +bool unload_dev(DCR *dcr, DEVICE *dev) +{ + JCR *jcr = dcr->jcr; + bool ok = true; + uint32_t timeout = dcr->device->max_changer_wait; + AUTOCHANGER *changer = dcr->dev->device->changer_res; + DEVICE *save_dev; + int save_slot; + + if (!changer) { + return false; + } + + save_dev = dcr->dev; /* save dcr device */ + dcr->set_dev(dev); /* temporarily point dcr at other device */ + + get_autochanger_loaded_slot(dcr); + + /* Fail if we have no slot to unload */ + if (dev->get_slot() <= 0) { + if (dev->get_slot() < 0) { + Dmsg1(dbglvl, "Cannot unload, slot not defined. dev=%s\n", + dev->print_name()); + } + dcr->set_dev(save_dev); + return false; + } + + save_slot = dcr->VolCatInfo.Slot; + dcr->VolCatInfo.Slot = dev->get_slot(); + + + POOLMEM *changer_cmd = get_pool_memory(PM_FNAME); + POOL_MEM results(PM_MESSAGE); + lock_changer(dcr); + Jmsg(jcr, M_INFO, 0, + _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), + dev->get_slot(), dev->drive_index); + + Dmsg2(dbglvl, "Issuing autochanger \"unload slot %d, drive %d\" command.\n", + dev->get_slot(), dev->drive_index); + + changer_cmd = edit_device_codes(dcr, changer_cmd, + dcr->device->changer_command, "unload"); + dev->close(); + Dmsg2(dbglvl, "close dev=%s reserve=%d\n", dev->print_name(), + dev->num_reserved()); + Dmsg1(dbglvl, "Run program=%s\n", changer_cmd); + int stat = run_program_full_output(changer_cmd, timeout, results.addr()); + dcr->VolCatInfo.Slot = save_slot; + if (stat != 0) { + berrno be; + be.set_errno(stat); + Jmsg(jcr, M_INFO, 0, _("3997 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"), + dev->get_slot(), dev->drive_index, be.bstrerror()); + Dmsg4(dbglvl, "Error: load slot %d, drive %d, bad stats=%s.\nResults=%s\n", + dev->get_slot(), dev->drive_index, + be.bstrerror(), results.c_str()); + ok = false; + dev->clear_slot(); /* unknown */ + } else { + Dmsg2(dbglvl, "Slot %d unloaded %s\n", dev->get_slot(), dev->print_name()); + dev->set_slot(0); /* unload OK, mark nothing loaded */ + dev->clear_unload(); + } + unlock_changer(dcr); + + if (ok) { + free_volume(dev); + } + dcr->set_dev(save_dev); + free_pool_memory(changer_cmd); + return ok; } + /* * List the Volumes that are in the autoloader possibly * with their barcodes. * We assume that it is always the Console that is calling us. */ -bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd) +bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd) { DEVICE *dev = dcr->dev; - JCR *jcr = dcr->jcr; uint32_t timeout = dcr->device->max_changer_wait; POOLMEM *changer; BPIPE *bpipe; - int slot, loaded; int len = sizeof_pool_memory(dir->msg) - 1; - bool ok = false; int stat; - if (!dev_cap(dev, CAP_AUTOCHANGER) || !dcr->device->changer_name || + if (!dev->is_autochanger() || !dcr->device->changer_name || !dcr->device->changer_command) { - bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"), + if (strcasecmp(cmd, "drives") == 0) { + dir->fsend("drives=1\n"); + } + dir->fsend(_("3993 Device %s not an autochanger device.\n"), dev->print_name()); return false; } - changer = get_pool_memory(PM_FNAME); - /* List command? */ - if (strcmp(cmd, "list") == 0) { - int drive = dev->device->drive_index; - /* Yes, to get a good listing, we unload any volumes */ - offline_or_rewind_dev(dev); - /* We are going to load a new tape, so close the device */ - force_close_dev(dev); - - /* First unload any tape */ - loaded = get_autochanger_loaded_slot(dcr); - if (loaded > 0) { - bnet_fsend(dir, - _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), - loaded, drive); - slot = dcr->VolCatInfo.Slot; - dcr->VolCatInfo.Slot = loaded; - changer = edit_device_codes(dcr, changer, - dcr->device->changer_command, "unload"); - lock_changer(dcr); - int stat = run_program(changer, timeout, NULL); - unlock_changer(dcr); - 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, drive, be.strerror()); - } - dcr->VolCatInfo.Slot = slot; + if (strcasecmp(cmd, "drives") == 0) { + AUTOCHANGER *changer_res = dcr->device->changer_res; + int drives = 1; + if (changer_res) { + drives = changer_res->device->size(); } + dir->fsend("drives=%d\n", drives); + Dmsg1(dbglvl, "drives=%d\n", drives); + return true; } + /* If listing, reprobe changer */ + if (bstrcasecmp(cmd, "list") || bstrcasecmp(cmd, "listall")) { + dcr->dev->set_slot(0); + get_autochanger_loaded_slot(dcr); + } + + changer = get_pool_memory(PM_FNAME); + lock_changer(dcr); /* Now issue the command */ - changer = edit_device_codes(dcr, changer, + changer = edit_device_codes(dcr, changer, dcr->device->changer_command, cmd); - bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd); - lock_changer(dcr); + dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd); bpipe = open_bpipe(changer, timeout, "r"); if (!bpipe) { - unlock_changer(dcr); - bnet_fsend(dir, _("3996 Open bpipe failed.\n")); - goto bail_out; + dir->fsend(_("3996 Open bpipe failed.\n")); + goto bail_out; /* TODO: check if we need to return false */ } - if (strcmp(cmd, "list") == 0) { + if (bstrcasecmp(cmd, "list") || bstrcasecmp(cmd, "listall")) { /* Get output from changer */ while (fgets(dir->msg, len, bpipe->rfd)) { dir->msglen = strlen(dir->msg); - Dmsg1(100, "msg); - bnet_send(dir); + Dmsg1(dbglvl, "msg); + dir->send(); } - } else { + } else if (strcasecmp(cmd, "slots") == 0 ) { + char buf[100], *p; /* For slots command, read a single line */ - bstrncpy(dir->msg, "slots=", len); - fgets(dir->msg+6, len-6, bpipe->rfd); - dir->msglen = strlen(dir->msg); - Dmsg1(100, "msg); - bnet_send(dir); + buf[0] = 0; + fgets(buf, sizeof(buf)-1, bpipe->rfd); + buf[sizeof(buf)-1] = 0; + /* Strip any leading space in front of # of slots */ + for (p=buf; B_ISSPACE(*p); p++) + { } + dir->fsend("slots=%s", p); + Dmsg1(dbglvl, "msg); } - + stat = close_bpipe(bpipe); - unlock_changer(dcr); if (stat != 0) { berrno be; be.set_errno(stat); - bnet_fsend(dir, "Autochanger error: ERR=%s\n", be.strerror()); + dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror()); } - bnet_sig(dir, BNET_EOD); - ok = true; bail_out: + unlock_changer(dcr); free_pool_memory(changer); return true; } @@ -317,6 +683,7 @@ bail_out: * %d = changer drive index * %f = Client's name * %j = Job name + * %l = archive control channel name * %o = command * %s = Slot base 0 * %S = Slot base 1 @@ -348,6 +715,9 @@ char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd) case 'c': str = NPRT(dcr->device->changer_name); break; + case 'l': + str = NPRT(dcr->device->control_name); + break; case 'd': sprintf(add, "%d", dcr->dev->drive_index); str = add; @@ -367,7 +737,15 @@ char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd) str = dcr->jcr->Job; break; case 'v': - str = NPRT(dcr->VolumeName); + if (dcr->VolCatInfo.VolCatName[0]) { + str = dcr->VolCatInfo.VolCatName; + } else if (dcr->VolumeName[0]) { + str = dcr->VolumeName; + } else if (dcr->dev->vol && dcr->dev->vol->vol_name) { + str = dcr->dev->vol->vol_name; + } else { + str = dcr->dev->VolHdr.VolumeName; + } break; case 'f': str = NPRT(dcr->jcr->client_name);