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