summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorCarsten Leonhardt <leo@debian.org>2019-02-27 23:12:38 +0100
committerCarsten Leonhardt <leo@debian.org>2019-02-27 23:12:38 +0100
commitd94bd19c05a1b654a7851821f7c4b7cdd70e1515 (patch)
treebe7cb2ff99167e6dbc12c2c99acad4b386f92bf5 /contrib
parentb695062f151fdfa5cd19cd20918bdf2dc6fd6332 (diff)
Import Upstream version 1.2.16rel
Diffstat (limited to 'contrib')
-rw-r--r--contrib/MTX.html209
-rw-r--r--contrib/README3
-rw-r--r--contrib/TapeChanger-MTX-0.71b.tar.gzbin0 -> 6179 bytes
-rwxr-xr-xcontrib/config_sgen_solaris.sh153
-rw-r--r--contrib/mtx-changer431
-rw-r--r--contrib/mtx.py306
-rw-r--r--contrib/mtxctl-0.0.2.tar.gzbin0 -> 5688 bytes
-rw-r--r--contrib/tapechanger.html209
-rw-r--r--contrib/tapeinfo.py55
-rw-r--r--contrib/tapeload.pl118
-rw-r--r--contrib/tapeunload.pl98
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-&gt;loadedtape;
+ print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
+<PRE>
+ TapeChanger::MTX-&gt;loadtape('next');
+ my $nowloaded = TapeChanger::MTX-&gt;loadedtape;
+ print &quot;Currently loaded: $nowloaded\n&quot; 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&lt;loadedtape()&gt;
+ prev C&lt;loadprevtape()&gt;
+ next C&lt;loadnexttape()&gt;
+ first C&lt;loadfirsttape()&gt;
+ last C&lt;loadlasttape()&gt;
+ 0 C&lt;_ejectdrive()&gt;
+ 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 &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</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 &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</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
new file mode 100644
index 0000000..a42a2e0
--- /dev/null
+++ b/contrib/TapeChanger-MTX-0.71b.tar.gz
Binary files differ
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
new file mode 100644
index 0000000..de41b7d
--- /dev/null
+++ b/contrib/mtxctl-0.0.2.tar.gz
Binary files differ
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-&gt;loadedtape;
+ print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
+<PRE>
+ TapeChanger::MTX-&gt;loadtape('next');
+ my $nowloaded = TapeChanger::MTX-&gt;loadedtape;
+ print &quot;Currently loaded: $nowloaded\n&quot; 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&lt;loadedtape()&gt;
+ prev C&lt;loadprevtape()&gt;
+ next C&lt;loadnexttape()&gt;
+ first C&lt;loadfirsttape()&gt;
+ last C&lt;loadlasttape()&gt;
+ 0 C&lt;_ejectdrive()&gt;
+ 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 &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</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 &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</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;
+}