#!/bin/sh # # Exit Status: # 0 Alles Ok # 1 Illegal Request # 2 Fatal Error # # Contributed by Eric DOUTRELEAU # This is supposed to work with Zubkoff/Dandelion version of mtx # # Modified by Joe Rhett # to work with MTX 1.2.9 by Eric Lee Green http://mtx.sourceforge.net # # Modified by Jason Hollinden on 13-Feb-2001 # to work with MTX 1.2.10, >9 slots, has barcode support, and works with # multiple configs at once. # NOTE: Only tested the 2 additions with an ADIC Scalar 100. ################################################################################ # Here are the things you need to do and know to configure this script: # # * Figure out what the robot device name is and what the tape drive # device name is. They will be different! # # You cannot send robot commands to a tape drive and vice versa. # Both should respond to "mtx -f /dev/... inquiry". Hopefully, # that output will make it obvious which is which. # # For instance, here is what mtx has to say about my current robot: # # Product Type: Medium Changer # Vendor ID: 'ATL ' # Product ID: 'ACL2640 206 ' # Revision: '2A5A' # Attached Changer: No # # and here is what it says about a tape drive: # # Product Type: Tape Drive # Vendor ID: 'Quantum ' # Product ID: 'DLT4000 ' # Revision: 'CD50' # Attached Changer: No # # Note the "Product Type" value makes it clear which is which. # # If it is not obvious, "mf -f /dev/... rewind" should be happy when # talking to a (loaded) tape drive but the changer should give some # kind of error. Similarly, "mtx -f /dev/... status" should show good # results with the changer but fail with a tape drive device name. # # Once you have this figured out, set "changerdev" in amanda.conf # to the changer device and "tapedev" to the tape device. # # * Find out what the first and last storage slots are. Running # "mtx -f /dev/... status" should give you something like this # (although the output will vary widely based on the version of mtx # and the specifics of your robot): # # Storage Changer /dev/changer:1 Drives, 9 Slots ( 0 Import/Export ) # Data Transfer Element 0:Empty # Storage Element 1:Full :VolumeTag=SR0001 # Storage Element 2:Full :VolumeTag=SR0002 # Storage Element 3:Full :VolumeTag=SR0003 # Storage Element 4:Full :VolumeTag=SR0004 # Storage Element 5:Full :VolumeTag=SR0005 # Storage Element 6:Full :VolumeTag=SR0006 # Storage Element 7:Full :VolumeTag=SR0007 # Storage Element 8:Full :VolumeTag=SR0008 # Storage Element 9:Full :VolumeTag=SR0009 # # This says the first storage slot (element) is "1" and the last # is "9". If you allocate the entire robot to Amanda, you do not need # to set the "firstslot" or "lastslot" configuration file variables -- # the script will compute these values for you. # # You do not have to allocate all of the slots for Amanda use, # but whatever slots you use must be contiguous (i.e. 4 through 9 # in the above would be OK but 1, 2, 5, 6, 9 would not). The one # exception to this is that if one of the slots contains a cleaning # cartridge, it may be in any slot (Amanda will just skip over it if # it is between firstslot and lastslot). # # * Speaking of cleaning cartridges, if you have a storage slot dedicated # to one, figure out what slot it is in. That slot number will go in # the "cleanslot" variable. # # Also, decide if you want the changer script to automatically run # the cleaning tape through the drive after every so many mounts, # and how many mounts you want to do between cleanings. If you # want the script to do this, set the "autoclean" variable to 1 and # the "autocleancount" to the number of mounts between cleanings. # If you do not want to do automatic cleanings (including not having # a cleaning cartridge in the robot), set "autoclean" to 0. # # Note that only a count of mounts is used to determine when it is # time to clean. The script does not try to detect if the drive is # requesting cleaning, or how much the drive was used on a given # mount. # # * If you tell Amanda about a cleaning cartridge, whether for automatic # operation or manual (amtape clean), you must also tell # the script how long it takes to run the cleaning cycle. It is # impossible for the script to determine when the cleaning operation # is done, so the "cleancycle" variable is the number of seconds # the longest cleaning operation takes (you'll just have to figure # this out by watching it a few times, or maybe finding it in a tape # drive hardware manual). The script will sleep for this length of # time whenever the cleaning tape is referenced. The default is 120 # seconds (two minutes). # # * Figure out the drive slot number. By default, it is set to 0. # In the example above, the tape drive ("Data Transfer Element") # is in slot 0. If your drive slot is not 0, you # need to set the drive slot number with the "driveslot" variable. # # * Figure out whether your robot has a barcode reader and whether # your version of mtx supports it. If you see "VolumeTag" entries # in the "mtx -f /dev/xxx status" output you did above, you have # a reader and mtx can work with it, so you may set the "havereader" # variable to 1. The default is 0 (do not use a reader). # # * Pick any tape to load and then determine if the robot can put it # away directly or whether an "offline" must be done first. # # With the tape still mounted and ready, try to put the tape away # with "mtx". If you get some kind of error, which is the most # common response, try "mt -f /dev/... offline", wait for the drive # to unload and make sure the robot takes no action on its own to # store the tape. Assuming it does not, try the "mtx" command again # to store the tape. # # If you had to issue the "mt -f /dev/... offline" before you could # use "mtx" to store the tape, set the "offline_before_unload" # variable to 1. If "mtx" unloaded the drive and put the tape away # all by itself, set it to 0. # # * Some drives and robots require a small delay between unloading the # tape and instructing the robot to move it back to storage. # For instance, if you try to grab the tape too soon on an ATL robot # with DLT tape drives, it will rip the leader out of the drive and # require sincerely painful hardware maintenance. # # If you need a little delay, set the "unloadpause" variable to # the number of seconds to wait before trying to take a tape from # a drive back to storage. The default is 0. # # * Some drives also require a short pause after loading, or the drive # will return an I/O error during a test to see if it's online (which # this script uses "mt rewind" to test). My drives don't recover from # this, and must be reloaded before they will come online after failing # such a test. For this reason there is an "initial_poll_delay" # variable which will pause for a certain number of seconds before # looping through the online test for the first time. The default is 0. #### #### # Now you are ready to set up the variables in the changer configuration # file. # # All variables are in "changerfile".conf where "changerfile" is set # in amanda.conf. For example, if amanda.conf has: # # changerfile="/etc/amanda/Dailyset1/CHANGER" # # the variables must be in "/etc/amanda/Dailyset1/CHANGER.conf". # # If "changerfile" is a relative path, it is relative to the directory # that contains amanda.conf. That also happens to be the directory Amanda # makes current before running this script. # # Here is a commented out example file with all the variables and showing # their default value (if any): #### # firstslot=? #### First storage slot (element) -- required # lastslot=? #### Last storage slot (element) -- required # cleanslot=-1 #### Slot with cleaner tape -- default is "-1" # #### Set negative to indicate no cleaner available # driveslot=0 #### Drive slot number. Defaults to 0 # #### Use the 'Data Transfer Element' you want # # # Do you want to clean the drive after a certain number of accesses? # # NOTE - This is unreliable, since 'accesses' aren't 'uses', and we # # have no reliable way to count this. A single amcheck could # # generate as many accesses as slots you have, plus 1. # # ALSO NOTE - many modern tape loaders handle this automatically. # # autoclean=0 #### Set to '1' or greater to enable # # autocleancount=99 #### Number of access before a clean. # # cleancycle=120 #### Time (seconds) to clean drive (default 120) # # havereader=0 #### If you have a barcode reader, set to 1. # # offline_before_unload=0 #### Does your robot require an # #### 'mt offline' before mtx unload? # # poll_drive_ready=NN #### Time (seconds) between tests to see if # #### the tape drive has gone ready (default: 3). # # max_drive_wait=NN #### Maximum time (seconds) to wait for the # #### tape drive to become ready (default: 120). # # initial_poll_delay=NN #### initial delay after load before polling for # #### readiness #### #### # Now it is time to test the setup. Do all of the following in the # directory that contains the amanda.conf file, and do all of it as # the Amanda user. # # * Run this: # # .../chg-zd-mtx -info # echo $? #### (or "echo $status" if you use csh/tcsh) # # You should get a single line from the script like this (the actual # numbers will vary): # # 5 9 1 1 # # The first number (5) is the "current" slot. This may or may not be # the slot actually loaded at the moment (if any). It is the slot # Amanda will try to use next. # # The second number (9) is the number of slots. # # The third number will always be "1" and indicates the changer is # capable of going backward. # # The fourth number is optional. If you set $havereader to 1, it # will be "1", otherwise it will not be present. # # The exit code ($? or $status) should be zero. # # * Run this: # # .../chg-zd-mtx -reset # echo $? # # The script should output a line like this: # # 1 /dev/rmt/0mn # # The number at the first should match $firstslot. The device name # after that should be your tape device. # # The exit code ($? or $status) should be zero. # # * Run this: # # .../chg-zd-mtx -slot next # echo $? # # The script should output a line like this: # # 2 /dev/rmt/0mn # # The number at the first should be one higher than $firstslot. # The device name after that should be your tape device. # # The exit code ($? or $status) should be zero. # # * Run this: # # .../chg-zd-mtx -slot current # echo $? # # Assuming the tape is still loaded from the previous test, the # robot should not move and the script should report the same thing # the previous command did. # # * If you continue to run "-slot next" commands, the robot should load # each tape in turn then wrap back around to the first when it # reaches $lasttape. If $cleanslot is within the $firstslot to # $lastslot range, the script will skip over that entry. # # * Finally, try some of the amtape commands and make sure they work: # # amtape reset # amtape slot next # amtape slot current # # * If you set $havereader non-zero, now would be a good time to create # the initial barcode database: # # amtape update #### ################################################################################ # To debug this script, first look in @AMANDA_DBGDIR@. The script # uses one of two log files there, depending on what version of Amanda # is calling it. It may be chg-zd-mtx.YYYYMMDD*.debug, or it may be # changer.debug.driveN where 'N' is the drive number. # # If the log file does not help, try running the script, **as the Amanda # user**, in the amanda.conf directory with whatever set of args the log # said were used when you had a problem. If nothing else useful shows up # in the output, try running the script with the DEBUG environment variable # set non-null, e.g.: # # env DEBUG=yes .../chg-zd-mtx ... ################################################################################ ################################################################################ # You may need to customize these things ################################################################################ MT=@MT@ MTF=@MT_FILE_FLAG@ MTX=@MTX@ ################################################################################ # No user-level customization should be required beyond this point. ################################################################################ test -n "$DEBUG" && set -x TMPDIR="@AMANDA_TMPDIR@" DBGDIR="@AMANDA_DBGDIR@" argv0=$0 myname=`expr "$argv0" : '.*/\(.*\)'` config=`pwd 2>/dev/null` config=`expr "$config" : '.*/\(.*\)'` ### # Functions to write a new log file entry and append more log information. ### ds=`date '+%H:%M:%S' 2>/dev/null` if [ $? -eq 0 -a -n "$ds" ]; then logprefix=`echo "$ds" | sed 's/./ /g'` else logprefix="" fi LogAppend() { if [ -z "$logprefix" ]; then echo "$@" >> $DBGFILE else echo "$logprefix" "$@" >> $DBGFILE fi } Log() { if [ -z "$logprefix" ]; then echo "===" "`date`" "===" >> $DBGFILE echo "$@" >> $DBGFILE else ds=`date '+%H:%M:%S' 2>/dev/null` echo "$ds" "$@" >> $DBGFILE fi } ### # Common exit function. # # $1 = exit code # $2 = slot result # $3 = additional information (error message, tape devive, etc) ### internal_call=0 Exit() { if [ $internal_call -gt 0 ]; then call_type=Return else call_type=Exit fi code=$1 shift exit_slot=$1 shift exit_answer="$@" Log $call_type "($code)" "->" "$exit_slot" "$@" echo "$exit_slot" "$@" if [ $call_type = Return ]; then return $code fi amgetconf$SUF dbclose.$argv0:$DBGFILE > /dev/null 2>&1 exit $code } ### # Function to run another command and log it. ### Run() { Log Running: "$@" rm -f $stdout $stderr "$@" > $stdout 2> $stderr exitcode=$? Log Exit code: $exitcode if [ -s $stdout ] then LogAppend Stdout: cat $stdout >> $DBGFILE fi if [ -s $stderr ] then LogAppend Stderr: cat $stderr >> $DBGFILE fi cat $stdout cat $stderr 1>&2 return $exitcode } ### # Return success if the arg is numeric. ### IsNumeric() { test -z "$1" && return 1 x="`expr "$1" : '\([-0-9][0-9]*\)' 2>/dev/null`" return `expr X"$1" != X"$x"` } ### # Run $MTX status unless the previous output is still valid. ### mtx_status_valid=0 get_mtx_status() { test -n "$DEBUG" && set -x if [ $mtx_status_valid -ne 0 ]; then return 0 fi rm -f $mtx_status Run $MTX status > $mtx_status 2>&1 status=$? if [ $status -eq 0 ]; then mtx_status_valid=1 fi return $status } ### # Determine the slot currently loaded. Set $loadedslot to the slot # currently loaded, or "-1", and $loadedbarcode to the corresponding # barcode (or nothing). ### get_loaded_info() { test -n "$DEBUG" && set -x get_mtx_status set x `sed -n ' /^Data Transfer Element:Empty/ { s/.*/-1/p q } /^Data Transfer Element '$driveslot':Empty/ { s/.*/-1/p q } /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/ { s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/\1 \2/p q } /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/ { s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/\1 \2/p q } /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded):VolumeTag *= *\([^ ]*\)/ { s/.*:VolumeTag *= *\([^ ]*\)/-2 \1/p q } /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ { s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p q } /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ { s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p q } /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ { s/.*/-2/p q } ' < $mtx_status 2>&1` shift # get rid of the "x" loadedslot=$1 loadedbarcode=$2 if [ -z "$loadedslot" ]; then Exit 2 "" "could not determine current slot, are you sure your drive slot is $driveslot" return $? # in case we are internal fi #Use the current slot if it's empty and we don't know which slot is loaded' if [ $loadedslot -eq -2 ]; then set x `sed -n ' { /^.*Storage Element '$currentslot':Empty/ { s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p q } /^.*Storage Element '$currentslot':Full/ { s/.*Storage Element \([0-9][0-9]*\):Full/-2/p q } } ' < $mtx_status 2>& 1` shift # get rid of the "x" loadedslotx=$1 if [ ! -z $loadedslotx ]; then loadedslot=$loadedslotx fi fi #Use the first empty slot if we don't know which slot is loaded' if [ $loadedslot -eq -2 ]; then set x `sed -n ' { /^.*Storage Element \([0-9][0-9]*\):Empty/ { s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p q } } ' < $mtx_status 2>& 1` shift # get rid of the "x" loadedslot=$1 fi if IsNumeric "$loadedslot" ; then : else Exit 2 \ "" \ "currently loaded slot ($loadedslot) not numeric" return $? # in case we are internal fi Log "STATUS -> currently loaded slot = $loadedslot" LogAppend " -> currently loaded barcode = \"$loadedbarcode\"" } ### # Get a list of slots between $firstslot and $lastslot, if they are set. # If they are not set, set them to the first and last slot seen on the # assumption the entire robot is to be used (???). ### slot_list= get_slot_list() { test -n "$DEBUG" && set -x if [ -n "$slot_list" ]; then return fi get_mtx_status slot_list=`sed -n ' /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ { s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p } /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ { s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p } /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ { : loop n /^.*Storage Element \([0-9][0-9]*\):Full/ { s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p b loop } /^.*Storage Element \([0-9][0-9]*\):Empty/ { s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p } } /^.*Storage Element \([0-9][0-9]*\):Full/ { s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p } ' < $mtx_status 2>&1 | grep -v "^${cleanslot}\$" | sort -n` slot_list=`echo $slot_list` # remove the newlines if [ $firstslot -lt 0 -o $lastslot -lt 0 ]; then last=$lastslot for slot in $slot_list; do if [ $firstslot -lt 0 ]; then Log "SLOTLIST -> firstslot set to $slot" firstslot=$slot fi if [ $lastslot -lt 0 ]; then last=$slot fi done if [ $lastslot -lt 0 -a $last -ge 0 ]; then Log "SLOTLIST -> lastslot set to $last" lastslot=$last fi if [ $firstslot -lt 0 ]; then Exit 2 \ "" \ "cannot determine first slot" return $? # in case we are internal elif [ $lastslot -lt 0 ]; then Exit 2 \ "" \ "cannot determine last slot" return $? # in case we are internal fi fi amanda_slot_list= for slot in $slot_list; do if [ $slot -ge $firstslot -a $slot -le $lastslot ]; then amanda_slot_list="$amanda_slot_list $slot" fi done if [ -z "$amanda_slot_list" ]; then Exit 2 \ "" \ "no slots available" return $? # in case we are internal fi slot_list="$amanda_slot_list" } # Paths prefix=@prefix@ exec_prefix=@exec_prefix@ sbindir=@sbindir@ libexecdir=@libexecdir@ # try to hit all the possibilities here PATH=$sbindir:$libexecdir:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb:/usr/local/bin export PATH USE_VERSION_SUFFIXES="@USE_VERSION_SUFFIXES@" if test "$USE_VERSION_SUFFIXES" = "yes"; then SUF="-@VERSION@" else SUF= fi DBGFILE=`amgetconf$SUF dbopen.$argv0 2>/dev/null | grep -v BUGGY` if [ -z "$DBGFILE" ] then DBGFILE=/dev/null # will try this again below fi changerfile=`amgetconf$SUF changerfile 2>/dev/null | grep -v BUGGY` if [ -z "$changerfile" ]; then Exit 2 \ "" \ "changerfile must be specified in amanda.conf" fi tape=`amgetconf$SUF tapedev 2>/dev/null | grep -v BUGGY` if [ -z "$tape" ]; then Exit 2 \ "" \ "tapedev may not be empty" elif [ $tape = "/dev/null" -o `expr "$tape" : 'null:'` -eq 5 ]; then Exit 2 \ "" \ "tapedev ($tape) may not be the null device" fi TAPE=`amgetconf$SUF changerdev 2>/dev/null | grep -v BUGGY` if [ -z "$TAPE" ]; then Exit 2 \ "" \ "changerdev may not be empty" elif [ $TAPE = "/dev/null" ]; then Exit 2 \ "" \ "changerdev ($TAPE) may not be the null device" fi export TAPE # for mtx command #### Set up the various config files. configfile=$changerfile.conf cleanfile=$changerfile-clean accessfile=$changerfile-access slotfile=$changerfile-slot labelfile=$changerfile-barcodes [ ! -s $cleanfile ] && echo 0 > $cleanfile [ ! -s $accessfile ] && echo 0 > $accessfile [ ! -s $slotfile ] && echo -1 > $slotfile [ ! -f $labelfile ] && > $labelfile cleancount=`cat $cleanfile` accesscount=`cat $accessfile` #### Dig out of the config file what is needed varlist= varlist="$varlist firstslot" varlist="$varlist lastslot" varlist="$varlist cleanslot" varlist="$varlist cleancycle" varlist="$varlist OFFLINE_BEFORE_UNLOAD" # old name varlist="$varlist offline_before_unload" varlist="$varlist unloadpause" varlist="$varlist AUTOCLEAN" # old name varlist="$varlist autoclean" varlist="$varlist autocleancount" varlist="$varlist havereader" varlist="$varlist driveslot" varlist="$varlist poll_drive_ready" varlist="$varlist initial_poll_delay" varlist="$varlist max_drive_wait" for var in $varlist do val="`cat $configfile 2>/dev/null | sed -n ' # Ignore comment lines (anything starting with a #). /^[ ]*#/d # Find the first var=val line in the file, print the value and quit. /^[ ]*'$var'[ ]*=[ ]*\([^ ][^ ]*\).*/ { s/^[ ]*'$var'[ ]*=[ ]*\([^ ][^ ]*\).*/\1/p q } '`" eval $var=\"$val\" done # Deal with driveslot first so we can get DBGFILE set if we are still # using the old amgetconf. if [ -z "$driveslot" ]; then driveslot=0; fi # Get DBGFILE set if it is not already. if [ $DBGFILE = /dev/null ]; then if [ -d "$DBGDIR" ]; then DBGFILE=$DBGDIR/changer.debug.drive$driveslot else DBGFILE=/dev/null fi Log === Start "`date`" === fi if [ -z "$driveslot" ]; then Exit 2 \ "" \ "cannot determine drive slot from $tape" fi stdout=$TMPDIR/$myname.1.$$ stderr=$TMPDIR/$myname.2.$$ mtx_status=$TMPDIR/$myname.status.$$ trap "rm -f $stdout $stderr $mtx_status" 0 # exit cleanup Log "Using config file $configfile" # Log the argument list. Log "Arg info:" LogAppend "\$# = $#" i=0 LogAppend "\$$i = \"$argv0\"" for arg in "$@"; do i=`expr $i + 1` LogAppend "\$$i = \"$arg\"" done # Set the default config values for those not in the file. Log the # results and make sure each is valid (numeric). firstslot=${firstslot:-'-1'} # default: mtx status lastslot=${lastslot:-'-1'} # default: mtx status cleanslot=${cleanslot:-'-1'} # default: -1 cleancycle=${cleancycle:-'120'} # default: two minutes if [ -z "$offline_before_unload" -a -n "$OFFLINE_BEFORE_UNLOAD" ]; then offline_before_unload=$OFFLINE_BEFORE_UNLOAD # (old name) fi offline_before_unload=${offline_before_unload:-'0'} # default: 0 unloadpause=${unloadpause:-'0'} # default: 0 if [ -z "$autoclean" -a -n "$AUTOCLEAN" ]; then autoclean=$AUTOCLEAN # (old name) fi autoclean=${autoclean:-'0'} # default: 0 autocleancount=${autocleancount:-'99'} # default: 99 havereader=${havereader:-'0'} # default: 0 poll_drive_ready=${poll_drive_ready:-'3'} # default: three seconds initial_poll_delay=${initial_poll_delay:-'0'} # default: zero zeconds max_drive_wait=${max_drive_wait:-'120'} # default: two minutes get_slot_list Log "Config info:" for var in $varlist; do if [ $var = "OFFLINE_BEFORE_UNLOAD" ]; then continue # old name elif [ $var = "AUTOCLEAN" ]; then continue # old name fi eval val=\"'$'$var\" if [ -z "$val" ]; then Exit 2 \ "" \ "$var missing in $configfile" fi if IsNumeric "$val" ; then : else Exit 2 \ "" \ "$var ($val) not numeric in $configfile" fi LogAppend $var = \"$val\" done # Run the rest of the config file sanity checks. if [ $firstslot -gt $lastslot ]; then Exit 2 \ "" \ "firstslot ($firstslot) greater than" \ "lastslot ($lastslot) in $configfile" fi if [ $autoclean -ne 0 -a $cleanslot -lt 0 ]; then Exit 2 \ "" \ "autoclean set but cleanslot not valid ($cleanslot)" fi # Set up the current slot currentslot=`cat $slotfile` if IsNumeric "$currentslot" ; then if [ $currentslot -lt $firstslot ]; then Log "SETUP -> current slot $currentslot" \ "less than $firstslot ..." \ "resetting to $firstslot" currentslot=$firstslot elif [ $currentslot -gt $lastslot ]; then Log "SETUP -> current slot $currentslot" \ "greater than $lastslot ..." \ "resetting to $lastslot" currentslot=$lastslot fi else Log "SETUP -> contents of $slotfile ($currentslot) invalid," \ "setting current slot to first slot ($firstslot)" currentslot=$firstslot fi found_current=0 first_slot_in_list=-1 next_slot_after_current=-1 for slot in $slot_list; do if [ $first_slot_in_list -lt 0 ]; then first_slot_in_list=$slot # in case $firstslot is missing fi if [ $slot -eq $currentslot ]; then found_current=1 break elif [ $slot -gt $currentslot ]; then next_slot_after_current=$slot # $currentslot is missing break fi done if [ $found_current -eq 0 ]; then if [ $next_slot_after_current -lt 0 ]; then new_currentslot=$first_slot_in_list else new_currentslot=$next_slot_after_current fi Log "WARNING -> current slot $currentslot not available," \ "setting current slot to next slot ($new_currentslot)" currentslot=$new_currentslot fi # More routines. ### # Eject the current tape and put it away. ### eject() { test -n "$DEBUG" && set -x Log "EJECT -> ejecting tape from $tape" get_loaded_info if [ $loadedslot -gt 0 ]; then Log "EJECT -> moving tape from drive $driveslot" \ "to storage slot $loadedslot" if [ $offline_before_unload -ne 0 ]; then Run $MT $MTF $tape offline > /dev/null 2>&1 fi sleep $unloadpause result=`Run $MTX unload $loadedslot $driveslot 2>&1` status=$? Log " -> status $status, result \"$result\"" mtx_status_valid=0 if [ $status -ne 0 ]; then answer="$result" code=2 else answer="$tape" code=0 fi else answer="Drive was not loaded" code=1 fi Exit $code "$loadedslot" "$answer" return $? # in case we are internal } ### # Reset the robot back to the first slot. ### reset() { test -n "$DEBUG" && set -x Log "RESET -> loading tape from slot $firstslot" \ "to drive $driveslot ($tape)" # Call loadslot without doing it as an internal and let it finish # things up. loadslot $firstslot # NOTREACHED Exit 2 "" "reset: should not get here" return $? # in case we are internal } ### # Unload the current tape (if necessary) and load a new one (unless # "advance"). If no tape is loaded, get the value of "current" from # $slotfile. ### loadslot() { test -n "$DEBUG" && set -x if [ $# -lt 1 ]; then Exit 2 "" "Missing -slot argument" return $? # in case we are internal fi whichslot=$1 Log "LOADSLOT -> load drive $driveslot ($tape) from slot $whichslot" numeric=`echo $whichslot | sed 's/[^0-9]//g'` case $whichslot in current|prev|next|advance) find_slot=$currentslot ;; first) find_slot=$firstslot ;; last) find_slot=$lastslot ;; $numeric) find_slot=$numeric ;; clean) find_slot=$cleanslot ;; *) Exit 2 "" "Illegal slot: \"$whichslot\"" return $? # in case we are internal ;; esac # Find the requested slot in the slot list. By loading the "set" # command with multiple copies, we guarantee that if the slot is # found, we can look both forward and backward without running # off the end. Putting $cleanslot at the end allows us to find # that slot since it is not in $slot_list. get_slot_list set x $slot_list $slot_list $slot_list $cleanslot shift # get rid of the "x" prev_slot=$1 shift while [ $# -gt 0 ]; do if [ $1 -eq $find_slot ]; then break fi prev_slot=$1 shift done if [ $# -le 0 ]; then Exit 2 \ "" \ "Cannot find slot $find_slot" \ "in slot list ($slot_list)" return $? # in case we are internal fi # Determine the slot to load. case $whichslot in next|advance) shift loadslot=$1 ;; prev) loadslot=$prev_slot ;; *) loadslot=$find_slot esac # If the desired slot is already loaded, we are done. Only update # current slot if this is not the cleaning slot. get_loaded_info if [ $loadslot = $loadedslot ]; then if [ $loadslot -ne $cleanslot ]; then rm -f $slotfile echo $loadslot > $slotfile fi Exit 0 "$loadedslot" "$tape" return $? # in case we are internal fi if [ $loadedslot -eq -2 ]; then Exit 0 "$loadedslot" "$tape" return $? # in case we are internal fi # If we are loading the cleaning tape, bump the cleaning count # and reset the access count. Otherwise, bump the access count # and see if it is time to do a cleaning. if [ $loadslot = $cleanslot ]; then rm -f $cleanfile $accessfile expr $cleancount + 1 > $cleanfile echo 0 > $accessfile else rm -f $accessfile expr $accesscount + 1 > $accessfile if [ $autoclean -ne 0 -a $accesscount -gt $autocleancount ] then internal_call=`expr $internal_call + 1` loadslot clean > /dev/null 2>&1 status=$? internal_call=`expr $internal_call - 1` if [ $status -ne 0 ]; then Exit $status "$loadslot" "$exit_answer" return $? # in case we are internal fi # Slot $cleanslot might contain an ordinary tape # rather than a cleaning tape. A cleaning tape # *MIGHT* auto-eject; an ordinary tape does not. # We therefore have to read the status again to # check what actually happened. mtx_status_valid=0 get_loaded_info fi fi # Unload whatever tape is in the drive. internal_call=`expr $internal_call + 1` eject > /dev/null 2>&1 status=$? internal_call=`expr $internal_call - 1` if [ $status -gt 1 ]; then Exit $status "$exit_slot" "$exit_answer" return $? # in case we are internal fi # If we were doing an "advance", we are done. if [ $whichslot = advance ]; then if [ $loadslot -ne $cleanslot ]; then rm -f $slotfile echo $loadslot > $slotfile fi Exit 0 "$loadslot" "/dev/null" return $? # in case we are internal fi # Load the tape, finally! Log "LOADSLOT -> loading tape from slot $loadslot" \ "to drive $driveslot ($tape)" result=`Run $MTX load $loadslot $driveslot 2>&1` status=$? Log " -> status $status, result \"$result\"" mtx_status_valid=0 if [ $status -ne 0 ]; then Exit 2 "$loadslot" "$result" return $? # in case we are internal fi ### # Cleaning tapes never go "ready", so instead we just sit here # for "long enough" (as determined empirically by the user), # then return success. ### if [ $loadslot -eq $cleanslot ]; then Run sleep $cleancycle Exit 0 "$loadslot" "$tape" return $? # in case we are internal fi ### # Wait for the drive to go online. ### waittime=0 ready=0 sleep $initial_poll_delay while [ $waittime -lt $max_drive_wait ]; do result=`Run $MT $MTF $tape rewind 2>&1` if [ $? -eq 0 ]; then ready=1 break fi sleep $poll_drive_ready waittime=`expr $waittime + $poll_drive_ready` done if [ $ready -eq 0 ]; then Exit 2 "$loadslot" "Drive not ready after" \ "$max_drive_wait seconds," \ "rewind said \"$result\"" return $? # in case we are internal fi if [ $loadslot -ne $cleanslot ]; then rm -f $slotfile echo $loadslot > $slotfile fi Exit 0 "$loadslot" "$tape" return $? # in case we are internal } ### # Return information about how the changer is configured and the current # state of the robot. ### info() { test -n "$DEBUG" && set -x get_loaded_info get_slot_list Log "INFO -> first slot: $firstslot" LogAppend " -> current slot: $currentslot" LogAppend " -> loaded slot: $loadedslot" LogAppend " -> last slot: $lastslot" LogAppend " -> slot list: $slot_list" LogAppend " -> can go backwards: 1" LogAppend " -> havereader: $havereader" ### # Check if a barcode reader is configured or not. If so, it # passes the 4th item in the echo back to amtape signifying it # can search based on barcodes. ### reader= if [ $havereader -eq 1 ]; then reader=1 fi if [ $currentslot -lt $firstslot -o $currentslot -gt $lastslot ]; then currentslot=$firstslot # what "current" will get fi set x $slot_list shift # get rid of the "x" numslots=$# Exit 0 "$currentslot" "$numslots 1 $reader" return $? # in case we are internal } ### # Read the labelfile and scan for a particular entry. ### read_labelfile() { labelfile_entry_found=0 labelfile_label= labelfile_barcode= lbl_search=$1 bc_search=$2 line=0 while read lbl bc junk; do line=`expr $line + 1` if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then Log "ERROR -> Line $line malformed: $lbl $bc $junk" LogAppend " -> Remove $labelfile" \ "and run" \ "\"$sbindir/amtape $config update\"" Exit 2 \ "" \ "Line $line malformed in $labelfile: $lbl $bc $junk" return $? # in case we are internal fi if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then if [ $labelfile_entry_found -ne 0 ]; then Log "ERROR -> Duplicate entries: $labelfile line $line" LogAppend " -> Remove $labelfile" \ "and run" \ "\"$sbindir/amtape $config update\"" Exit 2 \ "" \ "Duplicate entries: $labelfile line $line" return $? # in case we are internal fi labelfile_entry_found=1 labelfile_label=$lbl labelfile_barcode=$bc fi done } ### # Adds the label and barcode for the currently loaded tape to the # barcode file. Return an error if the database is messed up. ### addlabel() { test -n "$DEBUG" && set -x if [ $# -lt 1 ]; then Exit 2 "" "Missing -label argument" return $? # in case we are internal fi tapelabel=$1 if [ $havereader -eq 0 ]; then Exit 2 "" "Not configured with barcode reader" return $? # in case we are internal fi get_loaded_info if [ $loadedslot -lt 0 ]; then Exit 1 "" "No tape currently loaded" return $? # in case we are internal fi Log "LABEL -> Adding label \"$tapelabel\"" \ "with barcode \"$loadedbarcode\"" \ "for slot $loadedslot" \ "into $labelfile" read_labelfile "$tapelabel" "$loadedbarcode" < $labelfile if [ $labelfile_entry_found -ne 0 ]; then lf_val= if [ "$labelfile_barcode" != "$loadedbarcode" ]; then lf_type=label lf_val=$tapelabel val_type=barcode old_val=$labelfile_barcode new_val=$loadedbarcode elif [ "$labelfile_label" != "$tapelabel" ]; then lf_type=barcode lf_val=$loadedbarcode val_type=label old_val=$labelfile_label new_val=$tapelabel fi if [ -n "$lf_val" ]; then LogAppend "ERROR -> !!! Label database corrupted !!!" LogAppend " -> \"$old_val\" conflicts with" \ "new $val_type \"$new_val\"" \ "for $lf_type \"$lf_val\"" LogAppend " -> Remove $labelfile" \ "and run" \ "\"$sbindir/amtape $config update\"" Exit 2 \ "" \ "$tapelabel: \"$old_val\" conflicts with" \ "new $val_type \"$new_val\"" \ "for $lf_type \"$lf_val\"" return $? # in case we are internal fi LogAppend " -> already synced" else echo "$tapelabel $loadedbarcode" >> $labelfile LogAppend " -> appended $labelfile entry:" \ "$tapelabel $loadedbarcode" fi Exit 0 "$loadedslot" "$tape" return $? # in case we are internal } ### # Look for a label in the barcode file. If found, locate the slot it's # in by looking for the barcode in the mtx output, then load that tape. ### searchtape() { test -n "$DEBUG" && set -x if [ $# -lt 1 ]; then Exit 2 "" "Missing -search argument" return $? # in case we are internal fi tapelabel=$1 if [ $havereader -eq 0 ]; then Exit 2 "" "Not configured with barcode reader" return $? # in case we are internal fi Log "SEARCH -> Hunting for label \"$tapelabel\"" read_labelfile "$tapelabel" "" < $labelfile if [ $labelfile_entry_found -eq 0 ]; then LogAppend " -> !!! label \"$tapelabel\" not found" \ "in $labelfile !!!" LogAppend " -> Remove $labelfile" \ "and run" \ "\"$sbindir/amtape $config update\"" Exit 2 \ "" \ "$tapelabel: label \"$tapelabel\" not found in $labelfile" return $? # in case we are internal fi LogAppend " -> barcode is \"$labelfile_barcode\"" get_mtx_status foundslot=`sed -n ' /VolumeTag *= *'$labelfile_barcode' *$/ { s/.*Storage Element \([0-9][0-9]*\).*/\1/p q } ' < $mtx_status` LogAppend " -> foundslot is $foundslot" if [ -z "$foundslot" ]; then LogAppend "ERROR -> !!! Could not find slot" \ "for barcode \"$labelfile_barcode\"!!!" LogAppend " -> Remove $labelfile" \ "and run" \ "\"$sbindir/amtape $config update\"" Exit 2 \ "" \ "barcode \"$labelfile_barcode\"" \ "not found in mtx status output" return $? # in case we are internal fi # Call loadslot without doing it as an internal and let it finish # things up. loadslot $foundslot # NOTREACHED Exit 2 "" "searchtape: should not get here" return $? # in case we are internal } ### # Program invocation begins here ### if [ $# -lt 1 ]; then Exit 2 "" "Usage: $myname -command args" fi cmd=$1 shift case "$cmd" in -slot) loadslot "$@" ;; -info) info "$@" ;; -reset) reset "$@" ;; -eject) eject "$@" ;; -label) addlabel "$@" ;; -search) searchtape "$@" ;; -clean) loadslot clean ;; *) Exit 2 "" "unknown option: $cmd" ;; esac Exit 2 "" "$myname: should not get here"