Imported Debian patch 2.5.1-1
[debian/amanda] / changer-src / chg-zd-mtx.sh.in
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 #
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.
76 #
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).
83 #
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.
87 #
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.
95 #
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
99 #     mount.
100 #
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).
111 #
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.
116 #
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).
122 #
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.
125 #
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
131 #     to store the tape.
132 #
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.
137 #
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.
143 #
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.
147 #
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.
155 ####
156
157 ####
158 # Now you are ready to set up the variables in the changer configuration
159 # file.
160 #
161 # All variables are in "changerfile".conf where "changerfile" is set
162 # in amanda.conf.  For example, if amanda.conf has:
163 #
164 #       changerfile="/etc/amanda/Dailyset1/CHANGER"
165 #    or changerfile="/etc/amanda/Dailyset1/CHANGER.conf"
166 #
167 # the variables must be in "/etc/amanda/Dailyset1/CHANGER.conf".
168 # The ".conf" is appended only if it's not there".
169 #
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.
173 #
174 # Here is a commented out example file with all the variables and showing
175 # their default value (if any):
176 ####
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
183 #
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.
189 #
190 # autoclean=0               #### Set to '1' or greater to enable
191 #
192 # autocleancount=99         #### Number of access before a clean.
193 #
194 # cleancycle=120            #### Time (seconds) to clean drive (default 120)
195 #
196 # havereader=0              #### If you have a barcode reader, set to 1.
197 #
198 # offline_before_unload=0   #### Does your robot require an
199 #                           #### 'mt offline' before mtx unload?
200 #
201 # poll_drive_ready=NN       #### Time (seconds) between tests to see if
202 #                           #### the tape drive has gone ready (default: 3).
203 #
204 # max_drive_wait=NN         #### Maximum time (seconds) to wait for the
205 #                           #### tape drive to become ready (default: 120).
206 #
207 # initial_poll_delay=NN     #### initial delay after load before polling for
208 #                           #### readiness
209 ####
210
211 ####
212 # Now it is time to test the setup.  Do all of the following in the
213 # directory that contains the amanda.conf file, and do all of it as
214 # the Amanda user.
215 #
216 #   * Run this:
217 #
218 #       .../chg-zd-mtx -info
219 #       echo $?             #### (or "echo $status" if you use csh/tcsh)
220 #
221 #     You should get a single line from the script like this (the actual
222 #     numbers will vary):
223 #
224 #       5 9 1 1
225 #
226 #     The first number (5) is the "current" slot.  This may or may not be
227 #     the slot actually loaded at the moment (if any).  It is the slot
228 #     Amanda will try to use next.
229 #
230 #     The second number (9) is the number of slots.
231 #
232 #     The third number will always be "1" and indicates the changer is
233 #     capable of going backward.
234 #
235 #     The fourth number is optional.  If you set $havereader to 1, it
236 #     will be "1", otherwise it will not be present.
237 #
238 #     The exit code ($? or $status) should be zero.
239 #
240 #   * Run this:
241 #
242 #       .../chg-zd-mtx -reset
243 #       echo $?
244 #
245 #     The script should output a line like this:
246 #
247 #       1 /dev/rmt/0mn
248 #
249 #     The number at the first should match $firstslot.  The device name
250 #     after that should be your tape device.
251 #
252 #     The exit code ($? or $status) should be zero.
253 #
254 #   * Run this:
255 #
256 #       .../chg-zd-mtx -slot next
257 #       echo $?
258 #
259 #     The script should output a line like this:
260 #
261 #       2 /dev/rmt/0mn
262 #
263 #     The number at the first should be one higher than $firstslot.
264 #     The device name after that should be your tape device.
265 #
266 #     The exit code ($? or $status) should be zero.
267 #
268 #   * Run this:
269 #
270 #       .../chg-zd-mtx -slot current
271 #       echo $?
272 #
273 #     Assuming the tape is still loaded from the previous test, the
274 #     robot should not move and the script should report the same thing
275 #     the previous command did.
276 #
277 #   * If you continue to run "-slot next" commands, the robot should load
278 #     each tape in turn then wrap back around to the first when it
279 #     reaches $lasttape.  If $cleanslot is within the $firstslot to
280 #     $lastslot range, the script will skip over that entry.
281 #
282 #   * Finally, try some of the amtape commands and make sure they work:
283 #
284 #       amtape <config> reset
285 #       amtape <config> slot next
286 #       amtape <config> slot current
287 #
288 #   * If you set $havereader non-zero, now would be a good time to create
289 #     the initial barcode database:
290 #
291 #       amtape <config> update
292 ####
293
294 ################################################################################
295 # To debug this script, first look in @AMANDA_DBGDIR@.  The script
296 # uses one of two log files there, depending on what version of Amanda
297 # is calling it.  It may be chg-zd-mtx.YYYYMMDD*.debug, or it may be
298 # changer.debug.driveN where 'N' is the drive number.
299 #
300 # If the log file does not help, try running the script, **as the Amanda
301 # user**, in the amanda.conf directory with whatever set of args the log
302 # said were used when you had a problem.  If nothing else useful shows up
303 # in the output, try running the script with the DEBUG environment variable
304 # set non-null, e.g.:
305 #
306 #       env DEBUG=yes .../chg-zd-mtx ...
307 ################################################################################
308
309 ################################################################################
310 # You may need to customize these things
311 ################################################################################
312
313 MT=@MT@
314 MTF=@MT_FILE_FLAG@
315 MTX=@MTX@
316
317 ################################################################################
318 # No user-level customization should be required beyond this point.
319 ################################################################################
320
321 test -n "$DEBUG" && set -x
322 TMPDIR="@AMANDA_TMPDIR@"
323 DBGDIR="@AMANDA_DBGDIR@"
324
325 argv0=$0
326 myname=`expr "$argv0" : '.*/\(.*\)'`
327
328 config=`pwd 2>/dev/null`
329 config=`expr "$config" : '.*/\(.*\)'`
330
331 ###
332 # Functions to write a new log file entry and append more log information.
333 ###
334
335 ds=`date '+%H:%M:%S' 2>/dev/null`
336 if [ $? -eq 0  -a  -n "$ds" ]; then
337         logprefix=`echo "$ds" | sed 's/./ /g'`
338 else
339         logprefix=""
340 fi
341
342 LogAppend() {
343         if [ -z "$logprefix" ]; then
344                 echo "$@" >> $DBGFILE
345         else
346                 echo "$logprefix" "$@" >> $DBGFILE
347         fi
348 }
349
350 Log() {
351         if [ -z "$logprefix" ]; then
352                 echo "===" "`date`" "===" >> $DBGFILE
353                 echo "$@" >> $DBGFILE
354         else
355                 ds=`date '+%H:%M:%S' 2>/dev/null`
356                 echo "$ds" "$@" >> $DBGFILE
357         fi
358 }
359
360 ###
361 # Common exit function.
362 #
363 #   $1 = exit code
364 #   $2 = slot result
365 #   $3 = additional information (error message, tape devive, etc)
366 ###
367
368 internal_call=0
369 Exit() {
370         if [ $internal_call -gt 0 ]; then
371                 call_type=Return
372         else
373                 call_type=Exit
374         fi
375         code=$1
376         shift
377         exit_slot=$1
378         shift
379         exit_answer="$@"
380         Log $call_type "($code)" "->" "$exit_slot" "$@"
381         echo "$exit_slot" "$@"
382         if [ $call_type = Return ]; then
383                 return $code
384         fi
385         amgetconf$SUF dbclose.$argv0:$DBGFILE > /dev/null 2>&1
386         exit $code
387 }
388
389 ###
390 # Function to run another command and log it.
391 ###
392
393 Run() {
394         Log Running: "$@"
395         rm -f $stdout $stderr
396         "$@" > $stdout 2> $stderr
397         exitcode=$?
398         Log Exit code: $exitcode
399         if [ -s $stdout ]
400         then
401                 LogAppend Stdout:
402                 cat $stdout >> $DBGFILE
403         fi
404         if [ -s $stderr ]
405         then
406                 LogAppend Stderr:
407                 cat $stderr >> $DBGFILE
408         fi
409         cat $stdout
410         cat $stderr 1>&2
411         return $exitcode
412 }
413
414 ###
415 # Return success if the arg is numeric.
416 ###
417
418 IsNumeric() {
419         test -z "$1" && return 1
420         x="`expr "$1" : '\([-0-9][0-9]*\)' 2>/dev/null`"
421         return `expr X"$1" != X"$x"`
422 }
423
424 ###
425 # Run $MTX status unless the previous output is still valid.
426 ###
427
428 mtx_status_valid=0
429 get_mtx_status() {
430         test -n "$DEBUG" && set -x
431         if [ $mtx_status_valid -ne 0 ]; then
432                 return 0
433         fi
434         rm -f $mtx_status
435         Run $MTX status > $mtx_status 2>&1
436         status=$?
437         if [ $status -eq 0 ]; then
438                 mtx_status_valid=1
439         fi
440         return $status
441 }
442
443 ###
444 # Determine the slot currently loaded.  Set $loadedslot to the slot
445 # currently loaded, or "-1", and $loadedbarcode to the corresponding
446 # barcode (or nothing).
447 ###
448
449 get_loaded_info() {
450         test -n "$DEBUG" && set -x
451         get_mtx_status
452
453         set x `sed -n '
454 /^Data Transfer Element:Empty/                          {
455     s/.*/-1/p
456     q
457 }
458 /^Data Transfer Element '$driveslot':Empty/             {
459     s/.*/-1/p
460     q
461 }
462 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/               {
463     s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
464     q
465 }
466 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/  {
467     s/.*(Storage Element \([0-9][0-9]*\) Loaded):VolumeTag *= *\([^     ]*\)/\1 \2/p
468     q
469 }
470 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded):VolumeTag *= *\([^     ]*\)/ {
471     s/.*:VolumeTag *= *\([^     ]*\)/-2 \1/p
472     q
473 }
474 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/                           {
475     s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
476     q
477 }
478 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/              {
479     s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
480     q
481 }
482 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/     {
483     s/.*/-2/p
484     q
485 }
486 ' < $mtx_status 2>&1`
487         shift                                   # get rid of the "x"
488         loadedslot=$1
489         loadedbarcode=$2
490         if [ -z "$loadedslot" ]; then
491                 Exit 2 "<none>" "could not determine current slot, are you sure your drive slot is $driveslot"
492                 return $?                       # in case we are internal
493         fi
494
495         #Use the current slot if it's empty and we don't know which slot is loaded'
496         if [ $loadedslot -eq -2 ]; then
497                 set x `sed -n '
498 {
499     /^.*Storage Element '$currentslot':Empty/ {
500         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
501         q
502     }
503     /^.*Storage Element '$currentslot':Full/ {
504         s/.*Storage Element \([0-9][0-9]*\):Full/-2/p
505         q
506     }
507 }
508 ' < $mtx_status 2>& 1`
509                 shift                           # get rid of the "x"
510                 loadedslotx=$1
511                 if [ ! -z $loadedslotx ]; then
512                         loadedslot=$loadedslotx
513                 fi
514         fi
515
516         #Use the first empty slot if we don't know which slot is loaded'
517         if [ $loadedslot -eq -2 ]; then
518                 set x `sed -n '
519 {
520     /^.*Storage Element \([0-9][0-9]*\):Empty/ {
521         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
522         q
523     }
524 }
525 ' < $mtx_status 2>& 1`
526                 shift                           # get rid of the "x"
527                 loadedslot=$1
528         fi
529
530         if IsNumeric "$loadedslot" ; then
531                 :
532         else
533                 Exit 2 \
534                      "<none>" \
535                      "currently loaded slot ($loadedslot) not numeric"
536                 return $?                       # in case we are internal
537         fi
538         Log       "STATUS   -> currently loaded slot = $loadedslot"
539         LogAppend "         -> currently loaded barcode = \"$loadedbarcode\""
540 }
541
542 ###
543 # Get a list of slots between $firstslot and $lastslot, if they are set.
544 # If they are not set, set them to the first and last slot seen on the
545 # assumption the entire robot is to be used (???).
546 ###
547
548 slot_list=
549 get_slot_list() {
550         test -n "$DEBUG" && set -x
551         if [ -n "$slot_list" ]; then
552                 return
553         fi
554         get_mtx_status
555         slot_list=`sed -n '
556 /^Data Transfer Element:Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
557     s/.*(Storage Element \([0-9][0-9]*\) Loaded).*/\1/p
558 }
559 /^Data Transfer Element '$driveslot':Full (Storage Element \([0-9][0-9]*\) Loaded)/ {
560     s/.*Storage Element \([0-9][0-9]*\) Loaded.*/\1/p
561 }
562 /^Data Transfer Element '$driveslot':Full (Unknown Storage Element Loaded)/ {
563     : loop
564     n
565     /^.*Storage Element \([0-9][0-9]*\):Full/ {
566         s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
567         b loop
568     }
569     /^.*Storage Element \([0-9][0-9]*\):Empty/ {
570         s/.*Storage Element \([0-9][0-9]*\):Empty/\1/p
571     }
572 }
573 /^.*Storage Element \([0-9][0-9]*\):Full/ {
574     s/.*Storage Element \([0-9][0-9]*\):Full.*/\1/p
575 }
576 ' < $mtx_status 2>&1 | grep -v "^${cleanslot}\$" | sort -n`
577         slot_list=`echo $slot_list`             # remove the newlines
578         if [ $firstslot -lt 0 -o $lastslot -lt 0 ]; then
579                 last=$lastslot
580                 for slot in $slot_list; do
581                         if [ $firstslot -lt 0 ]; then
582                                 Log "SLOTLIST -> firstslot set to $slot"
583                                 firstslot=$slot
584                         fi
585                         if [ $lastslot -lt 0 ]; then
586                                 last=$slot
587                         fi
588                 done
589                 if [ $lastslot -lt 0 -a $last -ge 0 ]; then
590                         Log "SLOTLIST -> lastslot set to $last"
591                         lastslot=$last
592                 fi
593                 if [ $firstslot -lt 0 ]; then
594                         Exit 2 \
595                              "<none>" \
596                              "cannot determine first slot"
597                         return $?               # in case we are internal
598                 elif [ $lastslot -lt 0 ]; then
599                         Exit 2 \
600                              "<none>" \
601                              "cannot determine last slot"
602                         return $?               # in case we are internal
603                 fi
604         fi
605         amanda_slot_list=
606         for slot in $slot_list; do
607                 if [ $slot -ge $firstslot -a $slot -le $lastslot ]; then
608                         amanda_slot_list="$amanda_slot_list $slot"
609                 fi
610         done
611         if [ -z "$amanda_slot_list" ]; then
612                 Exit 2 \
613                      "<none>" \
614                      "no slots available"
615                 return $?                       # in case we are internal
616         fi
617         slot_list="$amanda_slot_list"
618 }
619
620 # Paths
621 prefix=@prefix@
622 exec_prefix=@exec_prefix@
623 sbindir=@sbindir@
624 libexecdir=@libexecdir@
625
626 # try to hit all the possibilities here
627 PATH=$sbindir:$libexecdir:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb:/usr/local/bin
628 export PATH
629
630 USE_VERSION_SUFFIXES="@USE_VERSION_SUFFIXES@"
631 if test "$USE_VERSION_SUFFIXES" = "yes"; then
632         SUF="-@VERSION@"
633 else
634         SUF=
635 fi
636
637 DBGFILE=`amgetconf$SUF dbopen.$argv0 2>/dev/null`
638 if [ -z "$DBGFILE" ]
639 then
640         DBGFILE=/dev/null                       # will try this again below
641 fi
642
643 changerfile=`amgetconf$SUF changerfile 2>/dev/null`
644 if [ -z "$changerfile" ]; then
645         Exit 2 \
646              "<none>" \
647              "changerfile must be specified in amanda.conf"
648 fi
649
650 tape=`amgetconf$SUF tapedev 2>/dev/null`
651 if [ -z "$tape" ]; then
652         Exit 2 \
653              "<none>" \
654              "tapedev may not be empty"
655 elif [ $tape = "/dev/null" -o `expr "$tape" : 'null:'` -eq 5 ]; then
656         Exit 2 \
657              "<none>" \
658              "tapedev ($tape) may not be the null device"
659 fi
660 TAPE=`amgetconf$SUF changerdev 2>/dev/null`
661 if [ -z "$TAPE" ]; then
662         Exit 2 \
663              "<none>" \
664              "changerdev may not be empty"
665 elif [ $TAPE = "/dev/null" ]; then
666         Exit 2 \
667              "<none>" \
668              "changerdev ($TAPE) may not be the null device"
669 fi
670 export TAPE                                     # for mtx command
671
672 #### Set up the various config files.
673
674 conf_match=`expr "$changerfile" : .\*\.conf\$`
675 if [ $conf_match -ge 6 ]; then
676         configfile=$changerfile
677         changerfile=`echo $changerfile | sed 's/.conf$//g'`
678 else
679         configfile=$changerfile.conf
680 fi
681
682 cleanfile=$changerfile-clean
683 accessfile=$changerfile-access
684 slotfile=$changerfile-slot
685 labelfile=$changerfile-barcodes
686 [ ! -s $cleanfile ] && echo 0 > $cleanfile
687 [ ! -s $accessfile ] && echo 0 > $accessfile
688 [ ! -s $slotfile ] && echo -1 > $slotfile
689 [ ! -f $labelfile ] && > $labelfile
690 cleancount=`cat $cleanfile`
691 accesscount=`cat $accessfile`
692
693 #### Dig out of the config file what is needed
694
695 varlist=
696 varlist="$varlist firstslot"
697 varlist="$varlist lastslot"
698 varlist="$varlist cleanslot"
699 varlist="$varlist cleancycle"
700 varlist="$varlist OFFLINE_BEFORE_UNLOAD"        # old name
701 varlist="$varlist offline_before_unload"
702 varlist="$varlist unloadpause"
703 varlist="$varlist AUTOCLEAN"                    # old name
704 varlist="$varlist autoclean"
705 varlist="$varlist autocleancount"
706 varlist="$varlist havereader"
707 varlist="$varlist driveslot"
708 varlist="$varlist poll_drive_ready"
709 varlist="$varlist initial_poll_delay"
710 varlist="$varlist max_drive_wait"
711
712 for var in $varlist
713 do
714         val="`cat $configfile 2>/dev/null | sed -n '
715 # Ignore comment lines (anything starting with a #).
716 /^[     ]*#/d
717 # Find the first var=val line in the file, print the value and quit.
718 /^[     ]*'$var'[       ]*=[    ]*\([^  ][^     ]*\).*/ {
719         s/^[    ]*'$var'[       ]*=[    ]*\([^  ][^     ]*\).*/\1/p
720         q
721 }
722 '`"
723         eval $var=\"$val\"
724 done
725
726 # Deal with driveslot first so we can get DBGFILE set if we are still
727 # using the old amgetconf.
728
729 if [ -z "$driveslot" ]; then
730         driveslot=0;
731 fi
732
733 # Get DBGFILE set if it is not already.
734
735 if [ $DBGFILE = /dev/null ]; then
736         if [ -d "$DBGDIR" ]; then
737                 DBGFILE=$DBGDIR/changer.debug.drive$driveslot
738         else
739                 DBGFILE=/dev/null
740         fi
741         Log === Start "`date`" ===
742 fi
743 if [ -z "$driveslot" ]; then
744         Exit 2 \
745              "<none>" \
746              "cannot determine drive slot from $tape"
747 fi
748
749 stdout=$TMPDIR/$myname.1.$$
750 stderr=$TMPDIR/$myname.2.$$
751 mtx_status=$TMPDIR/$myname.status.$$
752 trap "rm -f $stdout $stderr $mtx_status" 0      # exit cleanup
753
754 Log "Using config file $configfile"
755
756 # Log the argument list.
757
758 Log "Arg info:"
759 LogAppend "\$# = $#"
760 i=0
761 LogAppend "\$$i = \"$argv0\""
762 for arg in "$@"; do
763         i=`expr $i + 1`
764         LogAppend "\$$i = \"$arg\""
765 done
766
767 # Set the default config values for those not in the file.  Log the
768 # results and make sure each is valid (numeric).
769
770 firstslot=${firstslot:-'-1'}                            # default: mtx status
771 lastslot=${lastslot:-'-1'}                              # default: mtx status
772 cleanslot=${cleanslot:-'-1'}                            # default: -1
773 cleancycle=${cleancycle:-'120'}                         # default: two minutes
774 if [ -z "$offline_before_unload" -a -n "$OFFLINE_BEFORE_UNLOAD" ]; then
775         offline_before_unload=$OFFLINE_BEFORE_UNLOAD    # (old name)
776 fi
777 offline_before_unload=${offline_before_unload:-'0'}     # default: 0
778 unloadpause=${unloadpause:-'0'}                         # default: 0
779 if [ -z "$autoclean" -a -n "$AUTOCLEAN" ]; then
780         autoclean=$AUTOCLEAN                            # (old name)
781 fi
782 autoclean=${autoclean:-'0'}                             # default: 0
783 autocleancount=${autocleancount:-'99'}                  # default: 99
784 havereader=${havereader:-'0'}                           # default: 0
785 poll_drive_ready=${poll_drive_ready:-'3'}               # default: three seconds
786 initial_poll_delay=${initial_poll_delay:-'0'}           # default: zero zeconds
787 max_drive_wait=${max_drive_wait:-'120'}                 # default: two minutes
788
789 get_slot_list
790
791 Log "Config info:"
792 for var in $varlist; do
793         if [ $var = "OFFLINE_BEFORE_UNLOAD" ]; then
794                 continue                        # old name
795         elif [ $var = "AUTOCLEAN" ]; then
796                 continue                        # old name
797         fi
798         eval val=\"'$'$var\"
799         if [ -z "$val" ]; then
800                 Exit 2 \
801                      "<none>" \
802                      "$var missing in $configfile"
803         fi
804         if IsNumeric "$val" ; then
805                 :
806         else
807                 Exit 2 \
808                      "<none>" \
809                      "$var ($val) not numeric in $configfile"
810         fi
811         LogAppend $var = \"$val\"
812 done
813
814 # Run the rest of the config file sanity checks.
815
816 if [ $firstslot -gt $lastslot ]; then
817         Exit 2 \
818              "<none>" \
819              "firstslot ($firstslot) greater than" \
820              "lastslot ($lastslot) in $configfile"
821 fi
822 if [ $autoclean -ne 0 -a $cleanslot -lt 0 ]; then
823         Exit 2 \
824              "<none>" \
825              "autoclean set but cleanslot not valid ($cleanslot)"
826 fi
827
828 # Set up the current slot
829
830 currentslot=`cat $slotfile`
831 if IsNumeric "$currentslot" ; then
832         if [ $currentslot -lt $firstslot ]; then
833                 Log "SETUP    -> current slot $currentslot" \
834                                  "less than $firstslot ..." \
835                                  "resetting to $firstslot"
836                 currentslot=$firstslot
837         elif [ $currentslot -gt $lastslot ]; then
838                 Log "SETUP    -> current slot $currentslot" \
839                                  "greater than $lastslot ..." \
840                                  "resetting to $lastslot"
841                 currentslot=$lastslot
842         fi
843 else
844         Log "SETUP    -> contents of $slotfile ($currentslot) invalid," \
845                          "setting current slot to first slot ($firstslot)"
846         currentslot=$firstslot
847 fi
848
849 found_current=0
850 first_slot_in_list=-1
851 next_slot_after_current=-1
852 for slot in $slot_list; do
853         if [ $first_slot_in_list -lt 0 ]; then
854                 first_slot_in_list=$slot        # in case $firstslot is missing
855         fi
856         if [ $slot -eq $currentslot ]; then
857                 found_current=1
858                 break
859         elif [ $slot -gt $currentslot ]; then
860                 next_slot_after_current=$slot   # $currentslot is missing
861                 break
862         fi
863 done
864 if [ $found_current -eq 0 ]; then
865         if [ $next_slot_after_current -lt 0 ]; then
866                 new_currentslot=$first_slot_in_list
867         else
868                 new_currentslot=$next_slot_after_current
869         fi
870         Log "WARNING  -> current slot $currentslot not available," \
871                          "setting current slot to next slot ($new_currentslot)"
872         currentslot=$new_currentslot
873 fi
874
875 # More routines.
876
877 ###
878 # Eject the current tape and put it away.
879 ###
880
881 eject() {
882         test -n "$DEBUG" && set -x
883         Log "EJECT    -> ejecting tape from $tape"
884         get_loaded_info 
885         if [ $loadedslot -gt 0 ]; then
886                 Log "EJECT    -> moving tape from drive $driveslot" \
887                                  "to storage slot $loadedslot"
888                 if [ $offline_before_unload -ne 0 ]; then
889                         Run $MT $MTF $tape offline > /dev/null 2>&1
890                 fi
891                 sleep $unloadpause
892                 result=`Run $MTX unload $loadedslot $driveslot 2>&1`
893                 status=$?
894                 Log "         -> status $status, result \"$result\""
895                 mtx_status_valid=0
896                 if [ $status -ne 0 ]; then
897                         answer="$result"
898                         code=2
899                 else
900                         answer="$tape"
901                         code=0
902                 fi
903         else
904                 answer="Drive was not loaded"
905                 code=1
906         fi
907         Exit $code "$loadedslot" "$answer"
908         return $?                               # in case we are internal
909 }
910
911 ###
912 # Reset the robot back to the first slot.
913 ###
914
915 reset() {
916         test -n "$DEBUG" && set -x
917         Log "RESET    -> loading tape from slot $firstslot" \
918                          "to drive $driveslot ($tape)"
919         # Call loadslot without doing it as an internal and let it finish
920         # things up.
921         loadslot $firstslot
922         # NOTREACHED
923         Exit 2 "<none>" "reset: should not get here"
924         return $?                               # in case we are internal
925 }
926
927 ###
928 # Unload the current tape (if necessary) and load a new one (unless
929 # "advance").  If no tape is loaded, get the value of "current" from
930 # $slotfile.
931 ###
932
933 loadslot() {
934         test -n "$DEBUG" && set -x
935         if [ $# -lt 1 ]; then
936                 Exit 2 "<none>" "Missing -slot argument"
937                 return $?                       # in case we are internal
938         fi
939         whichslot=$1
940         Log "LOADSLOT -> load drive $driveslot ($tape) from slot $whichslot"
941
942         numeric=`echo $whichslot | sed 's/[^0-9]//g'`
943         case $whichslot in
944         current|prev|next|advance)
945                 find_slot=$currentslot
946                 ;;
947         first)
948                 find_slot=$firstslot
949                 ;;
950         last)
951                 find_slot=$lastslot
952                 ;;
953         $numeric)
954                 find_slot=$numeric
955                 ;;
956         clean)
957                 find_slot=$cleanslot
958                 ;;
959         *)
960                 Exit 2 "<none>" "Illegal slot: \"$whichslot\""
961                 return $?                       # in case we are internal
962                 ;;
963         esac
964
965         # Find the requested slot in the slot list.  By loading the "set"
966         # command with multiple copies, we guarantee that if the slot is
967         # found, we can look both forward and backward without running
968         # off the end.  Putting $cleanslot at the end allows us to find
969         # that slot since it is not in $slot_list.
970         get_slot_list
971         set x $slot_list $slot_list $slot_list $cleanslot
972         shift                                   # get rid of the "x"
973         prev_slot=$1
974         shift
975         while [ $# -gt 0 ]; do
976                 if [ $1 -eq $find_slot ]; then
977                         break
978                 fi
979                 prev_slot=$1
980                 shift
981         done
982         if [ $# -le 0 ]; then
983                 Exit 2 \
984                      "<none>" \
985                      "Cannot find slot $find_slot" \
986                      "in slot list ($slot_list)"
987                 return $?                       # in case we are internal
988         fi
989
990         # Determine the slot to load.
991         case $whichslot in
992         next|advance)
993                 shift
994                 loadslot=$1
995                 ;;
996         prev)
997                 loadslot=$prev_slot
998                 ;;
999         *)
1000                 loadslot=$find_slot
1001         esac
1002
1003         # If the desired slot is already loaded, we are done.  Only update
1004         # current slot if this is not the cleaning slot.
1005         get_loaded_info
1006         if [ $loadslot = $loadedslot ]; then
1007                 if [ $loadslot -ne $cleanslot ]; then
1008                         rm -f $slotfile
1009                         echo $loadslot > $slotfile
1010                 fi
1011                 Exit 0 "$loadedslot" "$tape"
1012                 return $?                       # in case we are internal
1013         fi
1014         if [ $loadedslot -eq -2 ]; then
1015                 Exit 0 "$loadedslot" "$tape"
1016                 return $?                       # in case we are internal
1017         fi
1018
1019         # If we are loading the cleaning tape, bump the cleaning count
1020         # and reset the access count.  Otherwise, bump the access count
1021         # and see if it is time to do a cleaning.
1022         if [ $loadslot = $cleanslot ]; then
1023                 rm -f $cleanfile $accessfile
1024                 expr $cleancount + 1 > $cleanfile
1025                 echo 0 > $accessfile
1026         else
1027                 rm -f $accessfile
1028                 expr $accesscount + 1 > $accessfile
1029                 if [ $autoclean -ne 0 -a $accesscount -gt $autocleancount ]
1030                 then
1031                         internal_call=`expr $internal_call + 1`
1032                         loadslot clean > /dev/null 2>&1
1033                         status=$?
1034                         internal_call=`expr $internal_call - 1`
1035                         if [ $status -ne 0 ]; then
1036                                 Exit $status "$loadslot" "$exit_answer"
1037                                 return $?       # in case we are internal
1038                         fi
1039
1040                         # Slot $cleanslot might contain an ordinary tape
1041                         # rather than a cleaning tape.  A cleaning tape
1042                         # *MIGHT* auto-eject; an ordinary tape does not.
1043                         # We therefore have to read the status again to
1044                         # check what actually happened.
1045                         mtx_status_valid=0
1046                         get_loaded_info
1047                 fi
1048         fi
1049
1050         # Unload whatever tape is in the drive.
1051         internal_call=`expr $internal_call + 1`
1052         eject > /dev/null 2>&1
1053         status=$?
1054         internal_call=`expr $internal_call - 1`
1055         if [ $status -gt 1 ]; then
1056                 Exit $status "$exit_slot" "$exit_answer"
1057                 return $?                       # in case we are internal
1058         fi
1059
1060         # If we were doing an "advance", we are done.
1061         if [ $whichslot = advance ]; then
1062                 if [ $loadslot -ne $cleanslot ]; then
1063                         rm -f $slotfile
1064                         echo $loadslot > $slotfile
1065                 fi
1066                 Exit 0 "$loadslot" "/dev/null"
1067                 return $?                       # in case we are internal
1068         fi
1069
1070         # Load the tape, finally!
1071         Log "LOADSLOT -> loading tape from slot $loadslot" \
1072                          "to drive $driveslot ($tape)"
1073         result=`Run $MTX load $loadslot $driveslot 2>&1`
1074         status=$?
1075         Log "         -> status $status, result \"$result\""
1076         mtx_status_valid=0
1077         if [ $status -ne 0 ]; then
1078                 Exit 2 "$loadslot" "$result"
1079                 return $?                       # in case we are internal
1080         fi
1081
1082         ###
1083         # Cleaning tapes never go "ready", so instead we just sit here
1084         # for "long enough" (as determined empirically by the user),
1085         # then return success.
1086         ###
1087         if [ $loadslot -eq $cleanslot ]; then
1088                 Run sleep $cleancycle
1089                 Exit 0 "$loadslot" "$tape"
1090                 return $?                       # in case we are internal
1091         fi
1092
1093         ###
1094         # Wait for the drive to go online.
1095         ###
1096         waittime=0
1097         ready=0
1098         sleep $initial_poll_delay
1099         while [ $waittime -lt $max_drive_wait ]; do
1100                 result=`Run $MT $MTF $tape rewind 2>&1`
1101                 if [ $? -eq 0 ]; then
1102                         ready=1
1103                         break
1104                 fi
1105                 sleep $poll_drive_ready
1106                 waittime=`expr $waittime + $poll_drive_ready`
1107         done
1108         if [ $ready -eq 0 ]; then
1109                 Exit 2 "$loadslot" "Drive not ready after" \
1110                                    "$max_drive_wait seconds," \
1111                                    "rewind said \"$result\""
1112                 return $?                       # in case we are internal
1113         fi
1114
1115         if [ $loadslot -ne $cleanslot ]; then
1116                 rm -f $slotfile
1117                 echo $loadslot > $slotfile
1118         fi
1119         Exit 0 "$loadslot" "$tape"
1120         return $?                               # in case we are internal
1121 }
1122
1123 ###
1124 # Return information about how the changer is configured and the current
1125 # state of the robot.
1126 ###
1127
1128 info() {
1129         test -n "$DEBUG" && set -x
1130         get_loaded_info
1131         get_slot_list
1132         Log       "INFO     -> first slot: $firstslot"
1133         LogAppend "         -> current slot: $currentslot"
1134         LogAppend "         -> loaded slot: $loadedslot"
1135         LogAppend "         -> last slot: $lastslot"
1136         LogAppend "         -> slot list: $slot_list"
1137         LogAppend "         -> can go backwards: 1"
1138         LogAppend "         -> havereader: $havereader"
1139
1140         ###
1141         # Check if a barcode reader is configured or not.  If so, it
1142         # passes the 4th item in the echo back to amtape signifying it
1143         # can search based on barcodes.
1144         ###
1145         reader=
1146         if [ $havereader -eq 1 ]; then
1147                 reader=1
1148         fi
1149
1150         if [ $currentslot -lt $firstslot -o $currentslot -gt $lastslot ]; then
1151                 currentslot=$firstslot          # what "current" will get
1152         fi
1153         numslots=`expr $lastslot - $firstslot + 1`
1154         Exit 0 "$currentslot" "$numslots 1 $reader"
1155         return $?                               # in case we are internal
1156 }
1157
1158 ###
1159 # Read the labelfile and scan for a particular entry.
1160 ###
1161
1162 read_labelfile() {
1163         labelfile_entry_found=0
1164         labelfile_label=
1165         labelfile_barcode=
1166
1167         lbl_search=$1
1168         bc_search=$2
1169
1170         line=0
1171         while read lbl bc junk; do
1172                 line=`expr $line + 1`
1173                 if [ -z "$lbl" -o -z "$bc" -o -n "$junk" ]; then
1174                         Log       "ERROR    -> Line $line malformed: $lbl $bc $junk"
1175                         LogAppend "         -> Remove $labelfile" \
1176                                                "and run" \
1177                                                "\"$sbindir/amtape $config update\""
1178                         Exit 2 \
1179                              "<none>" \
1180                              "Line $line malformed in $labelfile: $lbl $bc $junk"
1181                         return $?               # in case we are internal
1182                 fi
1183                 if [ $lbl = "$lbl_search" -o $bc = "$bc_search" ]; then
1184                         if [ $labelfile_entry_found -ne 0 ]; then
1185                                 Log       "ERROR    -> Duplicate entries: $labelfile line $line"
1186                                 LogAppend "         -> Remove $labelfile" \
1187                                                        "and run" \
1188                                                        "\"$sbindir/amtape $config update\""
1189                                 Exit 2 \
1190                                      "<none>" \
1191                                      "Duplicate entries: $labelfile line $line"
1192                                 return $?       # in case we are internal
1193                         fi
1194                         labelfile_entry_found=1
1195                         labelfile_label=$lbl
1196                         labelfile_barcode=$bc
1197                 fi
1198         done
1199 }
1200
1201 ###
1202 # Adds the label and barcode for the currently loaded tape to the
1203 # barcode file.  Return an error if the database is messed up.
1204 ###
1205
1206 addlabel() {
1207         test -n "$DEBUG" && set -x
1208         if [ $# -lt 1 ]; then
1209                 Exit 2 "<none>" "Missing -label argument"
1210                 return $?                       # in case we are internal
1211         fi
1212         tapelabel=$1
1213         if [ $havereader -eq 0 ]; then
1214                 Exit 2 "<none>" "Not configured with barcode reader"
1215                 return $?                       # in case we are internal
1216         fi
1217         get_loaded_info
1218         if [ $loadedslot -lt 0 ]; then
1219                 Exit 1 "<none>" "No tape currently loaded"
1220                 return $?                       # in case we are internal
1221         fi
1222         Log       "LABEL    -> Adding label \"$tapelabel\"" \
1223                                "with barcode \"$loadedbarcode\"" \
1224                                "for slot $loadedslot" \
1225                                "into $labelfile"
1226         read_labelfile "$tapelabel" "$loadedbarcode" < $labelfile
1227         if [ $labelfile_entry_found -ne 0 ]; then
1228                 lf_val=
1229                 if [ "$labelfile_barcode" != "$loadedbarcode" ]; then
1230                         lf_type=label
1231                         lf_val=$tapelabel
1232                         val_type=barcode
1233                         old_val=$labelfile_barcode
1234                         new_val=$loadedbarcode
1235                 elif [ "$labelfile_label" != "$tapelabel" ]; then
1236                         lf_type=barcode
1237                         lf_val=$loadedbarcode
1238                         val_type=label
1239                         old_val=$labelfile_label
1240                         new_val=$tapelabel
1241                 fi
1242                 if [ -n "$lf_val" ]; then
1243                         LogAppend "ERROR    -> !!! Label database corrupted !!!"
1244                         LogAppend "         -> \"$old_val\" conflicts with" \
1245                                                "new $val_type \"$new_val\"" \
1246                                                "for $lf_type \"$lf_val\""
1247                         LogAppend "         -> Remove $labelfile" \
1248                                                "and run" \
1249                                                "\"$sbindir/amtape $config update\""
1250                         Exit 2 \
1251                              "<none>" \
1252                              "$tapelabel: \"$old_val\" conflicts with" \
1253                              "new $val_type \"$new_val\"" \
1254                              "for $lf_type \"$lf_val\""
1255                         return $?               # in case we are internal
1256                 fi
1257                 LogAppend "         -> already synced"
1258         else
1259                 echo "$tapelabel $loadedbarcode" >> $labelfile
1260                 LogAppend "         -> appended $labelfile entry:" \
1261                                        "$tapelabel $loadedbarcode"
1262         fi
1263         Exit 0 "$loadedslot" "$tape"
1264         return $?                               # in case we are internal
1265 }
1266
1267 ###
1268 # Look for a label in the barcode file.  If found, locate the slot it's
1269 # in by looking for the barcode in the mtx output, then load that tape.
1270 ###
1271
1272 searchtape() {
1273         test -n "$DEBUG" && set -x
1274         if [ $# -lt 1 ]; then
1275                 Exit 2 "<none>" "Missing -search argument"
1276                 return $?                       # in case we are internal
1277         fi
1278         tapelabel=$1
1279         if [ $havereader -eq 0 ]; then
1280                 Exit 2 "<none>" "Not configured with barcode reader"
1281                 return $?                       # in case we are internal
1282         fi
1283         Log "SEARCH   -> Hunting for label \"$tapelabel\""
1284         read_labelfile "$tapelabel" "" < $labelfile
1285         if [ $labelfile_entry_found -eq 0 ]; then
1286                 LogAppend "         -> !!! label \"$tapelabel\" not found" \
1287                                        "in $labelfile !!!"
1288                 LogAppend "         -> Remove $labelfile" \
1289                                        "and run" \
1290                                        "\"$sbindir/amtape $config update\""
1291                 Exit 2 \
1292                      "<none>" \
1293                      "$tapelabel: label \"$tapelabel\" not found in $labelfile"
1294                 return $?                       # in case we are internal
1295         fi
1296         LogAppend "         -> barcode is \"$labelfile_barcode\""
1297         get_mtx_status
1298         foundslot=`sed -n '
1299 /VolumeTag *= *'$labelfile_barcode' *$/                 {
1300         s/.*Storage Element \([0-9][0-9]*\).*/\1/p
1301         q
1302 }
1303 ' < $mtx_status`
1304         LogAppend "         -> foundslot is $foundslot"
1305         if [ -z "$foundslot" ]; then
1306                 LogAppend "ERROR    -> !!! Could not find slot" \
1307                                        "for barcode \"$labelfile_barcode\"!!!"
1308                 LogAppend "         -> Remove $labelfile" \
1309                                        "and run" \
1310                                        "\"$sbindir/amtape $config update\""
1311                 Exit 2 \
1312                      "<none>" \
1313                      "barcode \"$labelfile_barcode\"" \
1314                      "not found in mtx status output"
1315                 return $?                       # in case we are internal
1316         fi
1317         # Call loadslot without doing it as an internal and let it finish
1318         # things up.
1319         loadslot $foundslot
1320         # NOTREACHED
1321         Exit 2 "<none>" "searchtape: should not get here"
1322         return $?                               # in case we are internal
1323 }
1324
1325 ###
1326 # Program invocation begins here
1327 ###
1328
1329 if [ $# -lt 1 ]; then
1330         Exit 2 "<none>" "Usage: $myname -command args"
1331 fi
1332 cmd=$1
1333 shift
1334 case "$cmd" in
1335 -slot)
1336         loadslot "$@"
1337         ;;
1338 -info)
1339         info "$@"
1340         ;;
1341 -reset)
1342         reset "$@"
1343         ;;
1344 -eject)
1345         eject "$@"
1346         ;;
1347 -label) 
1348         addlabel "$@"
1349         ;;
1350 -search)
1351         searchtape "$@"
1352         ;;
1353 -clean)
1354         loadslot clean
1355         ;;
1356 *)
1357         Exit 2 "<none>" "unknown option: $cmd"
1358         ;;
1359 esac
1360
1361 Exit 2 "<none>" "$myname: should not get here"