improve bash completions and related delivery mechanism
[debian/mtx] / contrib / mtx-changer
1 #! /bin/sh
2 ###############################################################################
3 # AMANDA Tape Changer script for use with the MTX tape changer program
4 # Version 1.0 - Tue Feb 20 13:59:39 CST 2001
5
6 # Based on 'stc-changer' by Eric Berggren (eric@ee.pdx.edu)
7 # Updated by Tim Skirvin (tskirvin@ks.uiuc.edu)
8
9 # Given that there's no license...let's make this the Perl Artistic License.
10 # Just make sure you give me and Eric credit if you modify this.  
11 ###############################################################################
12
13 ### USER CONFIGURATION
14 # Name of the tape drive (takes place of "tapedev" option in amanda.conf)
15 #  and default driver number in library (usu 0) that DRIVE_NAME points to
16 DRIVE_NAME="/dev/rmt/0n"
17 DRIVE_NUM=0
18
19 # Location of "STC" command and control device
20 MTX_CMD="/usr/local/sbin/mtx";
21 MTX_CONTROL="/dev/scsi/changer/c4t1d0";
22
23 # Whether tape drive must eject tape before changer retrieves
24 #  (ie, EXB-2x0). Usually okay if set while not necessary, bad if
25 #  required but not set.
26 DRIVE_MUST_EJECT=1
27
28 # How long to check drive readiness (in seconds) after mounting (or
29 #  ejecting) a volume (on some libraries, the motion or eject command may
30 #  complete before the drive has the volume fully mounted and online,
31 #  or ready for retrieval, resulting in "Drive not ready"/"Media not
32 #  ready" errors). Do an "mt status" command every 5 seconds upto this
33 #  time.
34 DRIVE_READY_TIME_MAX=120
35
36 #  tape "mt" command location...
37 MT_CMD="/usr/bin/mt"     # called via "MT_CMD -f DRIVE_NAME rewind" &
38                          #   "MT_CMD -f DRIVE_NAME offline" to eject
39                          # and "MT_CMD -f DRIVE_NAME status" to get ready info
40
41 ##############################################################################
42 #
43 NumDrives=-1
44 NumSlots=-1
45 LastSlot=-1
46 LoadedTape=-1
47
48 #
49 # Usage information
50 #
51 usage()
52 {
53     echo
54     echo "Usage: $Progname <command> [arg...]"
55     echo "  -info          reports capability and loaded tape"
56     echo "  -slot <slot>   loads specified tape into drive"
57     echo "        current  reports current mounted tape"
58     echo "        next     loads logically next tape (loops to top)"
59     echo "        prev     loads logically previous tape (loops to bot)"
60     echo "        first    loads first tape"
61     echo "        last     loads last tape"
62     echo "        0..99    loads tape from specified slot#"
63     echo "  -eject         uloads current mounted tape"
64     echo "  -reset         resets changer (and drive); loads first tape"
65     echo
66     exit 5
67 }
68
69 #
70 # Perform "stc" changer command (& handle the "fatal" errors)
71 #  else, set 'CommandResStr' and 'CommandRawResStr' to the result string 
72 #  and 'CommandResCode' to the exit code
73 #
74 dotapecmd()
75 {
76     cmd=$1
77     arg=$2
78
79     CommandResStr=`$MTX_CMD $MTX_CONTROL $cmd $arg 2>&1`
80     CommandRawResStr=$CommandResStr
81     CommandResCode=$?
82
83     CommandResStr=`echo $CommandResStr | head -1 | sed 's/^[^:]*: //'`
84     if [ $CommandResCode -gt 1 ]; then
85         echo "0 $Progname: returned $CommandResStr"
86         exit 2
87     fi
88 }
89
90 #
91 # Unload tape from drive (a drive command; "ejecttape" is a changer command
92 #  to actually retrieve the tape). Needed by some changers (controlled by
93 #  setting "DRIVE_MUST_EJECT")
94 #
95 ejectdrive()
96 {
97     # Tell drive to eject tape before changer retrieves; req'd by some
98     #  drives (ie, EXB-2x0). Not needed by QDLT-4x00. Do a "rewind"
99     #  command first, then "offline" to eject (instead of "rewoffl")
100     #
101     if [ "$DRIVE_MUST_EJECT" -ne 0 ]; then
102         mtresstr=`$MT_CMD -f $DRIVE_NAME rewind 2>&1`
103         mtrescode=$?
104
105         if [ $mtrescode -ne 0 ]; then
106             if echo "$mtresstr" | egrep -s 'no tape'; then
107                 :;   # no tape mounted; assume okay...
108             else
109                 # can't eject tape, bad; output: <tape#> reason
110                 echo "0 $mtresstr"
111                 exit 1
112             fi
113         else
114             mtresstr=`$MT_CMD -f $DRIVE_NAME offline 2>&1`
115             mtrescode=$?
116
117             checkdrive 1
118         fi
119     fi
120 }
121
122 #
123 # Check drive readiness after (un)mounting a volume (which may take a while
124 #  after the volume change command completes)
125 #
126 checkdrive()
127 {
128     unmounting=$1
129
130     if [ "$DRIVE_READY_TIME_MAX" -gt 0 ]; then
131
132         # sleep time between checks
133         pausetime=5
134
135         # number of interations to check
136         numchecks=`expr $DRIVE_READY_TIME_MAX / $pausetime`
137         if [ "$numchecks" -eq 0 ]; then
138             numchecks=1
139         fi
140
141         # check until success, or out of attempts...
142         while [ "$numchecks" -gt 0 ]; do
143             mtresstr=`$MT_CMD -f $DRIVE_NAME status 2>&1`
144             mtrescode=$?
145
146             if [ $mtrescode -eq 0 ]; then
147                 # Success ?
148                 return 0
149             else
150                 # pause, before trying again....
151                 if [ "$numchecks" -gt 1 ]; then
152                     sleep $pausetime
153
154                     # if unmounting a volume, check for 'mt' command
155                     #  failure; (sleep first for additional comfort)
156                     if [ "$unmounting" -ne 0 ]; then
157                         return 0
158                     fi
159                 fi
160             fi
161             numchecks=`expr $numchecks - 1`
162         done
163
164         # failed; output: -1 reason
165         echo "-1 drive won't report ready"
166         exit 1
167     fi
168 }
169
170 #
171 # Get changer parameters
172 #
173 getchangerparms()
174 {
175     dotapecmd status
176     if [ $CommandResCode -eq 0 ] && \
177         echo "$CommandResStr" | egrep -s '^Storage Changer'; then
178
179         NumDrives=`echo $dspec | wc -l`
180         NumDrives=`echo "$CommandRawResStr" | \
181                         grep 'Data Transfer Element' | wc -l`
182         if [ "$NumDrives" -le "$DRIVE_NUM" ]; then
183             echo "$Program: Invalid drive # specified ($DRIVE_NUM > $NumDrives)"
184             exit 3
185         fi
186                         # grep 'Data Transfer Element $DRIVE_NUM' | \
187         LoadedTape=`echo "$CommandRawResStr" | \
188                         grep 'Data Transfer Element' | \
189                         grep 'Storage Element [0-9]' | \
190                         awk '{ print $7 }'      `
191         if [ -z "$LoadedTape" -o "$LoadedTape" = "e" ]; then
192             LoadedTape=-1
193         fi
194         NumSlots=`echo "$CommandRawResStr" | \
195                 grep 'Storage Element [0-9]\{1,\}:' | \
196                 grep -v 'Data Element' | \
197                 wc -l | sed -e 's/ //g' `
198         LastSlot=`expr $NumSlots - 1`
199     else
200         echo \
201           "$Progname: Can't get changer parameters; Result was $CommandResStr"
202         exit 3
203     fi
204 }
205     
206 #
207 # Display changer info
208 #
209 changerinfo()
210 {
211     getchangerparms
212
213     # output status string: currenttape numslots randomaccess?
214     echo "$LoadedTape $NumSlots 1"
215     exit 0
216 }
217
218 #
219 # Eject current mounted tape
220 #
221 ejecttape()
222 {
223     getchangerparms
224     ct=$LoadedTape
225
226     # If no tape reported mounted, assume success (could be bad if changer
227     #  lost track of tape)
228     #
229     if [ $ct -lt 0 ]; then
230         CommandResCode=0
231     else
232         ejectdrive
233         dotapecmd unload
234     fi
235
236     if [ $CommandResCode -ne 0 ]; then
237         # failed; output: <tape#> reason
238         echo "$ct $CommandResStr"
239         exit 1
240     else
241         # success; output: <tape#> drive
242         echo "$ct $DRIVE_NAME"
243         exit 0
244     fi
245 }
246
247 #
248 # Move specified tape into drive (operation level)
249 #
250 doloadtape()
251 {
252     slot=$1
253     if [ "$slot" -eq "$LoadedTape" ]; then
254         return 0
255     fi
256     ejectdrive
257     dotapecmd load $slot
258     return $CommandResCode
259 }
260
261 #
262 # Load next available tape into drive
263 #
264 loadnexttape()
265 {
266     curslot=$1
267     direction=$2
268
269     startslot=$curslot
270     while true; do
271         if doloadtape $curslot; then
272             return 0
273         else
274             if echo $CommandResStr | egrep -s 'Slot.*reported empty'; then
275
276                 if [ "$direction" -lt 0 ]; then
277                     curslot=`expr $curslot - 1`
278                     if [ "$curslot" -lt 0 ]; then
279                         curslot=$LastSlot
280                     fi
281                 else
282                     curslot=`expr $curslot + 1`
283                     if [ "$curslot" -gt "$LastSlot" ]; then
284                         curslot=0
285                     fi
286                 fi
287
288                 # Check if we're back to where we started...
289                 if [ "$curslot" = "$startslot" ]; then
290                     if [ "$direction" -lt 0 ]; then
291                         CommandResStr="No previous volume available"
292                     else
293                         CommandResStr="No subsequent volume available"
294                     fi
295                     return 1
296                 fi
297             else
298                 return 1
299             fi
300         fi
301     done
302 }
303
304 #
305 # Report loadtape() status
306 #
307 reportstatus()
308 {
309     if [ $CommandResCode -eq 0 ]; then
310         # success; output currenttape drivename
311         echo "$LoadedTape $DRIVE_NAME"
312         exit 0
313     else
314         # failed (empty slot?); output currenttape reason
315         echo "$LoadedTape $CommandResStr"
316         exit 1
317     fi
318 }
319
320
321 #
322 # Move specified tape into drive (command level)
323 #
324 loadtape()
325 {
326     slot=$1
327
328     getchangerparms
329
330     case "$slot" in
331         current)
332             if [ $LoadedTape -lt 0 ]; then
333                 CommandResStr="Can't determine current tape; drive empty ?"
334                 CommandResCode=1
335             fi
336             ;;
337         prev)
338             if [ $LoadedTape -le 0 ]; then
339                 loadnexttape $LastSlot -1
340             else
341                 loadnexttape `expr $LoadedTape - 1` -1
342             fi
343             ;;
344         next)
345             if [ $LoadedTape -ge $LastSlot -o $LoadedTape -lt 0 ]; then
346                 loadnexttape 0 1
347             else
348                 loadnexttape `expr $LoadedTape + 1` 1
349             fi
350             ;;
351         first)
352             loadnexttape 0 1
353             ;;
354         last)
355             loadnexttape $LastSlot -1
356             ;;
357         [0-9]*)
358             doloadtape $slot
359             ;;
360         *)
361             # error; no valid slot specified
362             echo "$Progname: No valid slot specified"
363             exit 1
364             ;;
365     esac
366
367     if [ $CommandResCode -eq 0 ]; then
368         getchangerparms
369         checkdrive
370     fi
371     reportstatus
372 }
373
374 #
375 # Reset changer to known state
376 #
377 resetchanger()
378 {
379     ejectdrive
380     dotapecmd reset
381     if [ $CommandResCode -ne 0 ]; then
382         # failed; output: failed? reason
383         echo "-1 $CommandResStr"
384         exit 2;
385     else
386         loadtape first
387     fi
388 }
389
390 #############################################################################
391 #
392 # MAIN
393 #
394 Progname=`basename $0`
395
396 if [ ! -x "$MTX_CMD" ]; then
397     echo "-1 $Progname: cannot run STC command ($MTX_CMD)"
398     exit 2
399 fi
400 if [ -n "$MTX_CONTROL" ]; then
401     if echo "$MTX_CONTROL" | egrep -s '^-f'; then
402         :;
403     else
404         MTX_CONTROL="-f $MTX_CONTROL"
405     fi
406 fi
407 if [ -n "$DRIVE_NUM" ]; then
408     DRIVE_NUM=0
409 fi
410
411 if [ $# -ge 1 ]; then command=$1; else command="-usage"; fi
412
413 case "$command" in
414     -info)
415         changerinfo
416         ;;
417     -slot)
418         loadtape $2
419         ;;
420     -eject)
421         ejecttape
422         ;;
423     -reset)
424         resetchanger
425         ;;
426     *)
427         usage
428         ;;
429 esac
430
431 exit 0