8 # Contributed by Eric DOUTRELEAU <Eric.Doutreleau@int-evry.fr>
9 # This is supposed to work with Zubkoff/Dandelion version of mtx
11 # Modified by Joe Rhett <jrhett@isite.net>
12 # to work with MTX 1.2.9 by Eric Lee Green http://mtx.sourceforge.net
14 # Modified by Jason Hollinden <jhollind@sammg.com> on 13-Feb-2001
15 # to work with MTX 1.2.10, >9 slots, has barcode support, and works with
16 # multiple configs at once.
17 # NOTE: Only tested the 2 additions with an ADIC Scalar 100.
19 ################################################################################
20 # Here are the things you need to do and know to configure this script:
22 # * Figure out what the robot device name is and what the tape drive
23 # device name is. They will be different!
25 # You cannot send robot commands to a tape drive and vice versa.
26 # Both should respond to "mtx -f /dev/... inquiry". Hopefully,
27 # that output will make it obvious which is which.
29 # For instance, here is what mtx has to say about my current robot:
31 # Product Type: Medium Changer
33 # Product ID: 'ACL2640 206 '
35 # Attached Changer: No
37 # and here is what it says about a tape drive:
39 # Product Type: Tape Drive
40 # Vendor ID: 'Quantum '
41 # Product ID: 'DLT4000 '
43 # Attached Changer: No
45 # Note the "Product Type" value makes it clear which is which.
47 # If it is not obvious, "mf -f /dev/... rewind" should be happy when
48 # talking to a (loaded) tape drive but the changer should give some
49 # kind of error. Similarly, "mtx -f /dev/... status" should show good
50 # results with the changer but fail with a tape drive device name.
52 # Once you have this figured out, set "changerdev" in amanda.conf
53 # to the changer device and "tapedev" to the tape device.
55 # * Find out what the first and last storage slots are. Running
56 # "mtx -f /dev/... status" should give you something like this
57 # (although the output will vary widely based on the version of mtx
58 # and the specifics of your robot):
60 # Storage Changer /dev/changer:1 Drives, 9 Slots ( 0 Import/Export )
61 # Data Transfer Element 0:Empty
62 # Storage Element 1:Full :VolumeTag=SR0001
63 # Storage Element 2:Full :VolumeTag=SR0002
64 # Storage Element 3:Full :VolumeTag=SR0003
65 # Storage Element 4:Full :VolumeTag=SR0004
66 # Storage Element 5:Full :VolumeTag=SR0005
67 # Storage Element 6:Full :VolumeTag=SR0006
68 # Storage Element 7:Full :VolumeTag=SR0007
69 # Storage Element 8:Full :VolumeTag=SR0008
70 # Storage Element 9:Full :VolumeTag=SR0009
72 # This says the first storage slot (element) is "1" and the last
73 # is "9". If you allocate the entire robot to Amanda, you do not need
74 # to set the "firstslot" or "lastslot" configuration file variables --
75 # the script will compute these values for you.
77 # You do not have to allocate all of the slots for Amanda use,
78 # but whatever slots you use must be contiguous (i.e. 4 through 9
79 # in the above would be OK but 1, 2, 5, 6, 9 would not). The one
80 # exception to this is that if one of the slots contains a cleaning
81 # cartridge, it may be in any slot (Amanda will just skip over it if
82 # it is between firstslot and lastslot).
84 # * Speaking of cleaning cartridges, if you have a storage slot dedicated
85 # to one, figure out what slot it is in. That slot number will go in
86 # the "cleanslot" variable.
88 # Also, decide if you want the changer script to automatically run
89 # the cleaning tape through the drive after every so many mounts,
90 # and how many mounts you want to do between cleanings. If you
91 # want the script to do this, set the "autoclean" variable to 1 and
92 # the "autocleancount" to the number of mounts between cleanings.
93 # If you do not want to do automatic cleanings (including not having
94 # a cleaning cartridge in the robot), set "autoclean" to 0.
96 # Note that only a count of mounts is used to determine when it is
97 # time to clean. The script does not try to detect if the drive is
98 # requesting cleaning, or how much the drive was used on a given
101 # * If you tell Amanda about a cleaning cartridge, whether for automatic
102 # operation or manual (amtape <config> clean), you must also tell
103 # the script how long it takes to run the cleaning cycle. It is
104 # impossible for the script to determine when the cleaning operation
105 # is done, so the "cleancycle" variable is the number of seconds
106 # the longest cleaning operation takes (you'll just have to figure
107 # this out by watching it a few times, or maybe finding it in a tape
108 # drive hardware manual). The script will sleep for this length of
109 # time whenever the cleaning tape is referenced. The default is 120
110 # seconds (two minutes).
112 # * Figure out the drive slot number. By default, it is set to 0.
113 # In the example above, the tape drive ("Data Transfer Element")
114 # is in slot 0. If your drive slot is not 0, you
115 # need to set the drive slot number with the "driveslot" variable.
117 # * Figure out whether your robot has a barcode reader and whether
118 # your version of mtx supports it. If you see "VolumeTag" entries
119 # in the "mtx -f /dev/xxx status" output you did above, you have
120 # a reader and mtx can work with it, so you may set the "havereader"
121 # variable to 1. The default is 0 (do not use a reader).
123 # * Pick any tape to load and then determine if the robot can put it
124 # away directly or whether an "offline" must be done first.
126 # With the tape still mounted and ready, try to put the tape away
127 # with "mtx". If you get some kind of error, which is the most
128 # common response, try "mt -f /dev/... offline", wait for the drive
129 # to unload and make sure the robot takes no action on its own to
130 # store the tape. Assuming it does not, try the "mtx" command again
133 # If you had to issue the "mt -f /dev/... offline" before you could
134 # use "mtx" to store the tape, set the "offline_before_unload"
135 # variable to 1. If "mtx" unloaded the drive and put the tape away
136 # all by itself, set it to 0.
138 # * Some drives and robots require a small delay between unloading the
139 # tape and instructing the robot to move it back to storage.
140 # For instance, if you try to grab the tape too soon on an ATL robot
141 # with DLT tape drives, it will rip the leader out of the drive and
142 # require sincerely painful hardware maintenance.
144 # If you need a little delay, set the "unloadpause" variable to
145 # the number of seconds to wait before trying to take a tape from
146 # a drive back to storage. The default is 0.
148 # * Some drives also require a short pause after loading, or the drive
149 # will return an I/O error during a test to see if it's online (which
150 # this script uses "mt rewind" to test). My drives don't recover from
151 # this, and must be reloaded before they will come online after failing
152 # such a test. For this reason there is an "initial_poll_delay"
153 # variable which will pause for a certain number of seconds before
154 # looping through the online test for the first time. The default is 0.
158 # Now you are ready to set up the variables in the changer configuration
161 # All variables are in "changerfile".conf where "changerfile" is set
162 # in amanda.conf. For example, if amanda.conf has:
164 # changerfile="/etc/amanda/Dailyset1/CHANGER"
165 # or changerfile="/etc/amanda/Dailyset1/CHANGER.conf"
167 # the variables must be in "/etc/amanda/Dailyset1/CHANGER.conf".
168 # The ".conf" is appended only if it's not there".
170 # If "changerfile" is a relative path, it is relative to the directory
171 # that contains amanda.conf. That also happens to be the directory Amanda
172 # makes current before running this script.
174 # Here is a commented out example file with all the variables and showing
175 # their default value (if any):
177 # firstslot=? #### First storage slot (element) -- required
178 # lastslot=? #### Last storage slot (element) -- required
179 # cleanslot=-1 #### Slot with cleaner tape -- default is "-1"
180 # #### Set negative to indicate no cleaner available
181 # driveslot=0 #### Drive slot number. Defaults to 0
182 # #### Use the 'Data Transfer Element' you want
184 # # Do you want to clean the drive after a certain number of accesses?
185 # # NOTE - This is unreliable, since 'accesses' aren't 'uses', and we
186 # # have no reliable way to count this. A single amcheck could
187 # # generate as many accesses as slots you have, plus 1.
188 # # ALSO NOTE - many modern tape loaders handle this automatically.
190 # autoclean=0 #### Set to '1' or greater to enable
192 # autocleancount=99 #### Number of access before a clean.
194 # havereader=0 #### If you have a barcode reader, set to 1.
196 # offline_before_unload=0 #### Does your robot require an
197 # #### 'mt offline' before mtx unload?
199 # poll_drive_ready=NN #### Time (seconds) between tests to see if
200 # #### the tape drive has gone ready (default: 3).
202 # max_drive_wait=NN #### Maximum time (seconds) to wait for the
203 # #### tape drive to become ready (default: 120).
205 # initial_poll_delay=NN #### initial delay after load before polling for
210 # Now it is time to test the setup. Do all of the following in the
211 # directory that contains the amanda.conf file, and do all of it as
216 # .../chg-zd-mtx -info
217 # echo $? #### (or "echo $status" if you use csh/tcsh)
219 # You should get a single line from the script like this (the actual
220 # numbers will vary):
224 # The first number (5) is the "current" slot. This may or may not be
225 # the slot actually loaded at the moment (if any). It is the slot
226 # Amanda will try to use next.
228 # The second number (9) is the number of slots.
230 # The third number will always be "1" and indicates the changer is
231 # capable of going backward.
233 # The fourth number is optional. If you set $havereader to 1, it
234 # will be "1", otherwise it will not be present.
236 # The exit code ($? or $status) should be zero.
240 # .../chg-zd-mtx -reset
243 # The script should output a line like this:
247 # The number at the first should match $firstslot. The device name
248 # after that should be your tape device.
250 # The exit code ($? or $status) should be zero.
254 # .../chg-zd-mtx -slot next
257 # The script should output a line like this:
261 # The number at the first should be one higher than $firstslot.
262 # The device name after that should be your tape device.
264 # The exit code ($? or $status) should be zero.
268 # .../chg-zd-mtx -slot current
271 # Assuming the tape is still loaded from the previous test, the
272 # robot should not move and the script should report the same thing
273 # the previous command did.
275 # * If you continue to run "-slot next" commands, the robot should load
276 # each tape in turn then wrap back around to the first when it
277 # reaches $lasttape. If $cleanslot is within the $firstslot to
278 # $lastslot range, the script will skip over that entry.
280 # * Finally, try some of the amtape commands and make sure they work:
282 # amtape <config> reset
283 # amtape <config> slot next
284 # amtape <config> slot current
286 # * If you set $havereader non-zero, now would be a good time to create
287 # the initial barcode database:
289 # amtape <config> update
292 ################################################################################
293 # To debug this script, first look in @AMANDA_DBGDIR@. The script
294 # uses one of two log files there, depending on what version of Amanda
295 # is calling it. It may be chg-zd-mtx.YYYYMMDD*.debug, or it may be
296 # changer.debug.driveN where 'N' is the drive number.
298 # If the log file does not help, try running the script, **as the Amanda
299 # user**, in the amanda.conf directory with whatever set of args the log
300 # said were used when you had a problem. If nothing else useful shows up
301 # in the output, try running the script with the DEBUG environment variable
302 # set non-null, e.g.:
304 # env DEBUG=yes .../chg-zd-mtx ...
305 ################################################################################
307 ################################################################################
308 # You may need to customize these things
309 ################################################################################
315 ################################################################################
316 # No user-level customization should be required beyond this point.
317 ################################################################################
319 test -n "$DEBUG" && set -x
320 TMPDIR="@AMANDA_TMPDIR@"
321 DBGDIR="@AMANDA_DBGDIR@"
324 myname=`expr "$argv0" : '.*/\(.*\)'`
326 config=`pwd 2>/dev/null`
327 config=`expr "$config" : '.*/\(.*\)'`
330 # Functions to write a new log file entry and append more log information.
333 ds=`date '+%H:%M:%S' 2>/dev/null`
334 if [ $? -eq 0 -a -n "$ds" ]; then
335 logprefix=`echo "$ds" | sed 's/./ /g'`
341 if [ -z "$logprefix" ]; then
342 echo "$@" >> $DBGFILE
344 echo "$logprefix" "$@" >> $DBGFILE
349 if [ -z "$logprefix" ]; then
350 echo "===" "`date`" "===" >> $DBGFILE
351 echo "$@" >> $DBGFILE
353 ds=`date '+%H:%M:%S' 2>/dev/null`
354 echo "$ds" "$@" >> $DBGFILE
359 # Common exit function.
363 # $3 = additional information (error message, tape devive, etc)
368 if [ $internal_call -gt 0 ]; then
378 Log $call_type "($code)" "->" "$exit_slot" "$@"
379 echo "$exit_slot" "$@"
380 if [ $call_type = Return ]; then
383 amgetconf$SUF dbclose.$argv0:$DBGFILE > /dev/null 2>&1
388 # Function to run another command and log it.
393 rm -f $stdout $stderr
394 "$@" > $stdout 2> $stderr
396 Log Exit code: $exitcode
400 cat $stdout >> $DBGFILE
405 cat $stderr >> $DBGFILE
413 # Return success if the arg is numeric.
417 test -z "$1" && return 1
418 x="`expr "$1" : '\([-0-9][0-9]*\)' 2>/dev/null`"
419 return `expr X"$1" != X"$x"`
423 # Run $MTX status unless the previous output is still valid.
428 test -n "$DEBUG" && set -x
429 if [ $mtx_status_valid -ne 0 ]; then
433 Run $MTX status > $mtx_status 2>&1
435 if [ $status -eq 0 ]; then
442 # Determine the slot currently loaded. Set $loadedslot to the slot
443 # currently loaded, or "-1", and $loadedbarcode to the corresponding
444 # barcode (or nothing).
448 test -n "$DEBUG" && set -x
452 /^Data Transfer Element:Empty/ {
456 /^Data Transfer Element '$driveslot':Empty/ {
460 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/ {
461 s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/\1 \2/p
464 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/ {
465 s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^ ]*\)/\1 \2/p
468 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded):VolumeTag *= *\([^ ]*\)/ {
469 s/.*:VolumeTag *= *\([^ ]*\)/-2 \1/p
472 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
473 s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
476 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
477 s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
480 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ {
484 ' < $mtx_status 2>&1`
485 shift # get rid of the "x"
488 if [ -z "$loadedslot" ]; then
489 Exit 2 "<none>" "could not determine current slot, are you sure your drive slot is $driveslot"
490 return $? # in case we are internal
493 #Use the current slot if it's empty and we don't know which slot is loaded'
494 if [ $loadedslot -eq -2 ]; then
497 /^.*Storage Element '$currentslot':Empty/ {
498 s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
501 /^.*Storage Element '$currentslot':Full/ {
502 s/.*Storage Element \([0-9][0-9]*\):Full/-2/p
506 ' < $mtx_status 2>& 1`
507 shift # get rid of the "x"
509 if [ ! -z $loadedslotx ]; then
510 loadedslot=$loadedslotx
514 #Use the first empty slot if we don't know which slot is loaded'
515 if [ $loadedslot -eq -2 ]; then
518 /^.*Storage Element \([0-9][0-9]*\):Empty/ {
519 s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
523 ' < $mtx_status 2>& 1`
524 shift # get rid of the "x"
528 if IsNumeric "$loadedslot" ; then
533 "currently loaded slot ($loadedslot) not numeric"
534 return $? # in case we are internal
536 Log "STATUS -> currently loaded slot = $loadedslot"
537 LogAppend " -> currently loaded barcode = \"$loadedbarcode\""
541 # Get a list of slots between $firstslot and $lastslot, if they are set.
542 # If they are not set, set them to the first and last slot seen on the
543 # assumption the entire robot is to be used (???).
548 test -n "$DEBUG" && set -x
549 if [ -n "$slot_list" ]; then
554 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
555 s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
557 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
558 s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
560 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ {
563 /^.*Storage Element \([0-9][0-9]*\):Full/ {
564 s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
567 /^.*Storage Element \([0-9][0-9]*\):Empty/ {
568 s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
571 /^.*Storage Element \([0-9][0-9]*\):Full/ {
572 s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
574 ' < $mtx_status 2>&1 | grep -v "^${cleanslot}\$" | sort -n`
575 slot_list=`echo $slot_list` # remove the newlines
576 if [ $firstslot -lt 0 -o $lastslot -lt 0 ]; then
578 for slot in $slot_list; do
579 if [ $firstslot -lt 0 ]; then
580 Log "SLOTLIST -> firstslot set to $slot"
583 if [ $lastslot -lt 0 ]; then
587 if [ $lastslot -lt 0 -a $last -ge 0 ]; then
588 Log "SLOTLIST -> lastslot set to $last"
591 if [ $firstslot -lt 0 ]; then
594 "cannot determine first slot"
595 return $? # in case we are internal
596 elif [ $lastslot -lt 0 ]; then
599 "cannot determine last slot"
600 return $? # in case we are internal
604 for slot in $slot_list; do
605 if [ $slot -ge $firstslot -a $slot -le $lastslot ]; then
606 amanda_slot_list="$amanda_slot_list $slot"
609 if [ -z "$amanda_slot_list" ]; then
613 return $? # in case we are internal
615 slot_list="$amanda_slot_list"
620 exec_prefix=@exec_prefix@
622 libexecdir=@libexecdir@
624 # try to hit all the possibilities here
625 PATH=$sbindir:$libexecdir:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb:/usr/local/bin
628 USE_VERSION_SUFFIXES="@USE_VERSION_SUFFIXES@"
629 if test "$USE_VERSION_SUFFIXES" = "yes"; then
635 DBGFILE=`amgetconf$SUF dbopen.$argv0 2>/dev/null`
638 DBGFILE=/dev/null # will try this again below
641 changerfile=`amgetconf$SUF changerfile 2>/dev/null`
642 if [ -z "$changerfile" ]; then
645 "changerfile must be specified in amanda.conf"
648 tape=`amgetconf$SUF tapedev 2>/dev/null`
649 if [ -z "$tape" ]; then
652 "tapedev may not be empty"
653 elif [ $tape = "/dev/null" -o `expr "$tape" : 'null:'` -eq 5 ]; then
656 "tapedev ($tape) may not be the null device"
658 TAPE=`amgetconf$SUF changerdev 2>/dev/null`
659 if [ -z "$TAPE" ]; then
662 "changerdev may not be empty"
663 elif [ $TAPE = "/dev/null" ]; then
666 "changerdev ($TAPE) may not be the null device"
668 export TAPE # for mtx command
670 #### Set up the various config files.
672 conf_match=`expr "$changerfile" : .\*\.conf\$`
673 if [ $conf_match -ge 6 ]; then
674 configfile=$changerfile
675 changerfile=`echo $changerfile | sed 's/.conf$//g'`
677 configfile=$changerfile.conf
680 cleanfile=$changerfile-clean
681 accessfile=$changerfile-access
682 slotfile=$changerfile-slot
683 labelfile=$changerfile-barcodes
684 [ ! -s $cleanfile ] && echo 0 > $cleanfile
685 [ ! -s $accessfile ] && echo 0 > $accessfile
686 [ ! -s $slotfile ] && echo -1 > $slotfile
687 [ ! -f $labelfile ] && > $labelfile
688 cleancount=`cat $cleanfile`
689 accesscount=`cat $accessfile`
691 #### Dig out of the config file what is needed
694 varlist="$varlist firstslot"
695 varlist="$varlist lastslot"
696 varlist="$varlist cleanslot"
697 varlist="$varlist cleancycle"
698 varlist="$varlist OFFLINE_BEFORE_UNLOAD" # old name
699 varlist="$varlist offline_before_unload"
700 varlist="$varlist unloadpause"
701 varlist="$varlist AUTOCLEAN" # old name
702 varlist="$varlist autoclean"
703 varlist="$varlist autocleancount"
704 varlist="$varlist havereader"
705 varlist="$varlist driveslot"
706 varlist="$varlist poll_drive_ready"
707 varlist="$varlist initial_poll_delay"
708 varlist="$varlist max_drive_wait"
712 val="`cat $configfile 2>/dev/null | sed -n '
713 # Ignore comment lines (anything starting with a #).
715 # Find the first var=val line in the file, print the value and quit.
716 /^[ ]*'$var'[ ]*=[ ]*\([^ ][^ ]*\).*/ {
717 s/^[ ]*'$var'[ ]*=[ ]*\([^ ][^ ]*\).*/\1/p
724 # Deal with driveslot first so we can get DBGFILE set if we are still
725 # using the old amgetconf.
727 if [ -z "$driveslot" ]; then
731 # Get DBGFILE set if it is not already.
733 if [ $DBGFILE = /dev/null ]; then
734 if [ -d "$DBGDIR" ]; then
735 DBGFILE=$DBGDIR/changer.debug.drive$driveslot
739 Log === Start "`date`" ===
741 if [ -z "$driveslot" ]; then
744 "cannot determine drive slot from $tape"
747 stdout=$TMPDIR/$myname.1.$$
748 stderr=$TMPDIR/$myname.2.$$
749 mtx_status=$TMPDIR/$myname.status.$$
750 trap "rm -f $stdout $stderr $mtx_status" 0 # exit cleanup
752 Log "Using config file $configfile"
754 # Log the argument list.
759 LogAppend "\$$i = \"$argv0\""
762 LogAppend "\$$i = \"$arg\""
765 # Set the default config values for those not in the file. Log the
766 # results and make sure each is valid (numeric).
768 firstslot=${firstslot:-'-1'} # default: mtx status
769 lastslot=${lastslot:-'-1'} # default: mtx status
770 cleanslot=${cleanslot:-'-1'} # default: -1
771 cleancycle=${cleancycle:-'120'} # default: two minutes
772 if [ -z "$offline_before_unload" -a -n "$OFFLINE_BEFORE_UNLOAD" ]; then
773 offline_before_unload=$OFFLINE_BEFORE_UNLOAD # (old name)
775 offline_before_unload=${offline_before_unload:-'0'} # default: 0
776 unloadpause=${unloadpause:-'0'} # default: 0
777 if [ -z "$autoclean" -a -n "$AUTOCLEAN" ]; then
778 autoclean=$AUTOCLEAN # (old name)
780 autoclean=${autoclean:-'0'} # default: 0
781 autocleancount=${autocleancount:-'99'} # default: 99
782 havereader=${havereader:-'0'} # default: 0
783 poll_drive_ready=${poll_drive_ready:-'3'} # default: three seconds
784 initial_poll_delay=${initial_poll_delay:-'0'} # default: zero zeconds
785 max_drive_wait=${max_drive_wait:-'120'} # default: two minutes
790 for var in $varlist; do
791 if [ $var = "OFFLINE_BEFORE_UNLOAD" ]; then
793 elif [ $var = "AUTOCLEAN" ]; then
797 if [ -z "$val" ]; then
800 "$var missing in $configfile"
802 if IsNumeric "$val" ; then
807 "$var ($val) not numeric in $configfile"
809 LogAppend $var = \"$val\"
812 # Run the rest of the config file sanity checks.
814 if [ $firstslot -gt $lastslot ]; then
817 "firstslot ($firstslot) greater than" \
818 "lastslot ($lastslot) in $configfile"
820 if [ $autoclean -ne 0 -a $cleanslot -lt 0 ]; then
823 "autoclean set but cleanslot not valid ($cleanslot)"
826 # Set up the current slot
828 currentslot=`cat $slotfile`
829 if IsNumeric "$currentslot" ; then
830 if [ $currentslot -lt $firstslot ]; then
831 Log "SETUP -> current slot $currentslot" \
832 "less than $firstslot ..." \
833 "resetting to $firstslot"
834 currentslot=$firstslot
835 elif [ $currentslot -gt $lastslot ]; then
836 Log "SETUP -> current slot $currentslot" \
837 "greater than $lastslot ..." \
838 "resetting to $lastslot"
839 currentslot=$lastslot
842 Log "SETUP -> contents of $slotfile ($currentslot) invalid," \
843 "setting current slot to first slot ($firstslot)"
844 currentslot=$firstslot
848 first_slot_in_list=-1
849 next_slot_after_current=-1
850 for slot in $slot_list; do
851 if [ $first_slot_in_list -lt 0 ]; then
852 first_slot_in_list=$slot # in case $firstslot is missing
854 if [ $slot -eq $currentslot ]; then
857 elif [ $slot -gt $currentslot ]; then
858 next_slot_after_current=$slot # $currentslot is missing
862 if [ $found_current -eq 0 ]; then
863 if [ $next_slot_after_current -lt 0 ]; then
864 new_currentslot=$first_slot_in_list
866 new_currentslot=$next_slot_after_current
868 Log "WARNING -> current slot $currentslot not available," \
869 "setting current slot to next slot ($new_currentslot)"
870 currentslot=$new_currentslot
876 # Eject the current tape and put it away.
880 test -n "$DEBUG" && set -x
881 Log "EJECT -> ejecting tape from $tape"
883 if [ $loadedslot -gt 0 ]; then
884 Log "EJECT -> moving tape from drive $driveslot" \
885 "to storage slot $loadedslot"
886 if [ $offline_before_unload -ne 0 ]; then
887 Run $MT $MTF $tape offline > /dev/null 2>&1
890 result=`Run $MTX unload $loadedslot $driveslot 2>&1`
892 Log " -> status $status, result \"$result\""
894 if [ $status -ne 0 ]; then
902 answer="Drive was not loaded"
905 Exit $code "$loadedslot" "$answer"
906 return $? # in case we are internal
910 # Reset the robot back to the first slot.
914 test -n "$DEBUG" && set -x
915 Log "RESET -> loading tape from slot $firstslot" \
916 "to drive $driveslot ($tape)"
917 # Call loadslot without doing it as an internal and let it finish
921 Exit 2 "<none>" "reset: should not get here"
922 return $? # in case we are internal
926 # Unload the current tape (if necessary) and load a new one (unless
927 # "advance"). If no tape is loaded, get the value of "current" from
932 test -n "$DEBUG" && set -x
933 if [ $# -lt 1 ]; then
934 Exit 2 "<none>" "Missing -slot argument"
935 return $? # in case we are internal
938 Log "LOADSLOT -> load drive $driveslot ($tape) from slot $whichslot"
940 numeric=`echo $whichslot | sed 's/[^0-9]//g'`
942 current|prev|next|advance)
943 find_slot=$currentslot
958 Exit 2 "<none>" "Illegal slot: \"$whichslot\""
959 return $? # in case we are internal
963 # Find the requested slot in the slot list. By loading the "set"
964 # command with multiple copies, we guarantee that if the slot is
965 # found, we can look both forward and backward without running
966 # off the end. Putting $cleanslot at the end allows us to find
967 # that slot since it is not in $slot_list.
969 set x $slot_list $slot_list $slot_list $cleanslot
970 shift # get rid of the "x"
973 while [ $# -gt 0 ]; do
974 if [ $1 -eq $find_slot ]; then
980 if [ $# -le 0 ]; then
983 "Cannot find slot $find_slot" \
984 "in slot list ($slot_list)"
985 return $? # in case we are internal
988 # Determine the slot to load.
1001 # If the desired slot is already loaded, we are done. Only update
1002 # current slot if this is not the cleaning slot.
1004 if [ $loadslot = $loadedslot ]; then
1005 if [ $loadslot -ne $cleanslot ]; then
1007 echo $loadslot > $slotfile
1009 Exit 0 "$loadedslot" "$tape"
1010 return $? # in case we are internal
1012 if [ $loadedslot -eq -2 ]; then
1013 Exit 0 "$loadedslot" "$tape"
1014 return $? # in case we are internal
1017 # If we are loading the cleaning tape, bump the cleaning count
1018 # and reset the access count. Otherwise, bump the access count
1019 # and see if it is time to do a cleaning.
1020 if [ $loadslot = $cleanslot ]; then
1021 rm -f $cleanfile $accessfile
1022 expr $cleancount + 1 > $cleanfile
1023 echo 0 > $accessfile
1026 expr $accesscount + 1 > $accessfile
1027 if [ $autoclean -ne 0 -a $accesscount -gt $autocleancount ]
1029 internal_call=`expr $internal_call + 1`
1030 loadslot clean > /dev/null 2>&1
1032 internal_call=`expr $internal_call - 1`
1033 if [ $status -ne 0 ]; then
1034 Exit $status "$loadslot" "$exit_answer"
1035 return $? # in case we are internal
1038 # Slot $cleanslot might contain an ordinary tape
1039 # rather than a cleaning tape. A cleaning tape
1040 # *MIGHT* auto-eject; an ordinary tape does not.
1041 # We therefore have to read the status again to
1042 # check what actually happened.
1048 # Unload whatever tape is in the drive.
1049 internal_call=`expr $internal_call + 1`
1050 eject > /dev/null 2>&1
1052 internal_call=`expr $internal_call - 1`
1053 if [ $status -gt 1 ]; then
1054 Exit $status "$exit_slot" "$exit_answer"
1055 return $? # in case we are internal
1058 # If we were doing an "advance", we are done.
1059 if [ $whichslot = advance ]; then
1060 if [ $loadslot -ne $cleanslot ]; then
1062 echo $loadslot > $slotfile
1064 Exit 0 "$loadslot" "/dev/null"
1065 return $? # in case we are internal
1068 # Load the tape, finally!
1069 Log "LOADSLOT -> loading tape from slot $loadslot" \
1070 "to drive $driveslot ($tape)"
1071 result=`Run $MTX load $loadslot $driveslot 2>&1`
1073 Log " -> status $status, result \"$result\""
1075 if [ $status -ne 0 ]; then
1076 Exit 2 "$loadslot" "$result"
1077 return $? # in case we are internal
1081 # Cleaning tapes never go "ready", so instead we just sit here
1082 # for "long enough" (as determined empirically by the user),
1083 # then return success.
1085 if [ $loadslot -eq $cleanslot ]; then
1086 Run sleep $cleancycle
1087 Exit 0 "$loadslot" "$tape"
1088 return $? # in case we are internal
1092 # Wait for the drive to go online.
1096 sleep $initial_poll_delay
1097 while [ $waittime -lt $max_drive_wait ]; do
1098 result=`Run $MT $MTF $tape rewind 2>&1`
1099 if [ $? -eq 0 ]; then
1103 sleep $poll_drive_ready
1104 waittime=`expr $waittime + $poll_drive_ready`
1106 if [ $ready -eq 0 ]; then
1107 Exit 2 "$loadslot" "Drive not ready after" \
1108 "$max_drive_wait seconds," \
1109 "rewind said \"$result\""
1110 return $? # in case we are internal
1113 if [ $loadslot -ne $cleanslot ]; then
1115 echo $loadslot > $slotfile
1117 Exit 0 "$loadslot" "$tape"
1118 return $? # in case we are internal
1122 # Return information about how the changer is configured and the current
1123 # state of the robot.
1127 test -n "$DEBUG" && set -x
1130 Log "INFO -> first slot: $firstslot"
1131 LogAppend " -> current slot: $currentslot"
1132 LogAppend " -> loaded slot: $loadedslot"
1133 LogAppend " -> last slot: $lastslot"
1134 LogAppend " -> slot list: $slot_list"
1135 LogAppend " -> can go backwards: 1"
1136 LogAppend " -> havereader: $havereader"
1139 # Check if a barcode reader is configured or not. If so, it
1140 # passes the 4th item in the echo back to amtape signifying it
1141 # can search based on barcodes.
1144 if [ $havereader -eq 1 ]; then
1148 if [ $currentslot -lt $firstslot -o $currentslot -gt $lastslot ]; then
1149 currentslot=$firstslot # what "current" will get
1151 numslots=`expr $lastslot - $firstslot + 1`
1152 Exit 0 "$currentslot" "$numslots 1 $reader"
1153 return $? # in case we are internal
1157 # Read the labelfile and scan for a particular entry.
1161 labelfile_entry_found=0
1169 while read lbl bc junk; do
1170 line=`expr $line + 1`
1171 if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
1172 Log "ERROR -> Line $line malformed: $lbl $bc $junk"
1173 LogAppend " -> Remove $labelfile" \
1175 "\"$sbindir/amtape $config update\""
1178 "Line $line malformed in $labelfile: $lbl $bc $junk"
1179 return $? # in case we are internal
1181 if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
1182 if [ $labelfile_entry_found -ne 0 ]; then
1183 Log "ERROR -> Duplicate entries: $labelfile line $line"
1184 LogAppend " -> Remove $labelfile" \
1186 "\"$sbindir/amtape $config update\""
1189 "Duplicate entries: $labelfile line $line"
1190 return $? # in case we are internal
1192 labelfile_entry_found=1
1193 labelfile_label=$lbl
1194 labelfile_barcode=$bc
1200 # Adds the label and barcode for the currently loaded tape to the
1201 # barcode file. Return an error if the database is messed up.
1205 test -n "$DEBUG" && set -x
1206 if [ $# -lt 1 ]; then
1207 Exit 2 "<none>" "Missing -label argument"
1208 return $? # in case we are internal
1211 if [ $havereader -eq 0 ]; then
1212 Exit 2 "<none>" "Not configured with barcode reader"
1213 return $? # in case we are internal
1216 if [ $loadedslot -lt 0 ]; then
1217 Exit 1 "<none>" "No tape currently loaded"
1218 return $? # in case we are internal
1220 Log "LABEL -> Adding label \"$tapelabel\"" \
1221 "with barcode \"$loadedbarcode\"" \
1222 "for slot $loadedslot" \
1224 read_labelfile "$tapelabel" "$loadedbarcode" < $labelfile
1225 if [ $labelfile_entry_found -ne 0 ]; then
1227 if [ "$labelfile_barcode" != "$loadedbarcode" ]; then
1231 old_val=$labelfile_barcode
1232 new_val=$loadedbarcode
1233 elif [ "$labelfile_label" != "$tapelabel" ]; then
1235 lf_val=$loadedbarcode
1237 old_val=$labelfile_label
1240 if [ -n "$lf_val" ]; then
1241 LogAppend "ERROR -> !!! Label database corrupted !!!"
1242 LogAppend " -> \"$old_val\" conflicts with" \
1243 "new $val_type \"$new_val\"" \
1244 "for $lf_type \"$lf_val\""
1245 LogAppend " -> Remove $labelfile" \
1247 "\"$sbindir/amtape $config update\""
1250 "$tapelabel: \"$old_val\" conflicts with" \
1251 "new $val_type \"$new_val\"" \
1252 "for $lf_type \"$lf_val\""
1253 return $? # in case we are internal
1255 LogAppend " -> already synced"
1257 echo "$tapelabel $loadedbarcode" >> $labelfile
1258 LogAppend " -> appended $labelfile entry:" \
1259 "$tapelabel $loadedbarcode"
1261 Exit 0 "$loadedslot" "$tape"
1262 return $? # in case we are internal
1266 # Look for a label in the barcode file. If found, locate the slot it's
1267 # in by looking for the barcode in the mtx output, then load that tape.
1271 test -n "$DEBUG" && set -x
1272 if [ $# -lt 1 ]; then
1273 Exit 2 "<none>" "Missing -search argument"
1274 return $? # in case we are internal
1277 if [ $havereader -eq 0 ]; then
1278 Exit 2 "<none>" "Not configured with barcode reader"
1279 return $? # in case we are internal
1281 Log "SEARCH -> Hunting for label \"$tapelabel\""
1282 read_labelfile "$tapelabel" "" < $labelfile
1283 if [ $labelfile_entry_found -eq 0 ]; then
1284 LogAppend " -> !!! label \"$tapelabel\" not found" \
1286 LogAppend " -> Remove $labelfile" \
1288 "\"$sbindir/amtape $config update\""
1291 "$tapelabel: label \"$tapelabel\" not found in $labelfile"
1292 return $? # in case we are internal
1294 LogAppend " -> barcode is \"$labelfile_barcode\""
1297 /VolumeTag *= *'$labelfile_barcode' *$/ {
1298 s/.*Storage Element \([0-9][0-9]*\).*/\1/p
1302 LogAppend " -> foundslot is $foundslot"
1303 if [ -z "$foundslot" ]; then
1304 LogAppend "ERROR -> !!! Could not find slot" \
1305 "for barcode \"$labelfile_barcode\"!!!"
1306 LogAppend " -> Remove $labelfile" \
1308 "\"$sbindir/amtape $config update\""
1311 "barcode \"$labelfile_barcode\"" \
1312 "not found in mtx status output"
1313 return $? # in case we are internal
1315 # Call loadslot without doing it as an internal and let it finish
1319 Exit 2 "<none>" "searchtape: should not get here"
1320 return $? # in case we are internal
1324 # Program invocation begins here
1327 if [ $# -lt 1 ]; then
1328 Exit 2 "<none>" "Usage: $myname -command args"
1355 Exit 2 "<none>" "unknown option: $cmd"
1359 Exit 2 "<none>" "$myname: should not get here"