dd26273daf9cdc607b6a35c0d3f2eb55d62c4f4f
[debian/amanda] / changer-src / chg-zd-mtx.sh
1 #!@SHELL@ 
2 #
3 # Exit Status:
4 # 0 Alles Ok
5 # 1 Illegal Request
6 # 2 Fatal Error
7 #
8 # Contributed by Eric DOUTRELEAU <Eric.Doutreleau@int-evry.fr>
9 # This is supposed to work with Zubkoff/Dandelion version of mtx
10 #
11 # Modified by Joe Rhett <jrhett@isite.net>
12 # to work with MTX 1.2.9 by Eric Lee Green http://mtx.sourceforge.net
13 #
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.
18
19 ################################################################################
20 # Here are the things you need to do and know to configure this script:
21 #
22 #   * Figure out what the robot device name is and what the tape drive
23 #     device name is.  They will be different!
24 #
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.
28 #
29 #     For instance, here is what mtx has to say about my current robot:
30 #
31 #       Product Type: Medium Changer
32 #       Vendor ID: 'ATL     '
33 #       Product ID: 'ACL2640 206     '
34 #       Revision: '2A5A'
35 #       Attached Changer: No
36 #
37 #     and here is what it says about a tape drive:
38 #
39 #       Product Type: Tape Drive
40 #       Vendor ID: 'Quantum '
41 #       Product ID: 'DLT4000         '
42 #       Revision: 'CD50'
43 #       Attached Changer: No
44 #
45 #     Note the "Product Type" value makes it clear which is which.
46 #
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.
51 #
52 #     Once you have this figured out, set "changerdev" in amanda.conf
53 #     to the changer device and "tapedev" to the tape device.
54 #
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):
59 #
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
71 #             Storage Element 10 IMPORT/EXPORT:Full :VolumeTag=SR0009
72 #
73 #     This says the first storage slot (element) is "1" and the last
74 #     is "9".  If you allocate the entire robot to Amanda, you do not need
75 #     to set the "firstslot" or "lastslot" configuration file variables --
76 #     the script will compute these values for you.
77 #
78 #     You do not have to allocate all of the slots for Amanda use,
79 #     but whatever slots you use must be contiguous (i.e. 4 through 9
80 #     in the above would be OK but 1, 2, 5, 6, 9 would not).  The one
81 #     exception to this is that if one of the slots contains a cleaning
82 #     cartridge, it may be in any slot (Amanda will just skip over it if
83 #     it is between firstslot and lastslot).
84 #
85 #   * Speaking of cleaning cartridges, if you have a storage slot dedicated
86 #     to one, figure out what slot it is in.  That slot number will go in
87 #     the "cleanslot" variable.
88 #
89 #     Also, decide if you want the changer script to automatically run
90 #     the cleaning tape through the drive after every so many mounts,
91 #     and how many mounts you want to do between cleanings.  If you
92 #     want the script to do this, set the "autoclean" variable to 1 and
93 #     the "autocleancount" to the number of mounts between cleanings.
94 #     If you do not want to do automatic cleanings (including not having
95 #     a cleaning cartridge in the robot), set "autoclean" to 0.
96 #
97 #     Note that only a count of mounts is used to determine when it is
98 #     time to clean.  The script does not try to detect if the drive is
99 #     requesting cleaning, or how much the drive was used on a given
100 #     mount.
101 #
102 #   * If you tell Amanda about a cleaning cartridge, whether for automatic
103 #     operation or manual (amtape <config> clean), you must also tell
104 #     the script how long it takes to run the cleaning cycle.  It is
105 #     impossible for the script to determine when the cleaning operation
106 #     is done, so the "cleancycle" variable is the number of seconds
107 #     the longest cleaning operation takes (you'll just have to figure
108 #     this out by watching it a few times, or maybe finding it in a tape
109 #     drive hardware manual).  The script will sleep for this length of
110 #     time whenever the cleaning tape is referenced.  The default is 120
111 #     seconds (two minutes).
112 #
113 #   * Figure out the drive slot number.  By default, it is set to 0.
114 #     In the example above, the tape drive ("Data Transfer Element")
115 #     is in slot 0. If your drive slot is not 0, you
116 #     need to set the drive slot number with the "driveslot" variable.
117 #
118 #   * Figure out whether your robot has a barcode reader and whether
119 #     your version of mtx supports it.  If you see "VolumeTag" entries
120 #     in the "mtx -f /dev/xxx status" output you did above, you have
121 #     a reader and mtx can work with it, so you may set the "havereader"
122 #     variable to 1.  The default is 0 (do not use a reader).
123 #
124 #   * Pick any tape to load and then determine if the robot can put it
125 #     away directly or whether an "offline" must be done first.
126 #
127 #     With the tape still mounted and ready, try to put the tape away
128 #     with "mtx".  If you get some kind of error, which is the most
129 #     common response, try "mt -f /dev/... offline", wait for the drive
130 #     to unload and make sure the robot takes no action on its own to
131 #     store the tape.  Assuming it does not, try the "mtx" command again
132 #     to store the tape.
133 #
134 #     If you had to issue the "mt -f /dev/... offline" before you could
135 #     use "mtx" to store the tape, set the "offline_before_unload"
136 #     variable to 1.  If "mtx" unloaded the drive and put the tape away
137 #     all by itself, set it to 0.
138 #
139 #   * Some drives and robots require a small delay between unloading the
140 #     tape and instructing the robot to move it back to storage.
141 #     For instance, if you try to grab the tape too soon on an ATL robot
142 #     with DLT tape drives, it will rip the leader out of the drive and
143 #     require sincerely painful hardware maintenance.
144 #
145 #     If you need a little delay, set the "unloadpause" variable to
146 #     the number of seconds to wait before trying to take a tape from
147 #     a drive back to storage.  The default is 0.
148 #
149 #   * Some drives also require a short pause after loading, or the drive
150 #     will return an I/O error during a test to see if it's online (which
151 #     this script uses "mt rewind" to test).  My drives don't recover from
152 #     this, and must be reloaded before they will come online after failing
153 #     such a test.  For this reason there is an "initial_poll_delay"
154 #     variable which will pause for a certain number of seconds before
155 #     looping through the online test for the first time.  The default is 0.
156 ####
157
158 ####
159 # Now you are ready to set up the variables in the changer configuration
160 # file.
161 #
162 # All variables are in "changerfile".conf where "changerfile" is set
163 # in amanda.conf.  For example, if amanda.conf has:
164 #
165 #       changerfile="/etc/amanda/Dailyset1/CHANGER"
166 #    or changerfile="/etc/amanda/Dailyset1/CHANGER.conf"
167 #
168 # the variables must be in "/etc/amanda/Dailyset1/CHANGER.conf".
169 # The ".conf" is appended only if it's not there".
170 #
171 # If "changerfile" is a relative path, it is relative to the directory
172 # that contains amanda.conf.  That also happens to be the directory Amanda
173 # makes current before running this script.
174 #
175 # Here is a commented out example file with all the variables and showing
176 # their default value (if any):
177 ####
178 # firstslot=?               #### First storage slot (element) -- required
179 # lastslot=?                #### Last storage slot (element) -- required
180 # cleanslot=-1              #### Slot with cleaner tape -- default is "-1"
181 #                           #### Set negative to indicate no cleaner available
182 # driveslot=0               #### Drive slot number.  Defaults to 0
183 #                           #### Use the 'Data Transfer Element' you want
184 #
185 #   # Do you want to clean the drive after a certain number of accesses?
186 #   # NOTE - This is unreliable, since 'accesses' aren't 'uses', and we
187 #   #        have no reliable way to count this.  A single amcheck could
188 #   #        generate as many accesses as slots you have, plus 1.
189 #   # ALSO NOTE - many modern tape loaders handle this automatically.
190 #
191 # autoclean=0               #### Set to '1' or greater to enable
192 #
193 # autocleancount=99         #### Number of access before a clean.
194 #
195 # havereader=0              #### If you have a barcode reader, set to 1.
196 #
197 # offline_before_unload=0   #### Does your robot require an
198 #                           #### 'mt offline' before mtx unload?
199 #
200 # poll_drive_ready=NN       #### Time (seconds) between tests to see if
201 #                           #### the tape drive has gone ready (default: 3).
202 #
203 # max_drive_wait=NN         #### Maximum time (seconds) to wait for the
204 #                           #### tape drive to become ready (default: 120).
205 #
206 # initial_poll_delay=NN     #### initial delay after load before polling for
207 #                           #### readiness
208 #
209 # slotinfofile=FILENAME     #### record slot information to this file, in
210 #                           #### the line-based format "SLOT LABEL\n"
211 #
212 ####
213
214 ####
215 # Now it is time to test the setup.  Do all of the following in the
216 # directory that contains the amanda.conf file, and do all of it as
217 # the Amanda user.
218 #
219 #   * Run this:
220 #
221 #       .../chg-zd-mtx -info
222 #       echo $?             #### (or "echo $status" if you use csh/tcsh)
223 #
224 #     You should get a single line from the script like this (the actual
225 #     numbers will vary):
226 #
227 #       5 9 1 1
228 #
229 #     The first number (5) is the "current" slot.  This may or may not be
230 #     the slot actually loaded at the moment (if any).  It is the slot
231 #     Amanda will try to use next.
232 #
233 #     The second number (9) is the number of slots.
234 #
235 #     The third number will always be "1" and indicates the changer is
236 #     capable of going backward.
237 #
238 #     The fourth number is optional.  If you set $havereader to 1, it
239 #     will be "1", otherwise it will not be present.
240 #
241 #     The exit code ($? or $status) should be zero.
242 #
243 #   * Run this:
244 #
245 #       .../chg-zd-mtx -reset
246 #       echo $?
247 #
248 #     The script should output a line like this:
249 #
250 #       1 /dev/rmt/0mn
251 #
252 #     The number at the first should match $firstslot.  The device name
253 #     after that should be your tape device.
254 #
255 #     The exit code ($? or $status) should be zero.
256 #
257 #   * Run this:
258 #
259 #       .../chg-zd-mtx -slot next
260 #       echo $?
261 #
262 #     The script should output a line like this:
263 #
264 #       2 /dev/rmt/0mn
265 #
266 #     The number at the first should be one higher than $firstslot.
267 #     The device name after that should be your tape device.
268 #
269 #     The exit code ($? or $status) should be zero.
270 #
271 #   * Run this:
272 #
273 #       .../chg-zd-mtx -slot current
274 #       echo $?
275 #
276 #     Assuming the tape is still loaded from the previous test, the
277 #     robot should not move and the script should report the same thing
278 #     the previous command did.
279 #
280 #   * If you continue to run "-slot next" commands, the robot should load
281 #     each tape in turn then wrap back around to the first when it
282 #     reaches $lasttape.  If $cleanslot is within the $firstslot to
283 #     $lastslot range, the script will skip over that entry.
284 #
285 #   * Finally, try some of the amtape commands and make sure they work:
286 #
287 #       amtape <config> reset
288 #       amtape <config> slot next
289 #       amtape <config> slot current
290 #
291 #   * If you set $havereader non-zero, now would be a good time to create
292 #     the initial barcode database:
293 #
294 #       amtape <config> update
295 ####
296
297 ################################################################################
298 # To debug this script, first look in @AMANDA_DBGDIR@.  The script
299 # uses one of two log files there, depending on what version of Amanda
300 # is calling it.  It may be chg-zd-mtx.YYYYMMDD*.debug, or it may be
301 # changer.debug.driveN where 'N' is the drive number.
302 #
303 # If the log file does not help, try running the script, **as the Amanda
304 # user**, in the amanda.conf directory with whatever set of args the log
305 # said were used when you had a problem.  If nothing else useful shows up
306 # in the output, try running the script with the DEBUG environment variable
307 # set non-null, e.g.:
308 #
309 #       env DEBUG=yes .../chg-zd-mtx ...
310 ################################################################################
311
312 # source utility functions and values from configure
313 prefix=@prefix@
314 exec_prefix=@exec_prefix@
315 amlibexecdir=@amlibexecdir@
316 . ${amlibexecdir}/chg-lib.sh
317
318 test -n "$DEBUG" && set -x
319 TMPDIR="@AMANDA_TMPDIR@"
320 DBGDIR="@AMANDA_DBGDIR@"
321
322 argv0=$0
323 myname=`expr "$argv0" : '.*/\(.*\)'`
324
325 config=`pwd 2>/dev/null`
326 config=`expr "$config" : '.*/\(.*\)'`
327
328 ###
329 # Functions to write a new log file entry and append more log information.
330 ###
331
332 ds=`date '+%H:%M:%S' 2>/dev/null`
333 if [ $? -eq 0  -a  -n "$ds" ]; then
334         logprefix=`echo "$ds" | sed 's/./ /g'`
335 else
336         logprefix=""
337 fi
338
339 LogAppend() {
340         if [ -z "$logprefix" ]; then
341                 echo "$@" >> $DBGFILE
342         else
343                 echo "$logprefix" "$@" >> $DBGFILE
344         fi
345 }
346
347 Log() {
348         if [ -z "$logprefix" ]; then
349                 echo "===" "`date`" "===" >> $DBGFILE
350                 echo "$@" >> $DBGFILE
351         else
352                 ds=`date '+%H:%M:%S' 2>/dev/null`
353                 echo "$ds" "$@" >> $DBGFILE
354         fi
355 }
356
357 ###
358 # Common exit function.
359 #
360 #   $1 = exit code
361 #   $2 = slot result
362 #   $3 = additional information (error message, tape devive, etc)
363 ###
364
365 internal_call=0
366 Exit() {
367         if [ $internal_call -gt 0 ]; then
368                 call_type=Return
369         else
370                 call_type=Exit
371         fi
372         code=$1
373         shift
374         exit_slot=$1
375         shift
376         exit_answer="$@"
377         Log $call_type "($code) -> $exit_slot $@"
378         echo "$exit_slot" "$@"
379         if [ $call_type = Return ]; then
380                 return $code
381         fi
382         amgetconf dbclose.$myname:$DBGFILE > /dev/null 2>&1
383         exit $code
384 }
385
386 ###
387 # Function to run another command and log it.
388 ###
389
390 Run() {
391         Log `_ 'Running: %s' "$*"`
392         rm -f $stdout $stderr
393         "$@" > $stdout 2> $stderr
394         exitcode=$?
395         Log `_ 'Exit code: %s' "$exitcode"`
396         if [ -s $stdout ]
397         then
398                 LogAppend Stdout:
399                 cat $stdout >> $DBGFILE
400         fi
401         if [ -s $stderr ]
402         then
403                 LogAppend Stderr:
404                 cat $stderr >> $DBGFILE
405         fi
406         cat $stdout
407         cat $stderr 1>&2
408         return $exitcode
409 }
410
411 ###
412 # Return success if the arg is numeric.
413 ###
414
415 IsNumeric() {
416         test -z "$1" && return 1
417         x="`expr -- "$1" : "\([-0-9][0-9]*\)" 2>/dev/null`"
418         return `expr X"$1" != X"$x"`
419 }
420
421 ###
422 # Run $MTX status unless the previous output is still valid.
423 ###
424
425 mtx_status_valid=0
426 get_mtx_status() {
427         test -n "$DEBUG" && set -x
428         if [ $mtx_status_valid -ne 0 ]; then
429                 return 0
430         fi
431         rm -f $mtx_status
432         Run $MTX status > $mtx_status 2>&1
433         status=$?
434         if [ $status -eq 0 ]; then
435                 mtx_status_valid=1
436         fi
437
438         # shim this in here so that we get a completely new slotinfofile
439         # every time we run mtx status
440         regenerate_slotinfo_from_mtx
441
442         return $status
443 }
444
445 ###
446 # Determine the slot currently loaded.  Set $loadedslot to the slot
447 # currently loaded, or "-1", and $loadedbarcode to the corresponding
448 # barcode (or nothing).
449 ###
450
451 get_loaded_info() {
452         test -n "$DEBUG" && set -x
453         get_mtx_status
454         if [ $mtx_status_valid -eq 0 ]; then
455                 Exit 2 \
456                      `_ '<none>'` \
457                      `head -1 $mtx_status`
458                 return $?
459         fi
460
461         set x `sed -n '
462 /^Data Transfer Element:Empty/                          {
463     s/.*/-1/p
464     q
465 }
466 /^Data Transfer Element '$driveslot':Empty/             {
467     s/.*/-1/p
468     q
469 }
470 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/               {
471     s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
472     q
473 }
474 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/  {
475     s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
476     q
477 }
478 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded):VolumeTag *= *\([^     ]*\)/ {
479     s/.*:VolumeTag *= *\([^     ]*\)/-2 \1/p
480     q
481 }
482 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/                           {
483     s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
484     q
485 }
486 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/              {
487     s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
488     q
489 }
490 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/     {
491     s/.*/-2/p
492     q
493 }
494 ' < $mtx_status 2>&1`
495         shift                                   # get rid of the "x"
496         loadedslot=$1
497         loadedbarcode=$2
498         if [ -z "$loadedslot" ]; then
499                 Exit 2 \
500                     `_ '<none>'` \
501                     "could not determine current slot, are you sure your drive slot is $driveslot"
502                 return $?                       # in case we are internal
503         fi
504
505         #Use the current slot if it's empty and we don't know which slot is loaded'
506         if [ $loadedslot -eq -2 ]; then
507                 set x `sed -n '
508 {
509     /^.*Storage Element '$currentslot':Empty/ {
510         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
511         q
512     }
513     /^.*Storage Element '$currentslot':Full/ {
514         s/.*Storage Element \([0-9][0-9]*\):Full/-2/p
515         q
516     }
517     /^.*Storage Element '$currentslot' IMPORT\/EXPORT:Empty/ {
518         s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/\1/p
519         q
520     }
521     /^.*Storage Element '$currentslot' IMPORT\/EXPORT:Full/ {
522         s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full/-2/p
523         q
524     }
525 }
526 ' < $mtx_status 2>& 1`
527                 shift                           # get rid of the "x"
528                 loadedslotx=$1
529                 if [ ! -z $loadedslotx ]; then
530                         loadedslot=$loadedslotx
531                 fi
532         fi
533
534         #Use the first empty slot if we don't know which slot is loaded'
535         if [ $loadedslot -eq -2 ]; then
536                 set x `sed -n '
537 {
538     /^.*Storage Element \([0-9][0-9]*\):Empty/ {
539         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
540         q
541     }
542     /^.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/ {
543         s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Empty/\1/p
544         q
545     }
546 }
547 ' < $mtx_status 2>& 1`
548                 shift                           # get rid of the "x"
549                 loadedslot=$1
550         fi
551
552         if IsNumeric "$loadedslot" ; then
553                 :
554         else
555                 Exit 2 \
556                      `_ '<none>'` \
557                      "currently loaded slot ($loadedslot) not numeric"
558                 return $?                       # in case we are internal
559         fi
560         Log       `_ 'STATUS   -> currently loaded slot = %s' "$loadedslot"`
561         LogAppend `_ '         -> currently loaded barcode = "%s"' "$loadedbarcode"`
562 }
563
564 ###
565 # Get a list of slots between $firstslot and $lastslot, if they are set.
566 # If they are not set, set them to the first and last slot seen on the
567 # assumption the entire robot is to be used (???).
568 ###
569
570 slot_list=
571 get_slot_list() {
572         test -n "$DEBUG" && set -x
573         if [ -n "$slot_list" ]; then
574                 return
575         fi
576         get_mtx_status
577         if [ $mtx_status_valid -eq 0 ]; then
578                 Exit 2 \
579                      `_ '<none>'` \
580                      `head -1 $mtx_status`
581                 return $?
582         fi
583         slot_list=`sed -n '
584 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
585     s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
586 }
587 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
588     s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
589 }
590 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ {
591     : loop
592     n
593     /^.*Storage Element \([0-9][0-9]*\):Full/ {
594         s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
595         b loop
596     }
597     /^.*Storage Element \([0-9][0-9]*\):Empty/ {
598         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
599     }
600 }
601 /^.*Storage Element \([0-9][0-9]*\):Full/ {
602     s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
603 }
604 /^.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full/ {
605     s/.*Storage Element \([0-9][0-9]*\) IMPORT\/EXPORT:Full.*/\1/p
606 }
607 ' < $mtx_status 2>&1 | grep -v "^${cleanslot}\$" | sort -n`
608         slot_list=`echo $slot_list`             # remove the newlines
609         if [ $firstslot -lt 0 -o $lastslot -lt 0 ]; then
610                 last=$lastslot
611                 for slot in $slot_list; do
612                         if [ $firstslot -lt 0 ]; then
613                                 Log `_ 'SLOTLIST -> firstslot set to %s' "$slot"`
614                                 firstslot=$slot
615                         fi
616                         if [ $lastslot -lt 0 ]; then
617                                 last=$slot
618                         fi
619                 done
620                 if [ $lastslot -lt 0 -a $last -ge 0 ]; then
621                         Log `_ 'SLOTLIST -> lastslot set to %s' "$last"`
622                         lastslot=$last
623                 fi
624                 if [ $firstslot -lt 0 ]; then
625                         Exit 2 \
626                              `_ '<none>'` \
627                              `_ 'cannot determine first slot'`
628                         return $?               # in case we are internal
629                 elif [ $lastslot -lt 0 ]; then
630                         Exit 2 \
631                              `_ '<none>'` \
632                              `_ 'cannot determine last slot'`
633                         return $?               # in case we are internal
634                 fi
635         fi
636         amanda_slot_list=
637         for slot in $slot_list; do
638                 if [ $slot -ge $firstslot -a $slot -le $lastslot ]; then
639                         amanda_slot_list="$amanda_slot_list $slot"
640                 fi
641         done
642         if [ -z "$amanda_slot_list" ]; then
643                 Exit 2 \
644                      `_ '<none>'` \
645                      "no slots available"
646                 return $?                       # in case we are internal
647         fi
648         slot_list="$amanda_slot_list"
649 }
650
651 ###
652 # Read the labelfile and scan for a particular entry.
653 ###
654
655 read_labelfile() {
656         labelfile_entry_found=0
657         labelfile_label=
658         labelfile_barcode=
659
660         lbl_search=$1
661         bc_search=$2
662
663         line=0
664         while read lbl bc junk; do
665                 line=`expr $line + 1`
666                 if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
667                         Log       `_ 'ERROR    -> Line %s malformed: %s %s %s' "$line" "$lbl" "$bc" "$junk"`
668                         LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
669                         Exit 2 \
670                              `_ '<none>'` \
671                              `_ 'Line %s malformed in %s: %s %s %s' "$line" "$labelfile" "$lbl" "$bc" "$junk"`
672                         return $?               # in case we are internal
673                 fi
674                 if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
675                         if [ $labelfile_entry_found -ne 0 ]; then
676                                 Log       `_ 'ERROR    -> Duplicate entries: %s line %s' "$labelfile" "$line"`
677                                 LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
678                                 Exit 2 \
679                                      `_ '<none>'` \
680                                      `_ 'Duplicate entries: %s line %s' "$labelfile" "$line"`
681                                 return $?       # in case we are internal
682                         fi
683                         labelfile_entry_found=1
684                         labelfile_label=$lbl
685                         labelfile_barcode=$bc
686                 fi
687         done
688 }
689
690 lookup_label_by_barcode() {
691     [ -z "$1" ] && return
692     read_labelfile "" "$1" < $labelfile
693     echo "$labelfile_label"
694 }
695
696 lookup_barcode_by_label() {
697     [ -z "$1" ] && return
698     read_labelfile "$1" "" < $labelfile
699     echo "$labelfile_barcode"
700 }
701
702 remove_from_labelfile() {
703         labelfile=$1
704         lbl_search=$2
705         bc_search=$3
706
707         internal_remove_from_labelfile "$lbl_search" "$bc_search" < $labelfile >$labelfile.new
708         if [ $labelfile_entry_found -ne 0 ]; then
709                 mv -f $labelfile.new $labelfile
710                 LogAppend `_ 'Removed Entry "%s %s" from barcode database' "$labelfile_label" "$labelfile_barcode"`
711         fi
712 }
713
714 internal_remove_from_labelfile() {
715         labelfile_entry_found=0
716         labelfile_label=
717         labelfile_barcode=
718
719         lbl_search=$1
720         bc_search=$2
721
722         line=0
723         while read lbl bc junk; do
724                 line=`expr $line + 1`
725                 if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
726                         Log       `_ 'ERROR    -> Line %s malformed: %s %s %s' "$line" "$lbl" "$bc" "$junk"`
727                         LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
728                         Exit 2 \
729                              `_ '<none>'` \
730                              `_ 'Line %s malformed in %s: %s %s %s' "$line" "$labelfile" "$lbl" "$bc" "$junk"`
731                         return $?               # in case we are internal
732                 fi
733                 if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
734                         if [ $labelfile_entry_found -ne 0 ]; then
735                                 Log       `_ 'ERROR    -> Duplicate entries: %s line %s' "$labelfile" "$line"`
736                                 LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
737                                 Exit 2 \
738                                      `_ '<none>'` \
739                                      `_ 'Duplicate entries: %s line %s' "$labelfile" "$line"`
740                                 return $?       # in case we are internal
741                         fi
742                         labelfile_entry_found=1
743                         labelfile_label=$lbl
744                         labelfile_barcode=$bc
745                 else
746                         echo $lbl $bc
747                 fi
748         done
749 }
750
751 ###
752 # Add a new slot -> label correspondance to the slotinfo file, removing any previous
753 # information about that slot.
754 ###
755
756 record_label_in_slot() {
757     [ -z "$slotinfofile" ] && return
758     newlabel="$1"
759     newslot="$2"
760
761     (
762         if [ -f "$slotinfofile" ]; then
763                 grep -v "^$newslot " < "$slotinfofile"
764         fi
765         echo "$newslot $newlabel"
766     ) > "$slotinfofile~"
767     mv "$slotinfofile~" "$slotinfofile"
768 }
769
770 ###
771 # Remove a slot from the slotinfo file
772 ###
773
774 remove_slot_from_slotinfo() {
775     [ -z "$slotinfofile" ] && return
776     emptyslot="$1"
777
778     (
779         if [ -f "$slotinfofile" ]; then
780                 grep -v "^$emptyslot " < "$slotinfofile"
781         fi
782     ) > "$slotinfofile~"
783     mv "$slotinfofile~" "$slotinfofile"
784 }
785
786 ###
787 # Assuming get_mtx_status has been run,
788 # - if we have barcodes, regenerate the slotinfo file completely by
789 #   mapping barcodes in the status into labels using the labelfile
790 # - otherwise, remove all empty slots from the slotinfo file
791 ###
792
793 regenerate_slotinfo_from_mtx() {
794     [ -z "$slotinfofile" ] && return
795     [ "$mtx_status_valid" = "1" ] || return
796
797     if [ "$havereader" = "1" ]; then
798         # rewrite slotinfo entirely based on the status, since it has barcodes
799         :> "$slotinfofile~"
800         sed -n '/.*Storage Element \([0-9][0-9]*\).*VolumeTag *= *\([^ ]*\) *$/{
801 s/.*Storage Element \([0-9][0-9]*\).*VolumeTag *= *\([^ ]*\) *$/\1 \2/
802 p
803 }' < $mtx_status | while read newslot newbarcode; do
804                 newlabel=`lookup_label_by_barcode "$newbarcode"`
805                 if [ -n "$newlabel" ]; then
806                     echo "$newslot $newlabel" >> "$slotinfofile~"
807                 fi
808             done
809         mv "$slotinfofile~" "$slotinfofile"
810     else
811         # just remove empty slots from slotinfo
812
813         # first determine which slots are not really empty, but are
814         # loaded into a data transfer element
815 loadedslots=`sed -n '/.*(Storage Element \([0-9][0-9]*\) Loaded).*/{
816 s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/g
817 p
818 }' < $mtx_status`
819
820         # now look for any slots which are empty, but which aren't
821         # in the set of loaded slots
822         sed -n '/.*Storage Element \([0-9][0-9]*\): *Empty.*/{
823 s/.*Storage Element \([0-9][0-9]*\): *Empty.*/\1/g
824 p
825 }' < $mtx_status | while read emptyslot; do
826             reallyempty=1
827             if [ -n "$loadedslots" ]; then
828                 for loadedslot in $loadedslots; do
829                     [ "$loadedslot" = "$emptyslot" ] && reallyempty=0
830                 done
831             fi
832             if [ "$reallyempty" = "1" ]; then
833                 remove_slot_from_slotinfo "$emptyslot"
834             fi
835         done
836     fi
837 }
838
839 DBGFILE=`amgetconf dbopen.$myname 2>/dev/null`
840 if [ -z "$DBGFILE" ]
841 then
842         DBGFILE=/dev/null                       # will try this again below
843 fi
844
845 changerfile=`amgetconf changerfile 2>/dev/null`
846 if [ -z "$changerfile" ]; then
847         Exit 2 \
848              `_ '<none>'` \
849              "changerfile must be specified in amanda.conf"
850 fi
851
852 rawtape=`amgetconf tapedev 2>/dev/null`
853 if [ -z "$rawtape" ]; then
854         Exit 2 \
855              `_ '<none>'` \
856              "tapedev may not be empty"
857 fi
858 tape=`tape_device_filename "$rawtape"`
859 if [ -z "$tape" ]; then
860         Exit 2 \
861              ` _ '<none>'` \
862              "tapedev $rawtape is not a tape device."
863 elif [ $tape = "/dev/null" -o `expr "$tape" : 'null:'` -eq 5 ]; then
864         Exit 2 \
865              `_ '<none>'` \
866              "tapedev ($tape) may not be the null device"
867 fi
868 # Confusingly, TAPE is the name of the changer device...
869 TAPE=`amgetconf changerdev 2>/dev/null`
870 if [ -z "$TAPE" ]; then
871         Exit 2 \
872              `_ '<none>'` \
873              "changerdev may not be empty"
874 elif [ $TAPE = "/dev/null" ]; then
875         Exit 2 \
876              `_ '<none>'` \
877              "changerdev ($TAPE) may not be the null device"
878 fi
879 export TAPE                                     # for mtx command
880
881 CHANGER=$TAPE 
882 export CHANGER                                  # for mtx command
883
884 #### Set up the various config files.
885
886 conf_match=`expr "$changerfile" : .\*\.conf\$`
887 if [ $conf_match -ge 6 ]; then
888         configfile=$changerfile
889         changerfile=`echo $changerfile | sed 's/.conf$//g'`
890 else
891         configfile=$changerfile.conf
892 fi
893
894 if [ ! -e $configfile ]; then
895         Exit 2 \
896              `_ '<none>'` \
897              "configuration file \"$configfile\" doesn't exist"
898 fi
899 if [ ! -f $configfile ]; then
900         Exit 2 \
901              `_ '<none>'` \
902              "configuration file \"$configfile\" is not a file"
903 fi
904
905 cleanfile=$changerfile-clean
906 accessfile=$changerfile-access
907 slotfile=$changerfile-slot
908 labelfile=$changerfile-barcodes
909 slotinfofile=""
910 [ ! -s $cleanfile ] && echo 0 > $cleanfile
911 [ ! -s $accessfile ] && echo 0 > $accessfile
912 [ ! -s $slotfile ] && echo -1 > $slotfile
913 [ ! -f $labelfile ] && > $labelfile
914 cleancount=`cat $cleanfile`
915 accesscount=`cat $accessfile`
916
917 #### Dig out of the config file what is needed
918
919 varlist=
920 varlist="$varlist firstslot"
921 varlist="$varlist lastslot"
922 varlist="$varlist cleanslot"
923 varlist="$varlist cleancycle"
924 varlist="$varlist OFFLINE_BEFORE_UNLOAD"        # old name
925 varlist="$varlist offline_before_unload"
926 varlist="$varlist unloadpause"
927 varlist="$varlist AUTOCLEAN"                    # old name
928 varlist="$varlist autoclean"
929 varlist="$varlist autocleancount"
930 varlist="$varlist havereader"
931 varlist="$varlist driveslot"
932 varlist="$varlist poll_drive_ready"
933 varlist="$varlist initial_poll_delay"
934 varlist="$varlist max_drive_wait"
935 varlist="$varlist slotinfofile"
936
937 for var in $varlist
938 do
939         val="`cat $configfile 2>/dev/null | sed -n '
940 # Ignore comment lines (anything starting with a #).
941 /^[     ]*#/d
942 # Find the first var=val line in the file, print the value and quit.
943 /^[     ]*'$var'[       ]*=[    ]*\([^  ][^     ]*\).*/ {
944         s/^[    ]*'$var'[       ]*=[    ]*\([^  ][^     ]*\).*/\1/p
945         q
946 }
947 '`"
948         eval $var=\"$val\"
949 done
950
951 # Deal with driveslot first so we can get DBGFILE set if we are still
952 # using the old amgetconf.
953
954 if [ -z "$driveslot" ]; then
955         driveslot=0;
956 fi
957
958 # Get DBGFILE set if it is not already.
959
960 if [ $DBGFILE = /dev/null ]; then
961         if [ -d "$DBGDIR" ]; then
962                 DBGFILE=$DBGDIR/changer.debug.drive$driveslot
963         else
964                 DBGFILE=/dev/null
965         fi
966         Log `_ '=== Start %s ===' "\`date\`"`
967 fi
968
969 stdout=$TMPDIR/$myname.1.$$
970 stderr=$TMPDIR/$myname.2.$$
971 mtx_status=$TMPDIR/$myname.status.$$
972 trap "rm -f $stdout $stderr $mtx_status" 0      # exit cleanup
973
974 Log `_ 'Using config file %s' "$configfile"`
975
976 # Log the argument list.
977
978 Log `_ "Arg info:"`
979 LogAppend "\$# = $#"
980 i=0
981 LogAppend "\$$i = \"$argv0\""
982 for arg in "$@"; do
983         i=`expr $i + 1`
984         LogAppend "\$$i = \"$arg\""
985 done
986
987 # Set the default config values for those not in the file.  Log the
988 # results and make sure each is valid (numeric).
989
990 firstslot=${firstslot:-'-1'}                            # default: mtx status
991 lastslot=${lastslot:-'-1'}                              # default: mtx status
992 cleanslot=${cleanslot:-'-1'}                            # default: -1
993 cleancycle=${cleancycle:-'120'}                         # default: two minutes
994 if [ -z "$offline_before_unload" -a -n "$OFFLINE_BEFORE_UNLOAD" ]; then
995         offline_before_unload=$OFFLINE_BEFORE_UNLOAD    # (old name)
996 fi
997 offline_before_unload=${offline_before_unload:-'0'}     # default: 0
998 unloadpause=${unloadpause:-'0'}                         # default: 0
999 if [ -z "$autoclean" -a -n "$AUTOCLEAN" ]; then
1000         autoclean=$AUTOCLEAN                            # (old name)
1001 fi
1002 autoclean=${autoclean:-'0'}                             # default: 0
1003 autocleancount=${autocleancount:-'99'}                  # default: 99
1004 havereader=${havereader:-'0'}                           # default: 0
1005 poll_drive_ready=${poll_drive_ready:-'3'}               # default: three seconds
1006 initial_poll_delay=${initial_poll_delay:-'0'}           # default: zero zeconds
1007 max_drive_wait=${max_drive_wait:-'120'}                 # default: two minutes
1008
1009 # check MT and MTX for sanity
1010 if test "${MTX%${MTX#?}}" = "/"; then
1011     if ! test -f "${MTX}"; then
1012         Exit 2 \
1013             `_ '<none>'` \
1014             `_ "mtx binary at '%s' not found" "$MTX"`
1015     fi
1016     if ! test -x "${MTX}"; then
1017         Exit 2 \
1018             `_ '<none>'` \
1019             `_ "mtx binary at '%s' is not executable" "$MTX"`
1020     fi
1021 else
1022     # try running it to see if the shell can find it
1023     "$MTX" >/dev/null 2>/dev/null
1024     if test $? -eq 127 -o $? -eq 126; then
1025         Exit 2 \
1026             `_ '<none>'` \
1027             `_ "Could not run mtx binary at '%s'" "$MTX"`
1028     fi
1029 fi
1030
1031 error=`try_find_mt`
1032 if test $? -ne 0; then
1033     Exit 2 '<none>' $error
1034 fi
1035
1036 get_slot_list
1037
1038 Log `_ "Config info:"`
1039 for var in $varlist; do
1040         if [ $var = "OFFLINE_BEFORE_UNLOAD" ]; then
1041                 continue                        # old name
1042         elif [ $var = "AUTOCLEAN" ]; then
1043                 continue                        # old name
1044         elif [ $var = "slotinfofile" ]; then
1045                 continue                        # not numeric
1046         fi
1047         eval val=\"'$'$var\"
1048         if [ -z "$val" ]; then
1049                 Exit 2 \
1050                      `_ '<none>'` \
1051                      `_ '%s missing in %s' "$var" "$configfile"`
1052         fi
1053         if IsNumeric "$val" ; then
1054                 :
1055         else
1056                 Exit 2 \
1057                      `_ '<none>'` \
1058                      `_ '%s (%s) not numeric in %s' "$var" "$val" "$configfile"`
1059         fi
1060         LogAppend $var = \"$val\"
1061 done
1062
1063 # Run the rest of the config file sanity checks.
1064
1065 if [ $firstslot -gt $lastslot ]; then
1066         Exit 2 \
1067              `_ '<none>'` \
1068              `_ 'firstslot (%s) greater than lastslot (%s) in %s' "$firstslot" "$lastslot" "$configfile"`
1069 fi
1070 if [ $autoclean -ne 0 -a $cleanslot -lt 0 ]; then
1071         Exit 2 \
1072              `_ '<none>'` \
1073              `_ 'autoclean set but cleanslot not valid (%s)' "$cleanslot"`
1074 fi
1075
1076 # Set up the current slot
1077
1078 currentslot=`cat $slotfile`
1079 if IsNumeric "$currentslot" ; then
1080         if [ $currentslot -lt $firstslot ]; then
1081                 Log `_ 'SETUP    -> current slot %s less than %s ... resetting to %s' "$currentslot" "$firstslot" "$firstslot"`
1082                 currentslot=$firstslot
1083         elif [ $currentslot -gt $lastslot ]; then
1084                 Log `_ 'SETUP    -> current slot %s greater than %s ... resetting to %s' "$currentslot" "$lastslot" "$lastslot"`
1085                 currentslot=$lastslot
1086         fi
1087 else
1088         Log `_ 'SETUP    -> contents of %s (%s) invalid, setting current slot to first slot (%s)' "$slotfile" "$currentslot" "$firstslot"`
1089         currentslot=$firstslot
1090 fi
1091
1092 found_current=0
1093 first_slot_in_list=-1
1094 next_slot_after_current=-1
1095 for slot in $slot_list; do
1096         if [ $first_slot_in_list -lt 0 ]; then
1097                 first_slot_in_list=$slot        # in case $firstslot is missing
1098         fi
1099         if [ $slot -eq $currentslot ]; then
1100                 found_current=1
1101                 break
1102         elif [ $slot -gt $currentslot ]; then
1103                 next_slot_after_current=$slot   # $currentslot is missing
1104                 break
1105         fi
1106 done
1107 if [ $found_current -eq 0 ]; then
1108         if [ $next_slot_after_current -lt 0 ]; then
1109                 new_currentslot=$first_slot_in_list
1110         else
1111                 new_currentslot=$next_slot_after_current
1112         fi
1113         Log `_ 'WARNING  -> current slot %s not available, setting current slot to next slot (%s)' "$currentslot" "$new_currentslot"`
1114         currentslot=$new_currentslot
1115 fi
1116
1117 # More routines.
1118
1119 ###
1120 # Eject the current tape and put it away.
1121 ###
1122
1123 eject() {
1124         test -n "$DEBUG" && set -x
1125         Log `_ 'EJECT    -> ejecting tape from %s' "$tape"`
1126         get_loaded_info 
1127         if [ $loadedslot -gt 0 ]; then
1128                 Log `_ 'EJECT    -> moving tape from drive %s to storage slot %s' "$driveslot" "$loadedslot"`
1129                 if [ $offline_before_unload -ne 0 ]; then
1130                         Run try_eject_device $tape
1131                 fi
1132                 sleep $unloadpause
1133                 result=`Run $MTX unload $loadedslot $driveslot 2>&1`
1134                 status=$?
1135                 Log `_ '         -> status %s, result "%s"' "$status" "$result"`
1136                 mtx_status_valid=0
1137                 if [ $status -ne 0 ]; then
1138                         answer="$result"
1139                         code=2
1140                 else
1141                         answer="$rawtape"
1142                         code=0
1143                 fi
1144         else
1145                 answer=`_ 'Drive was not loaded'`
1146                 code=1
1147         fi
1148         Exit $code "$loadedslot" "$answer"
1149         return $?                               # in case we are internal
1150 }
1151
1152 ###
1153 # Reset the robot back to the first slot.
1154 ###
1155
1156 reset() {
1157         test -n "$DEBUG" && set -x
1158         Log `_ 'RESET    -> loading tape from slot %s to drive %s (%s)' "$firstslot" "$driveslot" "$tape"`
1159         # Call loadslot without doing it as an internal and let it finish
1160         # things up.
1161         loadslot $firstslot
1162         # NOTREACHED
1163         Exit 2 `_ '<none>'` `_ 'reset: should not get here'`
1164         return $?                               # in case we are internal
1165 }
1166
1167 ###
1168 # Unload the current tape (if necessary) and load a new one (unless
1169 # "advance").  If no tape is loaded, get the value of "current" from
1170 # $slotfile.
1171 ###
1172
1173 loadslot() {
1174         test -n "$DEBUG" && set -x
1175         if [ $# -lt 1 ]; then
1176                 Exit 2 `_ '<none>'` `_ 'Missing -slot argument'`
1177                 return $?                       # in case we are internal
1178         fi
1179         whichslot=$1
1180         Log `_ 'LOADSLOT -> load drive %s (%s) from slot %s' "$driveslot" "$tape" "$whichslot"`
1181
1182         numeric=`echo $whichslot | sed 's/[^0-9]//g'`
1183         case $whichslot in
1184         current|prev|next|advance)
1185                 find_slot=$currentslot
1186                 ;;
1187         first)
1188                 find_slot=$firstslot
1189                 ;;
1190         last)
1191                 find_slot=$lastslot
1192                 ;;
1193         $numeric)
1194                 find_slot=$numeric
1195                 ;;
1196         clean)
1197                 find_slot=$cleanslot
1198                 ;;
1199         *)
1200                 Exit 2 `_ '<none>'` `_ 'Illegal slot: "%s"' "$whichslot"`
1201                 return $?                       # in case we are internal
1202                 ;;
1203         esac
1204
1205         # Find the requested slot in the slot list.  By loading the "set"
1206         # command with multiple copies, we guarantee that if the slot is
1207         # found, we can look both forward and backward without running
1208         # off the end.  Putting $cleanslot at the end allows us to find
1209         # that slot since it is not in $slot_list.
1210         get_slot_list
1211         set x $slot_list $slot_list $slot_list $cleanslot
1212         shift                                   # get rid of the "x"
1213         prev_slot=$1
1214         shift
1215         while [ $# -gt 0 ]; do
1216                 if [ $1 -eq $find_slot ]; then
1217                         break
1218                 fi
1219                 prev_slot=$1
1220                 shift
1221         done
1222         if [ $# -le 0 ]; then
1223                 Exit 2 \
1224                      `_ '<none>'` \
1225                      `_ 'Cannot find slot %s in slot list (%s)' "$find_slot " "$slot_list"`
1226                 return $?                       # in case we are internal
1227         fi
1228
1229         # Determine the slot to load.
1230         case $whichslot in
1231         next|advance)
1232                 shift
1233                 loadslot=$1
1234                 ;;
1235         prev)
1236                 loadslot=$prev_slot
1237                 ;;
1238         *)
1239                 loadslot=$find_slot
1240         esac
1241
1242         # If the desired slot is already loaded, we are done.  Only update
1243         # current slot if this is not the cleaning slot.
1244         get_loaded_info
1245         if [ $loadslot = $loadedslot ]; then
1246                 if [ $loadslot -ne $cleanslot ]; then
1247                         rm -f $slotfile
1248                         echo $loadslot > $slotfile
1249                 fi
1250                 Exit 0 "$loadedslot" "$rawtape"
1251                 return $?                       # in case we are internal
1252         fi
1253         if [ $loadedslot -eq -2 ]; then
1254                 Exit 0 "$loadedslot" "$rawtape"
1255                 return $?                       # in case we are internal
1256         fi
1257
1258         # If we are loading the cleaning tape, bump the cleaning count
1259         # and reset the access count.  Otherwise, bump the access count
1260         # and see if it is time to do a cleaning.
1261         if [ $loadslot = $cleanslot ]; then
1262                 rm -f $cleanfile $accessfile
1263                 expr $cleancount + 1 > $cleanfile
1264                 echo 0 > $accessfile
1265         else
1266                 rm -f $accessfile
1267                 expr $accesscount + 1 > $accessfile
1268                 if [ $autoclean -ne 0 -a $accesscount -gt $autocleancount ]
1269                 then
1270                         internal_call=`expr $internal_call + 1`
1271                         loadslot clean > /dev/null 2>&1
1272                         status=$?
1273                         internal_call=`expr $internal_call - 1`
1274                         if [ $status -ne 0 ]; then
1275                                 Exit $status "$loadslot" "$exit_answer"
1276                                 return $?       # in case we are internal
1277                         fi
1278
1279                         # Slot $cleanslot might contain an ordinary tape
1280                         # rather than a cleaning tape.  A cleaning tape
1281                         # *MIGHT* auto-eject; an ordinary tape does not.
1282                         # We therefore have to read the status again to
1283                         # check what actually happened.
1284                         mtx_status_valid=0
1285                         get_loaded_info
1286                 fi
1287         fi
1288
1289         # Unload whatever tape is in the drive.
1290         internal_call=`expr $internal_call + 1`
1291         eject > /dev/null 2>&1
1292         status=$?
1293         internal_call=`expr $internal_call - 1`
1294         if [ $status -gt 1 ]; then
1295                 Exit $status "$exit_slot" "$exit_answer"
1296                 return $?                       # in case we are internal
1297         fi
1298
1299         # If we were doing an "advance", we are done.
1300         if [ $whichslot = advance ]; then
1301                 if [ $loadslot -ne $cleanslot ]; then
1302                         rm -f $slotfile
1303                         echo $loadslot > $slotfile
1304                 fi
1305                 Exit 0 "$loadslot" "/dev/null"
1306                 return $?                       # in case we are internal
1307         fi
1308
1309         # Load the tape, finally!
1310         Log `_ "LOADSLOT -> loading tape from slot %s to drive %s (%s)" "$loadslot" "$driveslot" "$tape"`
1311         result=`Run $MTX load $loadslot $driveslot 2>&1`
1312         status=$?
1313         Log `_ '         -> status %s, result "%s"' "$status" "$result"`
1314         mtx_status_valid=0
1315         if [ $status -ne 0 ]; then
1316                 Exit 2 "$loadslot" "$result"
1317                 return $?                       # in case we are internal
1318         fi
1319
1320         ###
1321         # Cleaning tapes never go "ready", so instead we just sit here
1322         # for "long enough" (as determined empirically by the user),
1323         # then return success.
1324         ###
1325         if [ $loadslot -eq $cleanslot ]; then
1326                 Run sleep $cleancycle
1327                 Exit 0 "$loadslot" "$rawtape"
1328                 return $?                       # in case we are internal
1329         fi
1330
1331         ###
1332         # Wait for the drive to go online.
1333         ###
1334         waittime=0
1335         ready=0
1336         sleep $initial_poll_delay
1337         while [ $waittime -lt $max_drive_wait ]; do
1338                 amdevcheck_status $tape
1339                 if [ $? -eq 0 ]; then
1340                         ready=1
1341                         break
1342                 fi
1343                 sleep $poll_drive_ready
1344                 waittime=`expr $waittime + $poll_drive_ready`
1345         done
1346         if [ $ready -eq 0 ]; then
1347                 Exit 2 "$loadslot" `_ 'Drive not ready after %s seconds: %s' "$max_drive_wait" "$amdevcheck_message"`
1348                 return $?                       # in case we are internal
1349         fi
1350
1351         if [ $loadslot -ne $cleanslot ]; then
1352                 rm -f $slotfile
1353                 echo $loadslot > $slotfile
1354         fi
1355         Exit 0 "$loadslot" "$rawtape"
1356         return $?                               # in case we are internal
1357 }
1358
1359 ###
1360 # Return information about how the changer is configured and the current
1361 # state of the robot.
1362 ###
1363
1364 info() {
1365         test -n "$DEBUG" && set -x
1366         get_loaded_info
1367         get_slot_list
1368         Log       `_ 'INFO     -> first slot: %s' "$firstslot"`
1369         LogAppend `_ '         -> current slot: %s' "$currentslot"`
1370         LogAppend `_ '         -> loaded slot: %s' "$loadedslot"`
1371         LogAppend `_ '         -> last slot: %s' "$lastslot"`
1372         LogAppend `_ '         -> slot list: %s' "$slot_list"`
1373         LogAppend `_ '         -> can go backwards: 1'`
1374         LogAppend `_ '         -> havereader: %s' "$havereader"`
1375
1376         ###
1377         # Check if a barcode reader is configured or not.  If so, it
1378         # passes the 4th item in the echo back to amtape signifying it
1379         # can search based on barcodes.
1380         ###
1381         reader=
1382         if [ $havereader -eq 1 ]; then
1383                 reader=1
1384         fi
1385
1386         if [ $currentslot -lt $firstslot -o $currentslot -gt $lastslot ]; then
1387                 currentslot=$firstslot          # what "current" will get
1388         fi
1389         numslots=`expr $lastslot - $firstslot + 1`
1390         Exit 0 "$currentslot" "$numslots 1 $reader"
1391         return $?                               # in case we are internal
1392 }
1393
1394 ###
1395 # Adds the label and barcode for the currently loaded tape to the
1396 # barcode file.  Return an error if the database is messed up.
1397 ###
1398
1399 addlabel() {
1400         test -n "$DEBUG" && set -x
1401         if [ $# -lt 1 ]; then
1402                 Exit 2 `_ '<none>'` `_ 'Missing -label argument'`
1403                 return $?                       # in case we are internal
1404         fi
1405         tapelabel=$1
1406         get_loaded_info
1407         if [ $loadedslot -lt 0 ]; then
1408                 Exit 1 `_ '<none>'` `_ 'No tape currently loaded'`
1409                 return $?                       # in case we are internal
1410         fi
1411         record_label_in_slot "$tapelabel" "$loadedslot"
1412         if [ $havereader -eq 0 ]; then
1413                 Exit 0 "$loadedslot" "$rawtape" # that's all we needed
1414                 return $?                       # in case we are internal
1415         fi
1416         if [ -z "$loadedbarcode" ]; then
1417                 Exit 1 `_ '<none>'` `_ 'No barcode found for tape %s.' $tapelabel`
1418                 return $?                       # in case we are internal
1419         fi
1420         Log       `_ 'LABEL    -> Adding label "%s" with barcode "%s" for slot %s into %s' "$tapelabel" "$loadedbarcode" "$loadedslot" "$labelfile"`
1421         read_labelfile "$tapelabel" "$loadedbarcode" < $labelfile
1422         if [ $labelfile_entry_found -ne 0 ]; then
1423                 lf_val=
1424                 if [ "$labelfile_barcode" != "$loadedbarcode" ]; then
1425                         lf_type=label
1426                         lf_val=$tapelabel
1427                         val_type=barcode
1428                         old_val=$labelfile_barcode
1429                         new_val=$loadedbarcode
1430                 elif [ "$labelfile_label" != "$tapelabel" ]; then
1431                         lf_type=barcode
1432                         lf_val=$loadedbarcode
1433                         val_type=label
1434                         old_val=$labelfile_label
1435                         new_val=$tapelabel
1436                 fi
1437                 if [ -n "$lf_val" ]; then
1438                         if [ "$val_type" = "barcode" ]; then
1439                                 remove_from_labelfile $labelfile "" "$old_val"
1440                         else
1441                                 remove_from_labelfile $labelfile "$old_val" ""
1442                         fi
1443                         echo "$tapelabel $loadedbarcode" >> $labelfile
1444                         LogAppend `_ '         -> appended %s entry: %s %s' "$labelfile" "$tapelabel" "$loadedbarcode"`
1445                 else
1446                         LogAppend `_ "         -> already synced"`
1447                 fi
1448         else
1449                 echo "$tapelabel $loadedbarcode" >> $labelfile
1450                 LogAppend `_ '         -> appended %s entry: %s %s' "$labelfile" "$tapelabel" "$loadedbarcode"`
1451         fi
1452         Exit 0 "$loadedslot" "$rawtape"
1453         return $?                               # in case we are internal
1454 }
1455
1456 ###
1457 # Look for a label in the barcode file.  If found, locate the slot it's
1458 # in by looking for the barcode in the mtx output, then load that tape.
1459 ###
1460
1461 searchtape() {
1462         test -n "$DEBUG" && set -x
1463         if [ $# -lt 1 ]; then
1464                 Exit 2 `_ '<none>'` `_ 'Missing -search argument'`
1465                 return $?                       # in case we are internal
1466         fi
1467         tapelabel=$1
1468         if [ $havereader -eq 0 ]; then
1469                 Exit 2 `_ '<none>'` `_ 'Not configured with barcode reader'`
1470                 return $?                       # in case we are internal
1471         fi
1472         Log `_ 'SEARCH   -> Hunting for label "%s"' "$tapelabel"`
1473         read_labelfile "$tapelabel" "" < $labelfile
1474         if [ $labelfile_entry_found -eq 0 ]; then
1475                 LogAppend `_ '         -> !!! label "%s" not found in %s !!!' "$tapelabel" "$labelfile"`
1476                 LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
1477                 Exit 2 \
1478                      `_ '<none>'` \
1479                      `_ '%s: label "%s" not found in %s' "$tapelabel" "$tapelabel" "$labelfile"`
1480                 return $?                       # in case we are internal
1481         fi
1482         LogAppend `_ '         -> barcode is "%s"' "$labelfile_barcode"`
1483         get_mtx_status
1484         if [ $mtx_status_valid -eq 0 ]; then
1485                 Exit 2 \
1486                      `_ '<none>'` \
1487                      `head -1 $mtx_status`
1488                 return $?
1489         fi
1490         foundslot=`sed -n '
1491 /VolumeTag *= *'$labelfile_barcode' *$/                 {
1492         s/.*Storage Element \([0-9][0-9]*\).*/\1/p
1493         q
1494 }
1495 ' < $mtx_status`
1496         LogAppend `_ '         -> foundslot is %s' "$foundslot"`
1497         if [ -z "$foundslot" ]; then
1498                 LogAppend `_ 'ERROR    -> !!! Could not find slot for barcode "%s"!!!' "$labelfile_barcode"`
1499                 LogAppend `_ '         -> Remove %s and run "%s %s update"' "$labelfile" "$sbindir/amtape" "$config"`
1500                 Exit 2 \
1501                      `_ '<none>'` \
1502                      `_ 'barcode "%s" not found in mtx status output' "$labelfile_barcode"`
1503                 return $?                       # in case we are internal
1504         fi
1505         # Call loadslot without doing it as an internal and let it finish
1506         # things up.
1507         loadslot $foundslot
1508         # NOTREACHED
1509         Exit 2 `_ '<none>'` `_ 'searchtape: should not get here'`
1510         return $?                               # in case we are internal
1511 }
1512
1513 ###
1514 # Program invocation begins here
1515 ###
1516
1517 if [ $# -lt 1 ]; then
1518         Exit 2 `_ '<none>'` `_ 'Usage: %s -command args' "$myname"`
1519 fi
1520 cmd=$1
1521 shift
1522 case "$cmd" in
1523 -slot)
1524         loadslot "$@"
1525         ;;
1526 -info)
1527         info "$@"
1528         ;;
1529 -reset)
1530         reset "$@"
1531         ;;
1532 -eject)
1533         eject "$@"
1534         ;;
1535 -label) 
1536         addlabel "$@"
1537         ;;
1538 -search)
1539         searchtape "$@"
1540         ;;
1541 -clean)
1542         loadslot clean
1543         ;;
1544 *)
1545         Exit 2 `_ '<none>'` `_ 'unknown option: %s' "$cmd"`
1546         ;;
1547 esac
1548
1549 Exit 2 `_ '<none>'` `_ '%s: should not get here' "$myname"`