diff options
author | Carsten Leonhardt <leo@debian.org> | 2019-02-27 23:12:38 +0100 |
---|---|---|
committer | Carsten Leonhardt <leo@debian.org> | 2019-02-27 23:12:38 +0100 |
commit | d94bd19c05a1b654a7851821f7c4b7cdd70e1515 (patch) | |
tree | be7cb2ff99167e6dbc12c2c99acad4b386f92bf5 /contrib | |
parent | b695062f151fdfa5cd19cd20918bdf2dc6fd6332 (diff) |
Import Upstream version 1.2.16rel
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/MTX.html | 209 | ||||
-rw-r--r-- | contrib/README | 3 | ||||
-rw-r--r-- | contrib/TapeChanger-MTX-0.71b.tar.gz | bin | 0 -> 6179 bytes | |||
-rwxr-xr-x | contrib/config_sgen_solaris.sh | 153 | ||||
-rw-r--r-- | contrib/mtx-changer | 431 | ||||
-rw-r--r-- | contrib/mtx.py | 306 | ||||
-rw-r--r-- | contrib/mtxctl-0.0.2.tar.gz | bin | 0 -> 5688 bytes | |||
-rw-r--r-- | contrib/tapechanger.html | 209 | ||||
-rw-r--r-- | contrib/tapeinfo.py | 55 | ||||
-rw-r--r-- | contrib/tapeload.pl | 118 | ||||
-rw-r--r-- | contrib/tapeunload.pl | 98 |
11 files changed, 1582 insertions, 0 deletions
diff --git a/contrib/MTX.html b/contrib/MTX.html new file mode 100644 index 0000000..8ea9fce --- /dev/null +++ b/contrib/MTX.html @@ -0,0 +1,209 @@ +<HTML> +<HEAD> +<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE> +<LINK REV="made" HREF="mailto:none"> +</HEAD> + +<BODY> + +<A NAME="__index__"></A> +<!-- INDEX BEGIN --> + +<UL> + + <LI><A HREF="#name">NAME</A></LI> + <LI><A HREF="#synopsis">SYNOPSIS</A></LI> + <LI><A HREF="#description">DESCRIPTION</A></LI> + <LI><A HREF="#variables">VARIABLES</A></LI> + <LI><A HREF="#usage">USAGE</A></LI> + <LI><A HREF="#notes">NOTES</A></LI> + <LI><A HREF="#requirements">REQUIREMENTS</A></LI> + <LI><A HREF="#todo">TODO</A></LI> + <LI><A HREF="#see also">SEE ALSO</A></LI> + <LI><A HREF="#author">AUTHOR</A></LI> + <LI><A HREF="#copyright">COPYRIGHT</A></LI> +</UL> +<!-- INDEX END --> + +<HR> +<P> +<H1><A NAME="name">NAME</A></H1> +<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P> +<P> +<HR> +<H1><A NAME="synopsis">SYNOPSIS</A></H1> +<PRE> + use TapeChanger::MTX;</PRE> +<PRE> + my $loaded = TapeChanger::MTX->loadedtape; + print "Currently loaded: $loaded\n" if ($loaded);</PRE> +<PRE> + TapeChanger::MTX->loadtape('next'); + my $nowloaded = TapeChanger::MTX->loadedtape; + print "Currently loaded: $nowloaded\n" if ($nowloaded); +</PRE> +<PRE> + +See below for more available functions.</PRE> +<P> +<HR> +<H1><A NAME="description">DESCRIPTION</A></H1> +<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx' +tape library program. It is meant to work with a simple shell/perl script +to load and unload tapes as appropriate, and to provide a interface for +more complicated programs to do the same. The below functions and +variables should do as good a job as explaining this as anything.</P> +<P> +<HR> +<H1><A NAME="variables">VARIABLES</A></H1> +<DL> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT +=item $TapeChanger::MTX::MTX</A></STRONG><BR> +<DD> +What is the location of the 'mt' and 'mtx' binaries? Can be set with +'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and +'/usr/local/sbin/mtx'. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR> +<DD> +What are the names of the tape (DRIVE) and changer (CONTROL) device +nodes? Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to +'/dev/rmt/0' and '/dev/changer' respectively. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR> +<DD> +Does the tape drive have to eject the tape before the changer retrieves +it? It's okay to say 'yes' if it's not necessary, in most cases. Can be +set with $EJECT in ~/.mtxrc, or defaults to '1'. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR> +<DD> +How long should we wait to see if the drive is ready, in seconds, after +mounting a volume? Can be set with $READY_TIME in ~/.mtxrc, or defaults +to 60. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR> +<DD> +Print debugging information? Set to '0' for normal verbosity, '1' for +debugging information, or '-1' for 'quiet mode' (be as quiet as possible). +<P></P></DL> +<P> +<HR> +<H1><A NAME="usage">USAGE</A></H1> +<P>This module uses the following functions:</P> +<DL> +<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR> +<DD> +Runs 'mtx' and 'mt' as appropriate. <CODE>COMMAND</CODE> is the command you're +trying to send to them. Uses 'warn()' to print the commands to the screen +if $TapeChanger::MTX::DEBUG is set. +<P></P> +<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR> +<DD> +Returns the number of drives, number of slots, and currently loaded tape +values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>. +<P></P> +<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR> +<DD> +Loads a tape into the tape changer, and waits until the drive is again +ready to be written to. <CODE>SLOT</CODE> can be any of the following (with the +relevant function indicated): +<PRE> + current C<loadedtape()> + prev C<loadprevtape()> + next C<loadnexttape()> + first C<loadfirsttape()> + last C<loadlasttape()> + 0 C<_ejectdrive()> + 1..99 Loads the specified tape number, ejecting whatever is + currently in the drive.</PRE> +<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0. Returns 0 if +successful, an error string otherwise.</P> +<P></P> +<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR> +<DD> +Loads the next, previous, first, and last tapes in the changer +respectively. Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>, +<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively. +<P></P> +<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR> +<DD> +Ejects the tape, by first ejecting the tape from the drive +(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its +slot (<STRONG>tape_cmd(unload)</STRONG>). Returns 1 if successful, 0 otherwise. +<P></P> +<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR> +<DD> +Resets the changer, ejecting the tape and loading the first one from the +changer. +<P></P> +<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR> +<DD> +Checks to see if the drive is ready or not, by waiting for up to +$TapeChanger::MTX::READY_TIME seconds to see if it can get status +information using <STRONG>mt_cmd(status)</STRONG>. Returns 1 if so, 0 otherwise. +<P></P> +<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR> +<DD> +Returns a string containing the loaded tape and the drive that it's +mounted on. +<P></P> +<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR> +<DD> +Does some quick checks to see if you're actually capable of using this +module, based on your user permissions. Returns a list of problems if +there are any, 0 otherwise. +<P></P></DL> +<P> +<HR> +<H1><A NAME="notes">NOTES</A></H1> +<P>~/.mtxrc is automatically loaded when this module is used, if it exists, +using do(). This could cause security problems if you're trying to use +this with <CODE>setuid()</CODE> programs - so just don't do that. If you want someone +to have permission to mess with the tape drive and/or changer, let them +have that permission directly.</P> +<P> +<HR> +<H1><A NAME="requirements">REQUIREMENTS</A></H1> +<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and +reader connected to the system.</P> +<P> +<HR> +<H1><A NAME="todo">TODO</A></H1> +<P>Support for Input/Export slots is not included, though it may be later. +Possibly works for multiple drives per changer, but I haven't tested it, +so I probably missed something. 'load previous' doesn't actually work, +because mtx doesn't support it (though the help says it does).</P> +<P> +<HR> +<H1><A NAME="see also">SEE ALSO</A></H1> +<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>. Inspired by <STRONG>stc-changer</STRONG>, which comes +with the AMANDA tape backup package (http://www.amanda.org), and MTX, +available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P> +<P> +<HR> +<H1><A NAME="author">AUTHOR</A></H1> +<P>Tim Skirvin <<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>></P> +<P> +<HR> +<H1><A NAME="copyright">COPYRIGHT</A></H1> +<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and +Tim Skirvin <<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>>.</P> + +</BODY> + +</HTML> diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..832ee0a --- /dev/null +++ b/contrib/README @@ -0,0 +1,3 @@ +Everything in here is copyrighted by its original copyright holder. +The contents of this directory are not maintained as part of the 'mtx' +project, they are maintained by their original copyright holders. diff --git a/contrib/TapeChanger-MTX-0.71b.tar.gz b/contrib/TapeChanger-MTX-0.71b.tar.gz Binary files differnew file mode 100644 index 0000000..a42a2e0 --- /dev/null +++ b/contrib/TapeChanger-MTX-0.71b.tar.gz diff --git a/contrib/config_sgen_solaris.sh b/contrib/config_sgen_solaris.sh new file mode 100755 index 0000000..1d8938b --- /dev/null +++ b/contrib/config_sgen_solaris.sh @@ -0,0 +1,153 @@ +#!/bin/sh +# Copyright 2001 Enhanced Software Technologies Inc. +# All Rights Reserved +# +# This software is licensed under the terms of the Free Software Foundation's +# General Public License, version 2. See http://www.fsf.org for more +# inforation on the General Public License. It is released for public use in +# the hope that others will find it useful. [NOTE FROM ERIC: Note that +# this is now unmaintained, unless someone wishes to volunteer. In other +# words, if you have a problem with this script, please fix it and forward +# a new version of the script with your EMAIL address as the one to contact +# about it :-) ] +# +# usage: config_sgen_solaris.sh check|[un]install +# +# This configures sgen under Solaris (we hope! :-). Note that this +# *CAN* do a reboot of the system. Do NOT call this function unless +# you are willing to let it do a reboot of the system! Also note that +# this *must* be run as user 'root', since it does highly grokety things. + + +mode="$1" +cvs upd +SGEN="/kernel/drv/sgen" +SGEN_CONF="/kernel/drv/sgen.conf" + +do_check() { + if test ! -f $SGEN_CONF; then + # sgen.conf not installed... + return 1 + fi + + changer_type_count=`grep "changer" $SGEN_CONF | grep -v "^#" | wc -l` + target_count=`grep "target=" $SGEN_CONF | grep -v "^#" | wc -l` + + if test $changer_type_count = 0 -o $target_count = 0; then + # sgen.conf not configured + return 1 + fi + + # sgen.conf installed, and configured + return 0 +} + +do_install() { + + # see if already installed + do_check + if test $? = 0; then + echo "sgen already configured, skipping" + return 0 # successfully installed (?) + fi + + if test ! -f $SGEN; then + echo "sgen driver not installed, aborting" + return 1 + fi + + echo "configuring sgen driver..." + + echo 'device-type-config-list="changer"; # BRU-PRO' >>$SGEN_CONF + target=0 + while test $target -le 15; do + echo "name=\"sgen\" class=\"scsi\" target=$target lun=0; # BRU-PRO" >>$SGEN_CONF + target=`expr $target + 1` + done + + echo "Attempting to reload driver..." + rem_drv sgen >/dev/null 2>&1 + add_drv sgen + if test "$?" != "0"; then + # failed + touch /reconfigure + echo "Driver was successfully configured, but could not be re-loaded." + echo "The system must be rebooted for the driver changes to take effect." + + ans="" + while test "$ans" = ""; do + printf "Do you want to reboot now (shutdown -g 1 -y -i 6)? [Y/n] " + read ans + + if test "$ans" = "Y"; then + ans="y" + fi + + if test "$ans" = "N"; then + ans="n" + fi + + if test "$ans" != "y" -a "$ans" != "n"; then + echo "Please enter 'y' or 'n'" + ans="" + fi + done + + if test "$ans" = "y"; then + shutdown -g 1 -y -i 6 + # will be killed by reboot... + while true; do + echo "Waiting for reboot..." + sleep 300 + done + fi + + # not rebooted, exit with error + return 2 + fi + + # successful + return 0 +} + +do_uninstall() { + do_check + if test $? = 1; then + echo "sgen not configured, skipping" + return 0 # successfully uninstalled (?) + fi + + printf "removing BRU-PRO configuration from $SGEN_CONF..." + grep -v "# BRU-PRO" $SGEN_CONF > ${SGEN_CONF}.$$ || return 1 + cat ${SGEN_CONF}.$$ >${SGEN_CONF} || return 1 + rm -f ${SGEN_CONF}.$$ >/dev/null || return 1 + printf "done\n" + + touch /reconfigure + printf "Devices will be reconfigured at next reboot.\n" + return 0 +} + +uname | grep SunOS >/dev/null 2>&1 +if test $? != 0; then + echo "$0: not on Solaris, ABORT!" + exit 99 +fi + +case "$mode" in + check) + do_check + ;; + install) + do_install + ;; + uninstall) + do_uninstall + ;; + *) + echo "usage: $0 check|[un]install" + exit 1 + ;; +esac + +exit $? diff --git a/contrib/mtx-changer b/contrib/mtx-changer new file mode 100644 index 0000000..cf68ddd --- /dev/null +++ b/contrib/mtx-changer @@ -0,0 +1,431 @@ +#! /bin/sh +############################################################################### +# AMANDA Tape Changer script for use with the MTX tape changer program +# Version 1.0 - Tue Feb 20 13:59:39 CST 2001 +# +# Based on 'stc-changer' by Eric Berggren (eric@ee.pdx.edu) +# Updated by Tim Skirvin (tskirvin@ks.uiuc.edu) +# +# Given that there's no license...let's make this the Perl Artistic License. +# Just make sure you give me and Eric credit if you modify this. +############################################################################### + +### USER CONFIGURATION +# Name of the tape drive (takes place of "tapedev" option in amanda.conf) +# and default driver number in library (usu 0) that DRIVE_NAME points to +DRIVE_NAME="/dev/rmt/0n" +DRIVE_NUM=0 + +# Location of "STC" command and control device +MTX_CMD="/usr/local/sbin/mtx"; +MTX_CONTROL="/dev/scsi/changer/c4t1d0"; + +# Whether tape drive must eject tape before changer retrieves +# (ie, EXB-2x0). Usually okay if set while not necessary, bad if +# required but not set. +DRIVE_MUST_EJECT=1 + +# How long to check drive readiness (in seconds) after mounting (or +# ejecting) a volume (on some libraries, the motion or eject command may +# complete before the drive has the volume fully mounted and online, +# or ready for retrieval, resulting in "Drive not ready"/"Media not +# ready" errors). Do an "mt status" command every 5 seconds upto this +# time. +DRIVE_READY_TIME_MAX=120 + +# tape "mt" command location... +MT_CMD="/usr/bin/mt" # called via "MT_CMD -f DRIVE_NAME rewind" & + # "MT_CMD -f DRIVE_NAME offline" to eject + # and "MT_CMD -f DRIVE_NAME status" to get ready info + +############################################################################## +# +NumDrives=-1 +NumSlots=-1 +LastSlot=-1 +LoadedTape=-1 + +# +# Usage information +# +usage() +{ + echo + echo "Usage: $Progname <command> [arg...]" + echo " -info reports capability and loaded tape" + echo " -slot <slot> loads specified tape into drive" + echo " current reports current mounted tape" + echo " next loads logically next tape (loops to top)" + echo " prev loads logically previous tape (loops to bot)" + echo " first loads first tape" + echo " last loads last tape" + echo " 0..99 loads tape from specified slot#" + echo " -eject uloads current mounted tape" + echo " -reset resets changer (and drive); loads first tape" + echo + exit 5 +} + +# +# Perform "stc" changer command (& handle the "fatal" errors) +# else, set 'CommandResStr' and 'CommandRawResStr' to the result string +# and 'CommandResCode' to the exit code +# +dotapecmd() +{ + cmd=$1 + arg=$2 + + CommandResStr=`$MTX_CMD $MTX_CONTROL $cmd $arg 2>&1` + CommandRawResStr=$CommandResStr + CommandResCode=$? + + CommandResStr=`echo $CommandResStr | head -1 | sed 's/^[^:]*: //'` + if [ $CommandResCode -gt 1 ]; then + echo "0 $Progname: returned $CommandResStr" + exit 2 + fi +} + +# +# Unload tape from drive (a drive command; "ejecttape" is a changer command +# to actually retrieve the tape). Needed by some changers (controlled by +# setting "DRIVE_MUST_EJECT") +# +ejectdrive() +{ + # Tell drive to eject tape before changer retrieves; req'd by some + # drives (ie, EXB-2x0). Not needed by QDLT-4x00. Do a "rewind" + # command first, then "offline" to eject (instead of "rewoffl") + # + if [ "$DRIVE_MUST_EJECT" -ne 0 ]; then + mtresstr=`$MT_CMD -f $DRIVE_NAME rewind 2>&1` + mtrescode=$? + + if [ $mtrescode -ne 0 ]; then + if echo "$mtresstr" | egrep -s 'no tape'; then + :; # no tape mounted; assume okay... + else + # can't eject tape, bad; output: <tape#> reason + echo "0 $mtresstr" + exit 1 + fi + else + mtresstr=`$MT_CMD -f $DRIVE_NAME offline 2>&1` + mtrescode=$? + + checkdrive 1 + fi + fi +} + +# +# Check drive readiness after (un)mounting a volume (which may take a while +# after the volume change command completes) +# +checkdrive() +{ + unmounting=$1 + + if [ "$DRIVE_READY_TIME_MAX" -gt 0 ]; then + + # sleep time between checks + pausetime=5 + + # number of interations to check + numchecks=`expr $DRIVE_READY_TIME_MAX / $pausetime` + if [ "$numchecks" -eq 0 ]; then + numchecks=1 + fi + + # check until success, or out of attempts... + while [ "$numchecks" -gt 0 ]; do + mtresstr=`$MT_CMD -f $DRIVE_NAME status 2>&1` + mtrescode=$? + + if [ $mtrescode -eq 0 ]; then + # Success ? + return 0 + else + # pause, before trying again.... + if [ "$numchecks" -gt 1 ]; then + sleep $pausetime + + # if unmounting a volume, check for 'mt' command + # failure; (sleep first for additional comfort) + if [ "$unmounting" -ne 0 ]; then + return 0 + fi + fi + fi + numchecks=`expr $numchecks - 1` + done + + # failed; output: -1 reason + echo "-1 drive won't report ready" + exit 1 + fi +} + +# +# Get changer parameters +# +getchangerparms() +{ + dotapecmd status + if [ $CommandResCode -eq 0 ] && \ + echo "$CommandResStr" | egrep -s '^Storage Changer'; then + + NumDrives=`echo $dspec | wc -l` + NumDrives=`echo "$CommandRawResStr" | \ + grep 'Data Transfer Element' | wc -l` + if [ "$NumDrives" -le "$DRIVE_NUM" ]; then + echo "$Program: Invalid drive # specified ($DRIVE_NUM > $NumDrives)" + exit 3 + fi + # grep 'Data Transfer Element $DRIVE_NUM' | \ + LoadedTape=`echo "$CommandRawResStr" | \ + grep 'Data Transfer Element' | \ + grep 'Storage Element [0-9]' | \ + awk '{ print $7 }' ` + if [ -z "$LoadedTape" -o "$LoadedTape" = "e" ]; then + LoadedTape=-1 + fi + NumSlots=`echo "$CommandRawResStr" | \ + grep 'Storage Element [0-9]\{1,\}:' | \ + grep -v 'Data Element' | \ + wc -l | sed -e 's/ //g' ` + LastSlot=`expr $NumSlots - 1` + else + echo \ + "$Progname: Can't get changer parameters; Result was $CommandResStr" + exit 3 + fi +} + +# +# Display changer info +# +changerinfo() +{ + getchangerparms + + # output status string: currenttape numslots randomaccess? + echo "$LoadedTape $NumSlots 1" + exit 0 +} + +# +# Eject current mounted tape +# +ejecttape() +{ + getchangerparms + ct=$LoadedTape + + # If no tape reported mounted, assume success (could be bad if changer + # lost track of tape) + # + if [ $ct -lt 0 ]; then + CommandResCode=0 + else + ejectdrive + dotapecmd unload + fi + + if [ $CommandResCode -ne 0 ]; then + # failed; output: <tape#> reason + echo "$ct $CommandResStr" + exit 1 + else + # success; output: <tape#> drive + echo "$ct $DRIVE_NAME" + exit 0 + fi +} + +# +# Move specified tape into drive (operation level) +# +doloadtape() +{ + slot=$1 + if [ "$slot" -eq "$LoadedTape" ]; then + return 0 + fi + ejectdrive + dotapecmd load $slot + return $CommandResCode +} + +# +# Load next available tape into drive +# +loadnexttape() +{ + curslot=$1 + direction=$2 + + startslot=$curslot + while true; do + if doloadtape $curslot; then + return 0 + else + if echo $CommandResStr | egrep -s 'Slot.*reported empty'; then + + if [ "$direction" -lt 0 ]; then + curslot=`expr $curslot - 1` + if [ "$curslot" -lt 0 ]; then + curslot=$LastSlot + fi + else + curslot=`expr $curslot + 1` + if [ "$curslot" -gt "$LastSlot" ]; then + curslot=0 + fi + fi + + # Check if we're back to where we started... + if [ "$curslot" = "$startslot" ]; then + if [ "$direction" -lt 0 ]; then + CommandResStr="No previous volume available" + else + CommandResStr="No subsequent volume available" + fi + return 1 + fi + else + return 1 + fi + fi + done +} + +# +# Report loadtape() status +# +reportstatus() +{ + if [ $CommandResCode -eq 0 ]; then + # success; output currenttape drivename + echo "$LoadedTape $DRIVE_NAME" + exit 0 + else + # failed (empty slot?); output currenttape reason + echo "$LoadedTape $CommandResStr" + exit 1 + fi +} + + +# +# Move specified tape into drive (command level) +# +loadtape() +{ + slot=$1 + + getchangerparms + + case "$slot" in + current) + if [ $LoadedTape -lt 0 ]; then + CommandResStr="Can't determine current tape; drive empty ?" + CommandResCode=1 + fi + ;; + prev) + if [ $LoadedTape -le 0 ]; then + loadnexttape $LastSlot -1 + else + loadnexttape `expr $LoadedTape - 1` -1 + fi + ;; + next) + if [ $LoadedTape -ge $LastSlot -o $LoadedTape -lt 0 ]; then + loadnexttape 0 1 + else + loadnexttape `expr $LoadedTape + 1` 1 + fi + ;; + first) + loadnexttape 0 1 + ;; + last) + loadnexttape $LastSlot -1 + ;; + [0-9]*) + doloadtape $slot + ;; + *) + # error; no valid slot specified + echo "$Progname: No valid slot specified" + exit 1 + ;; + esac + + if [ $CommandResCode -eq 0 ]; then + getchangerparms + checkdrive + fi + reportstatus +} + +# +# Reset changer to known state +# +resetchanger() +{ + ejectdrive + dotapecmd reset + if [ $CommandResCode -ne 0 ]; then + # failed; output: failed? reason + echo "-1 $CommandResStr" + exit 2; + else + loadtape first + fi +} + +############################################################################# +# +# MAIN +# +Progname=`basename $0` + +if [ ! -x "$MTX_CMD" ]; then + echo "-1 $Progname: cannot run STC command ($MTX_CMD)" + exit 2 +fi +if [ -n "$MTX_CONTROL" ]; then + if echo "$MTX_CONTROL" | egrep -s '^-f'; then + :; + else + MTX_CONTROL="-f $MTX_CONTROL" + fi +fi +if [ -n "$DRIVE_NUM" ]; then + DRIVE_NUM=0 +fi + +if [ $# -ge 1 ]; then command=$1; else command="-usage"; fi + +case "$command" in + -info) + changerinfo + ;; + -slot) + loadtape $2 + ;; + -eject) + ejecttape + ;; + -reset) + resetchanger + ;; + *) + usage + ;; +esac + +exit 0 diff --git a/contrib/mtx.py b/contrib/mtx.py new file mode 100644 index 0000000..43564c3 --- /dev/null +++ b/contrib/mtx.py @@ -0,0 +1,306 @@ +# Copyright 2000 Enhanced Software Technologies Inc. +# Released under Free Software Foundation's General Public License, +# Version 2 or above +# +# This is an example of how to parse the 'mtx' output from a scripting +# language. +# +# Routine to call 'mtx' and read status, or +# whatever. +# +# We do this here rather than creating a Python "C" module because: +# 1) Reduces complexity of compile environment +# 2) Easier debugging. +# 3) More in keeping with the Unix philosophy of things. +# 4) Easier to add new functionality. +# +# + +import string +import os +import time # we can do some waiting here... + +def readpipe(command): + result="" + + infile=os.popen(command,"r") + + try: + s=infile.read() + except: + s="" + pass + if not s: + return None # didn't get anything :-(. + result=result+s + return result + + + + +# these are returned by the mtx.status routine: +class slotstatus: + def __init__(self,slotnum,middle,barcode=None): + middle=string.strip(middle) + try: + left,right=string.split(middle," ") + except: + left=middle + right=None + pass + # Note: We will not be able to test this at the moment since the + # 220 has no import/export port! + if right=="IMPORT/EXPORT": + self.importexport=1 + else: + self.importexport=0 + pass + self.slotnum=int(slotnum) # make sure it's an integer... + if left=="Full": + self.full=1 + else: + self.full=None + pass + if not barcode: + self.voltag=None + else: + l=string.split(barcode,"=",1) + self.voltag=l[1] + pass + return + pass + +# Drive status lines have this format: +#Data Transfer Element 0:Full (Storage Element 10 Loaded):VolumeTag = B0000009H +#Data Transfer Element 1:Empty + +class drivestatus: + def __init__(self,slotnum,middle,barcode=None): + self.slotnum=slotnum + if middle=="Empty": + self.full=None + self.origin=None + self.voltag=None + return + else: + self.full=1 + pass + + # okay, we know we have a tape in the drive. + # split and find out our origin: we will always have + # an origin, even if the #$@% mtx program had to dig one + # out of the air: + + l=string.split(middle," ") + self.origin=int(l[3]) + + if not barcode: + self.voltag=None # barcode of this element. + else: + l=string.split(barcode," ") + self.voltag=string.strip(l[2]) + pass + return + pass + +# This is the return value for mtx.status. +class mtxstatus: + def __init__(self): + self.numdrives=None + self.numslots=None + self.numexport=None + self.drives=[] # a list of drivestatus instances. + self.slots=[] # a list of slotstatus instances + self.export=[] # another list of slotstatus instances + return + pass + +# call 'mtx' and get barcode info, if possible: +# okay, we now have a string that consists of a number of lines. +# we want to explode this string into its component parts. +# Example format: +# Storage Changer /dev/sgd:2 Drives, 21 Slots +# Data Transfer Element 0:Full (Storage Element '5' Loaded) +# Data Transfer Element 1:Empty +# Storage Element 1:Full :VolumeTag=CLNA0001 +# Storage Element 2:Full :VolumeTag=B0000009 +# Storage Element 3:Full :VolumeTag=B0000010 +# .... +# What we want to do, then, is: +# 1) Turn it into lines by doing a string.split on newline. +# 2) Split the 1st line on ":" to get left and right. +# 3) Split the right half on space to get #drives "Drives," #slots +# 4) process the drives (Full,Empty, etc.) +# 5) For each of the remaining lines: Split on ':' +# 6) Full/Empty status is in 2) +# +configdir="/opt/brupro/bin" # sigh. + +def status(device): + retval=mtxstatus() + command="%s/mtx -f %s status" % (configdir,device) + result=readpipe(command) + if not result: + return None # sorry! + # now to parse: + + try: + lines=string.split(result,"\n") + except: + return None # sorry, no status! + + # print "DEBUG:lines=",lines + + try: + l=string.split(lines[0],":") + except: + return None # sorry, no status! + + # print "DEBUG:l=",l + leftside=l[0] + rightside=l[1] + if len(l) > 2: + barcode=l[3] + else: + barcode=None + pass + t=string.split(rightside) + retval.numdrives=int(t[0]) + retval.numslots=int(t[2]) + + for s in lines[1:]: + if not s: + continue # skip blank lines! + #print "DEBUG:s=",s + parts=string.split(s,":") + leftpart=string.split(parts[0]) + rightpart=parts[1] + try: + barcode=parts[2] + except: + barcode=None + pass + #print "DEBUG:leftpart=",leftpart + if leftpart[0]=="Data" and leftpart[1]=="Transfer": + retval.drives.append(drivestatus(leftpart[3],rightpart,barcode)) + pass + if leftpart[0]=="Storage" and leftpart[1]=="Element": + element=slotstatus(leftpart[2],rightpart,barcode) + if element.importexport: + retval.export.append(element) + else: + retval.slots.append(element) + pass + pass + continue + + return retval + +# Output of a mtx inquiry looks like: +# +#Product Type: Medium Changer +#Vendor ID: 'EXABYTE ' +#Product ID: 'Exabyte EZ17 ' +#Revision: '1.07' +#Attached Changer: No +# +# We simply return a hash table with these values { left:right } format. + +def mtxinquiry(device): + command="%s/mtx -f %s inquiry" % (configdir,device) + str=readpipe(command) # calls the command, returns all its data. + + str=string.strip(str) + lines=string.split(str,"\n") + retval={} + for l in lines: + # DEBUG # + l=string.strip(l) + print "splitting line: '",l,"'" + idx,val=string.split(l,':',1) + val=string.strip(val) + if val[0]=="'": + val=val[1:-1] # strip off single quotes, sigh. + pass + retval[idx]=val + continue + return retval + +# Now for the easy part: + +def load(device,slot,drive=0): + command="%s/mtx -f %s load %s %s >/dev/null " % (configdir,device,slot,drive) + status=os.system(command) + return status + +def unload(device,slot,drive=0): + command="%s/mtx -f %s unload %s %s >/dev/null " % (configdir,device,slot,drive) + return os.system(command) + +def inventory(device): + command="%s/mtx -f %s inventory >/dev/null " % (configdir,device) + return os.system(command) + +def wait_for_inventory(device): + # loop while we have an error return... + errcount=0 + while inventory(device): + if errcount==0: + print "Waiting for loader '%s'" % device + pass + time.sleep(1) + try: + s=status(device) + except: + s=None + pass + if s: + return 0 # well, whatever we're doing, we're inventoried :-( + errcount=errcount+1 + if errcount==600: # we've been waiting for 10 minutes :-( + return 1 # sorry! + continue + return 0 # we succeeded! + +# RCS REVISION LOG: +# $Log: mtx.py,v $ +# Revision 1.1.1.1 2001/06/05 17:10:51 elgreen +# Initial import into SourceForge +# +# Revision 1.2 2000/12/22 14:17:19 eric +# mtx 1.2.11pre1 +# +# Revision 1.14 2000/11/12 20:35:29 eric +# do string.strip on the voltag +# +# Revision 1.13 2000/11/04 00:33:38 eric +# if we can get an inventory on the loader after we send it 'mtx inventory', +# then obviously we managed to do SOMETHING. +# +# Revision 1.12 2000/10/28 00:04:34 eric +# added wait_for_inventory command +# +# Revision 1.11 2000/10/27 23:27:58 eric +# Added inventory command... +# +# Revision 1.10 2000/10/01 01:06:29 eric +# evening checkin +# +# Revision 1.9 2000/09/29 02:49:29 eric +# evening checkin +# +# Revision 1.8 2000/09/02 01:05:33 eric +# Evening Checkin +# +# Revision 1.7 2000/09/01 00:08:11 eric +# strip lines in mtxinquiry +# +# Revision 1.6 2000/09/01 00:05:33 eric +# debugging +# +# Revision 1.5 2000/08/31 23:46:01 eric +# fix def: +# +# Revision 1.4 2000/08/31 23:44:06 eric +# =->== +# diff --git a/contrib/mtxctl-0.0.2.tar.gz b/contrib/mtxctl-0.0.2.tar.gz Binary files differnew file mode 100644 index 0000000..de41b7d --- /dev/null +++ b/contrib/mtxctl-0.0.2.tar.gz diff --git a/contrib/tapechanger.html b/contrib/tapechanger.html new file mode 100644 index 0000000..8ea9fce --- /dev/null +++ b/contrib/tapechanger.html @@ -0,0 +1,209 @@ +<HTML> +<HEAD> +<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE> +<LINK REV="made" HREF="mailto:none"> +</HEAD> + +<BODY> + +<A NAME="__index__"></A> +<!-- INDEX BEGIN --> + +<UL> + + <LI><A HREF="#name">NAME</A></LI> + <LI><A HREF="#synopsis">SYNOPSIS</A></LI> + <LI><A HREF="#description">DESCRIPTION</A></LI> + <LI><A HREF="#variables">VARIABLES</A></LI> + <LI><A HREF="#usage">USAGE</A></LI> + <LI><A HREF="#notes">NOTES</A></LI> + <LI><A HREF="#requirements">REQUIREMENTS</A></LI> + <LI><A HREF="#todo">TODO</A></LI> + <LI><A HREF="#see also">SEE ALSO</A></LI> + <LI><A HREF="#author">AUTHOR</A></LI> + <LI><A HREF="#copyright">COPYRIGHT</A></LI> +</UL> +<!-- INDEX END --> + +<HR> +<P> +<H1><A NAME="name">NAME</A></H1> +<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P> +<P> +<HR> +<H1><A NAME="synopsis">SYNOPSIS</A></H1> +<PRE> + use TapeChanger::MTX;</PRE> +<PRE> + my $loaded = TapeChanger::MTX->loadedtape; + print "Currently loaded: $loaded\n" if ($loaded);</PRE> +<PRE> + TapeChanger::MTX->loadtape('next'); + my $nowloaded = TapeChanger::MTX->loadedtape; + print "Currently loaded: $nowloaded\n" if ($nowloaded); +</PRE> +<PRE> + +See below for more available functions.</PRE> +<P> +<HR> +<H1><A NAME="description">DESCRIPTION</A></H1> +<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx' +tape library program. It is meant to work with a simple shell/perl script +to load and unload tapes as appropriate, and to provide a interface for +more complicated programs to do the same. The below functions and +variables should do as good a job as explaining this as anything.</P> +<P> +<HR> +<H1><A NAME="variables">VARIABLES</A></H1> +<DL> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT +=item $TapeChanger::MTX::MTX</A></STRONG><BR> +<DD> +What is the location of the 'mt' and 'mtx' binaries? Can be set with +'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and +'/usr/local/sbin/mtx'. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR> +<DD> +What are the names of the tape (DRIVE) and changer (CONTROL) device +nodes? Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to +'/dev/rmt/0' and '/dev/changer' respectively. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR> +<DD> +Does the tape drive have to eject the tape before the changer retrieves +it? It's okay to say 'yes' if it's not necessary, in most cases. Can be +set with $EJECT in ~/.mtxrc, or defaults to '1'. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR> +<DD> +How long should we wait to see if the drive is ready, in seconds, after +mounting a volume? Can be set with $READY_TIME in ~/.mtxrc, or defaults +to 60. +<P></P> +<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR> +<DD> +Print debugging information? Set to '0' for normal verbosity, '1' for +debugging information, or '-1' for 'quiet mode' (be as quiet as possible). +<P></P></DL> +<P> +<HR> +<H1><A NAME="usage">USAGE</A></H1> +<P>This module uses the following functions:</P> +<DL> +<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR> +<DD> +Runs 'mtx' and 'mt' as appropriate. <CODE>COMMAND</CODE> is the command you're +trying to send to them. Uses 'warn()' to print the commands to the screen +if $TapeChanger::MTX::DEBUG is set. +<P></P> +<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR> +<DD> +Returns the number of drives, number of slots, and currently loaded tape +values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>. +<P></P> +<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR> +<DD> +Loads a tape into the tape changer, and waits until the drive is again +ready to be written to. <CODE>SLOT</CODE> can be any of the following (with the +relevant function indicated): +<PRE> + current C<loadedtape()> + prev C<loadprevtape()> + next C<loadnexttape()> + first C<loadfirsttape()> + last C<loadlasttape()> + 0 C<_ejectdrive()> + 1..99 Loads the specified tape number, ejecting whatever is + currently in the drive.</PRE> +<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0. Returns 0 if +successful, an error string otherwise.</P> +<P></P> +<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR> +<DD> +<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR> +<DD> +Loads the next, previous, first, and last tapes in the changer +respectively. Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>, +<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively. +<P></P> +<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR> +<DD> +Ejects the tape, by first ejecting the tape from the drive +(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its +slot (<STRONG>tape_cmd(unload)</STRONG>). Returns 1 if successful, 0 otherwise. +<P></P> +<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR> +<DD> +Resets the changer, ejecting the tape and loading the first one from the +changer. +<P></P> +<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR> +<DD> +Checks to see if the drive is ready or not, by waiting for up to +$TapeChanger::MTX::READY_TIME seconds to see if it can get status +information using <STRONG>mt_cmd(status)</STRONG>. Returns 1 if so, 0 otherwise. +<P></P> +<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR> +<DD> +Returns a string containing the loaded tape and the drive that it's +mounted on. +<P></P> +<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR> +<DD> +Does some quick checks to see if you're actually capable of using this +module, based on your user permissions. Returns a list of problems if +there are any, 0 otherwise. +<P></P></DL> +<P> +<HR> +<H1><A NAME="notes">NOTES</A></H1> +<P>~/.mtxrc is automatically loaded when this module is used, if it exists, +using do(). This could cause security problems if you're trying to use +this with <CODE>setuid()</CODE> programs - so just don't do that. If you want someone +to have permission to mess with the tape drive and/or changer, let them +have that permission directly.</P> +<P> +<HR> +<H1><A NAME="requirements">REQUIREMENTS</A></H1> +<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and +reader connected to the system.</P> +<P> +<HR> +<H1><A NAME="todo">TODO</A></H1> +<P>Support for Input/Export slots is not included, though it may be later. +Possibly works for multiple drives per changer, but I haven't tested it, +so I probably missed something. 'load previous' doesn't actually work, +because mtx doesn't support it (though the help says it does).</P> +<P> +<HR> +<H1><A NAME="see also">SEE ALSO</A></H1> +<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>. Inspired by <STRONG>stc-changer</STRONG>, which comes +with the AMANDA tape backup package (http://www.amanda.org), and MTX, +available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P> +<P> +<HR> +<H1><A NAME="author">AUTHOR</A></H1> +<P>Tim Skirvin <<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>></P> +<P> +<HR> +<H1><A NAME="copyright">COPYRIGHT</A></H1> +<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and +Tim Skirvin <<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>>.</P> + +</BODY> + +</HTML> diff --git a/contrib/tapeinfo.py b/contrib/tapeinfo.py new file mode 100644 index 0000000..d5df136 --- /dev/null +++ b/contrib/tapeinfo.py @@ -0,0 +1,55 @@ +# Copyright 2000 Enhanced Software Technologies Inc. +# All Rights Reserved +# Released under Free Software Foundation's General Public License, +# Version 2 or above + +# Routine to call 'tapeinfo' and read status for a node. This is an +# example of how to parse the 'tapeinfo' output from a scripting language. +# + +import os +import string +import sys + + +configdir="/opt/brupro/bin" # sigh. + +def inquiry(device): + retval={} + + # okay, now do the thing: + + command="%s/tapeinfo -f %s" % (configdir,device) + + # Now to read: + + infile=os.popen(command,"r") + + try: + s=infile.readline() + except: + s="" + pass + if not s: + return None # did not get anything. + while s: + s=string.strip(s) + idx,val=string.split(s,':',1) + val=string.strip(val) + if val[0]=="'": + val=val[1:-1] # strip off single quotes, sigh. + val=string.strip(val) + pass + while "\0" in val: + # zapo! + val=string.replace(val,"\0","") + pass + retval[idx]=val + try: + s=infile.readline() + except: + s="" + pass + continue # to top of loop! + return retval + diff --git a/contrib/tapeload.pl b/contrib/tapeload.pl new file mode 100644 index 0000000..de1f221 --- /dev/null +++ b/contrib/tapeload.pl @@ -0,0 +1,118 @@ +!/usr/bin/perl +########################## tapeload ########################### +# This script uses mtx 1.2.9pre2 to load a tape based +# on its volume tag. You can +# specify a tape drive by number, but if you don`t, it puts the +# tape in the first available drive and returns the number of that drive, +# both from the standard output and as the exit value. +# A negative exit value indicates an error. +# If volume tags are missing from any full slot, bar codes are rescanned +# automatically. +# +# usage: +# tapeload TAPE_LABEL_1 # Loads tape with label TAPE_LABEL_1 into a drive +# or +# tapeload TAPE_LABEL_1 1 # Loads tape with label TAPE_LABEL_1 into drive #1 +# + +# Set this variable to your mtx binary and proper scsi library device. +$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ; + +# Additions and corrections are welcome. +# This software comes with absolutely no warranty and every other imaginable +# disclaimer. +# -- Frank Samuelson sam@milagro.lanl.gov +################################################################## + +@wt= &mdo("status"); #slurp in the list of slots + +# Check to be certain that every full slot has a volume tag +for ($i=0; $i< $#wt; $i++) { # look through every line + if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) { + # if the element is full, but there is no volume tag, do inventory + @wt= &mdo("inventory status"); + break; + } +} + +#try to find our tape +$slot=-1; +for ($i=0; $i< $#wt; $i++) { # look through every line + if ($wt[$i] =~ / *Storage Element (d*):Full :VolumeTag=(.*)/ ) { + if ($ARGV[0] eq $2) { # We found the tape + $slot=$1; # set the slot number + break; # stop reading the rest of the file. + } + } +} + +if ( $slot>0) { # we found the tape you wanted. + + $drivefound=-1; # set flag to bad value + for ($i=0; $i< $#wt; $i++) { # look through every line + # if this is a tape drive + if ($wt[$i] =~ / *Data Transfer Element (d*):(.*)/ ) { #parse the line + $drive=$1; + $state=$2; +# print STDERR "$wt[$i] $drive $state"; + if ($state =~ /Full/) { # This drive is full. + # if we are looking for a particular drive and this is it + if ( $#ARGV==1 && $drive == $ARGV[1]) { + print STDERR " ERROR: Specified drive $ARGV[1] is full."; + print STDERR @wt; + exit(-6); + } + } elsif ($state =~ /Empty/) { #This is a tape drive and it`s empty. + if ( $#ARGV==1 ) { # If we want a particular drive + if ($drive == $ARGV[1]) { # and this is it, + $drivefound=$drive; # mark it so. + break; + } + } else { # If any old drive will do + $drivefound=$drive; # Mark it. + break; + } + } else { # This is a tape drive, but what the heck is it? + print STDERR " Cannot assess drive status in line"; + print STDERR $wt[$i]; + exit(-7); + } + } + } + + if ( $drivefound < 0 ) { # specified drive was not found + print STDERR "Error: Specified drive $ARGV[1] was not found"; + print STDERR @wt; + exit(-8); + } + # Now we actually load the tape. + @dump=&mdo(" load $slot $drivefound "); + print "$drivefound"; + exit($drivefound); + # The end. + + +} else { + print STDERR " Ug. Tape $ARGV[0] is not in the library."; + print STDERR @wt; + exit(-4); +} + + +sub mdo # a subroutine to call mtx ; +{ +# print STDERR "$_[0]"; + if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function + print STDERR " ERRKK. Could not start mtx "; + exit (-9); + } + + @twt= <FD>; # slurp in the output + + if (! close(FD)) { # if mtx exited with a nonzero value... + print STDERR " Mtx gave an error. Tapeload is exiting... "; + exit (-10); + } + + @twt; +} diff --git a/contrib/tapeunload.pl b/contrib/tapeunload.pl new file mode 100644 index 0000000..e52a4ef --- /dev/null +++ b/contrib/tapeunload.pl @@ -0,0 +1,98 @@ +#!/usr/bin/perl +########################## tapeunload ########################### +# This script uses mtx 1.2.9pre2 to unload a tape +# based on its volume tag. You can +# specify a slot into which the tape should go, but if you don`t, it puts the +# tape into the slot from which it was taken. This slot number +# is returned +# both from the standard output and as the exit value. +# A negative exit value indicates an error. +# If volume tags are missing from any full slot or drive, +# bar codes are rescanned automatically. +# Note: This script assumes that the tape is ready to be removed from +# the drive. That means you may have to unload the tape from the drive +# with "mt offline" before the tape can be moved to a storage slot. +# + +# usage: +# tapeunload TAPE_LABEL_1 +# Removes tape with label TAPE_LABEL_1 from a drive and puts it +# back into its storage slot. Or, +# tapeunload TAPE_LABEL_1 40 +# Removes tape with label TAPE_LABEL_1 from a drive and puts it +# into its storage slot 40. +# + +# Set this variable to your mtx binary and proper scsi library device. +$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ; + +# Additions and corrections are welcome. +# This software comes with absolutely no warranty and every other imaginable +# disclaimer. +# -- Frank Samuelson sam@milagro.lanl.gov + +################################################################## + +@wt= &mdo("status"); #slurp in the list of slots + +# Check to be certain that every full slot has a volume tag +# Rescanning probably will not help. I haven`t seen any bar code +# readers that read tapes that are in the drives. But you never know... +for ($i=0; $i< $#wt; $i++) { # look through every line + if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) { + # if the element is full, but there is no volume tag, do inventory + @wt= &mdo("inventory status"); + break; + } +} + +#try to find our tape +$drivein=-1; +for ($i=0; $i< $#wt; $i++) { # look through every line + # for a full tape drive + if ($wt[$i] =~ / *Data Transfer Element (d*):Full (Storage Element +(d*) Loaded):VolumeTag = (.*)/ ){ + if ($ARGV[0] eq $3) { # We found our tape + $drivein=$1; # set the drive number + $slottogo=$2; # set the slot number + break; # stop reading the rest of the file. + } + } +} + +if ( $drivein>=0) { # we found the tape you wanted. + if ($#ARGV==1) { #If an alternative slot was requested, set it. + $slottogo=$ARGV[1]; # and let mtx handle the errors. + } + + # Now we unload the tape. + @dump=&mdo(" unload $slottogo $drivein "); + print "$slottogo"; + exit($slottogo); + # The end. + + +} else { + print STDERR " Ug. Tape $ARGV[0] is not in a tape drive."; + print STDERR @wt; + exit(-4); +} + + +sub mdo # a subroutine to call mtx ; +{ +# print STDERR "$_[0]"; + if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function + print STDERR " ERRKK. Could not start mtx "; + exit (-9); + } + + @twt= <FD>; # slurp in the output + + if (! close(FD)) { # if mtx exited with a nonzero value... + print STDERR " Mtx gave an error. Tapeload is exiting... "; + exit (-10); + } + + @twt; +} |