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