Imported Upstream version 3.1.0
[debian/amanda] / changer-src / chg-chio.pl
1 #! @PERL@ -w
2
3 # Catch for sh/csh on systems without #! ability.
4 eval '(exit $?0)' && eval 'exec @PERL@ -S $0 ${1+"$@"}'
5         & eval 'exec @PERL@ -S $0 $argv:q'
6                 if 0;
7
8
9 # This changer script controls tape libraries on operating systems that have a
10 # chgio program
11 #       DSL 7000 on FreeBSD is an example
12 #
13 # The changer being used is a n tape juke, that can be used with 1, n-1 or n
14 # tapes in the juke. The special slot is slot n. The script does not
15 # make assumptions about the number of slots, except that the special slot
16 # is the highest number. The slot is special in the sense that it contains the
17 # the only tape if the juke contains 1 tape and contains no tape if the juke
18 # contains n-1 tapes. See getCurrentTape.
19 #
20 # Furthermore, the script uses drive 0 and assumes that the device is able to
21 # figure itself how to move a type from slot m to drive 0 if asked to do so and
22 # multiple pickers are present.
23 #
24 # The numbering of the slots is by the way from 1 to n with slots. The chio
25 # program returns the slot numbers numbered from 0 to n-1 however.
26
27 # This script is built up out of bits and pieces of the other scripts
28 # and no credits are claimed. Most notably the chg-rth.pl script was used. That
29 # script was written by Erik Frederick, <edf@tyrell.mc.duke.edu>.
30
31 # Permission to freely use and distribute is granted (by me and was granted by
32 # the original authors).
33 #
34 # Nick Hibma - nick.hibma@jrc.it
35 #
36
37 require 5.001;
38
39 ($progname = $0) =~ s#/.*/##;
40
41 use English;
42 use Getopt::Long;
43
44 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV', 'PATH'};
45 $ENV{'PATH'} = "/usr/bin:/usr/sbin:/sbin:/bin";
46
47 $| = 1;
48
49 if (-d "@AMANDA_DBGDIR@") {
50         $logfile = "@AMANDA_DBGDIR@/changer.debug";
51 } else {
52         $logfile = "/dev/null";
53 }
54 die "$progname: cannot open $logfile: $ERRNO\n"
55         unless (open (LOG, ">> $logfile"));
56
57 #
58 # get the information from the configuration file
59 #
60
61 $prefix="@prefix@";
62 $prefix=$prefix;                # avoid warnings about possible typo
63 $exec_prefix="@exec_prefix@";
64 $exec_prefix=$exec_prefix;      # Ditto
65 $sbindir="@sbindir@";
66 chomp ($tapeDevice = `$sbindir/amgetconf tapedev 2>&1`);
67 die "tapedev not found in amanda.conf"
68         if !$tapeDevice or $tapeDevice eq "" or
69             $tapeDevice =~ m/no such parameter/;
70 chomp ($changerDevice = `$sbindir/amgetconf changerdev 2>&1`);
71 chomp $changerDevice;
72 die "changerdev not found in amanda.conf"
73         if !$changerDevice or $changerDevice eq "" or
74             $changerDevice =~ m/no such parameter/;
75
76 #
77 # Initialise a few global variables
78 #
79
80 @slots = ();
81 @drives = ();
82 $max_slot = 0;
83 $max_drive = 0;
84 $nr_tapes = 0;
85
86 @dow = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
87 @moy = ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
88         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
89
90 sub do_time {
91         my (@t);
92         my ($r);
93
94         ###
95         # Get the local time for the value.
96         ###
97
98         @t = localtime (time ());
99
100         ###
101         # Return the result.
102         ###
103
104         $r = sprintf "%s %s %2d %2d:%02d:%02d %4d",
105           $dow[$t[6]],
106           $moy[$t[4]],
107           $t[3],
108           $t[2], $t[1], $t[0],
109           1900 + $t[5];
110
111         return $r;
112 }
113
114 sub getCurrentTape {
115         print LOG &do_time(), ": enter: getCurrentTape\n";
116
117         #
118         # Determines the slot number for the tape that is currently in the
119         # drive. getTapeParams and getTapeStatus should have been called.
120         # If there is no tape in the drive, no current tape, 0 is returned.
121         #
122
123         my($slot, $i);
124
125         if ( !$drives[0] ) {            # drive empty
126                 $i = 0;
127         } elsif ( $nr_tapes == 1 ) {    # one tape -> comes from slot max_slot
128                 $i = $max_slot;
129         } else {                        # find first empty slot
130                 $i = 0;
131                 while ( $i < $#slots and $slots[$i] ) {
132                         $i++
133                 }
134                 $i++;
135         }
136
137         print LOG &do_time(), ": leave: getCurrentTape: $i\n";
138         return $i;
139 }
140
141 sub getTapeStatus {
142         print LOG &do_time(), ": enter: getTapeStatus\n";
143
144         #
145         # Sets $nr_tapes, @slots, @drives, $current_tape
146         #
147
148         my($type,$num,$status);
149
150         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice status\n";
151         if ( !(open(FH,"@CHIO@ -f $changerDevice status|")) ) {
152                 print "$progname: '@CHIO@ -f $changerDevice status' failed, $!\n";
153                 exit(2);
154         }
155
156         #
157         # This routine requires the format of the output of 'chio status' to 
158         # be as follows:
159         #   picker 0: 
160         #   slot 0: <ACCESS>
161         #   slot 1: <ACCESS,FULL>
162         #   slot 2: <ACCESS,FULL>
163         #   (etc.)
164         #   drive 0: <ACCESS,FULL>
165
166
167         @slots=();
168         @drives=();
169
170         while( defined ($line = <FH>) ) {
171                 chomp( $line );
172                 print LOG &do_time(), ": $line\n";
173                 next unless $line =~ m/(\w+)\s+(\d+):\s*<([^>]+)>/;
174                 ($type,$num,$status) = ($1,$2,$3);
175                 if ( $type =~ m/slot/i ) {
176                         $slots[$num] = ( $status =~ m/full/i ) ? 1 : 0;
177                         if ($slots[ $num ]) { $nr_tapes++ }
178                 } elsif ( $type =~ m/drive/i ) {
179                         $drives[$num] = 0;
180                         if (  $status =~ m/full/i ) {
181                                 $drives[$num] = 1;
182                                 $nr_tapes++;
183                         }
184                 } else {
185                         # ignore 'picker', empty ones, etc...
186                 }
187         }
188         close(FH);
189
190         if ( $nr_tapes == 0 ) {
191                 print "$progname: No tapes in changer!\n";
192                 exit(2);
193         }
194
195         $currentTape = &getCurrentTape(); 
196
197         print LOG &do_time(), ": leave: getTapeStatus: $nr_tapes\n";
198         return($nr_tapes);
199 }
200
201 sub getTapeParams {
202         print LOG &do_time(), ": enter: getTapeParams\n";
203   
204         #
205         # Requests information on the number of slots, pickers and drives
206         # from the changer.
207         #
208
209         my($max_slot,$max_drive,$max_picker);
210   
211         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice params\n";
212         if ( !open(FH,"@CHIO@ -f $changerDevice params|") ) {
213                 print "$progname: '@CHIO@ -f $changerDevice params' failed, $!\n";
214                 exit(2);
215         }
216   
217         #
218         # the format of the output of 'chio params' should be
219         #  /dev/ch0: 8 slots, 1 drive, 1 picker
220         #  /dev/ch0: current picker: 0
221         #
222
223         $max_slot = 0;
224         $max_picker = -1;
225         $max_drive = 0;
226
227         while( defined ($line = <FH>) ) {
228                 chomp $line;
229                 print LOG &do_time(), ": $line\n";
230                 $max_slot       = $1 if $line =~ m/(\d+) slot/i;
231                 $max_drive      = $1 if $line =~ m/(\d+) drive/i;
232                 $max_picker     = $1 if $line =~ m/(\d+) picker/i;
233
234         }
235         close(FH);
236         if ( $max_drive == 0 or $max_picker == -1 ) {
237                 print "$progname: No drive or picker ? ($max_drive/$max_picker)\n";
238                 exit(2);
239         }
240
241         print LOG &do_time(), ": leave: getTapeParams: $max_slot, $max_drive, $max_picker\n";
242         return ($max_slot, $max_drive, $max_picker);
243 }
244
245 sub testTape {
246         my($tape) = @_;
247
248         #
249         # Check a few parameters to avoid the most serious problems
250         #
251
252         return
253                 if $currentTape == $tape;
254
255         if( $slots[$tape-1] == 0 ) {
256                 print "<none> $progname: no tape in slot requested\n";
257                 exit(1);
258         }
259         if( $tape > $max_slot ) {
260                 print $tape," $progname: requested a tape > $max_slot\n";
261                 exit(2);
262         }
263         if( $tape < 1 ) {
264                 print $tape," $progname: requested a tape < 1\n";
265                 exit(2);
266         }
267         return;
268 }
269
270 sub Load {
271         my($tape) = @_;
272         print LOG &do_time(), ": enter: Load: $tape\n";
273
274         #
275         # Load tape $tape into drive 0
276         #
277
278         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice move slot ", $tape - 1, " drive 0\n";
279         if ( system("@CHIO@ -f $changerDevice move slot ".($tape-1)." drive 0") ) {
280                 print "$progname: cannot '@CHIO@ -f $changerDevice move' tape $tape into drive 0\n";
281                 exit(2);
282         }
283
284         # wait for tape to load
285         $count = 1800;
286         while ( $count > 0 &&
287                 system("$MT $MTF $tapeDevice status > /dev/null 2>&1" ) ) {
288                 print LOG &do_time(), ": waiting for tape to load\n";
289                 sleep 30;
290                 $count -= 30;
291         }
292
293         print LOG &do_time(), ": leave: Load\n";
294 }
295
296 sub Unload {
297         my($tape) = @_;
298         print LOG &do_time(), ": enter: Unload: $tape\n";
299
300         #
301         # Unload the tape from drive 0 and put it into the slot specified by
302         # $tape.
303         #
304
305         #
306         # Ecrix AutoPAK devices (based on the Spectra Logics 215 changer)
307         # can lock up if you try to move a tape from a drive to an open slot
308         # without first rewinding and ejecting the tape.  This appears to
309         # occur when the operation times out and the ch driver sends a device
310         # or bus reset. Ecrix claims this is about to be fixed with a new
311         # firmware rev but for now it's safest to just explicitly eject
312         # the tape before moving the cartridge.
313         #
314         if ( system ("$MT $MTF $tapeDevice offline") ) {
315                 print "$progname: Warning, failed to eject the tape with '$MT $MTF $tapeDevice offline'\n";
316                 # NB: not fatal; let chio try it's thing
317         }
318
319         if ( system("@CHIO@ -f $changerDevice move drive 0 slot ".($tape-1)." ") ) {
320                 print "$progname: cannot '@CHIO@ -f $changerDevice move' tape $tape from drive 0\n";
321                 exit(2);
322         }
323         print LOG &do_time(), ": leave: Unload\n";
324 }
325
326 sub changeTape {
327         my($tape) = @_;
328         print LOG &do_time(), ": enter: changeTape: $tape\n";
329
330         #
331         # Unload current tape and load a new tape from slot $tape.
332         #
333
334         if ($tape != $currentTape) {
335
336                 &testTape($tape);
337
338                 if( $currentTape != 0 ) {
339                         &Unload($currentTape);
340                 }
341                 &Load($tape);
342                 $currentTape = $tape;
343         }
344         print LOG &do_time(), ": leave: changeTape\n";
345 }
346
347
348 #
349 # Main program
350 #
351
352 #
353 # Initialise
354 #
355
356 ($max_slot, $max_drive) = &getTapeParams();
357
358 $opt_slot = 0;                                  # perl -w fodder
359 $opt_info = 0;                                  # perl -w fodder
360 $opt_reset = 0;                                 # perl -w fodder
361 $opt_eject = 0;                                 # perl -w fodder
362
363 GetOptions("slot=s", "info", "reset", "eject"); 
364
365 $nr_tapes = &getTapeStatus();
366
367 #
368 # Before we do anything with the tape changer we'll have to rewind the tape
369 #
370
371 if (-x "$sbindir/ammt") {
372         $MT="$sbindir/ammt";
373         $MTF="-f";
374 } elsif (-x "@MT@") {
375         $MT="@MT@";
376         $MTF="@MT_FILE_FLAG@";
377 } else {
378         print LOG &do_time(), ": mt program not found\n";
379         print "<none> mt program not found\n";
380         exit(1);
381 }
382 print LOG &do_time(), ": MT -> $MT $MTF\n";
383
384 system ("$MT $MTF $tapeDevice rewind")
385         unless $currentTape == 0;
386
387
388 if ( $opt_slot ) {
389         if ( $opt_slot =~ /first/ ) {
390                 &changeTape(1);
391                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
392                 print "$currentTape $tapeDevice\n";
393         }
394         if ( $opt_slot =~ /last/ ) {
395                 &changeTape($max_slot);
396                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
397                 print "$currentTape $tapeDevice\n";
398         }
399         if ( $opt_slot =~ /current/ ) {
400                 &changeTape($currentTape);
401                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
402                 print "$currentTape $tapeDevice\n";
403         }
404         if ( $opt_slot =~ /next/ ) {
405                 $tape = $currentTape+1;
406                 if ( $tape > $max_slot ) {
407                         $tape = 1;
408                 }
409                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1 
410                         if ( ++$tape > $max_slot ) {
411                                 $tape = 1;
412                         }
413                 }
414                 &changeTape($tape);
415                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
416                 print "$currentTape $tapeDevice\n";
417         }
418         if ( $opt_slot =~ /prev/ ) {
419                 $tape = $currentTape-1;
420                 if ( $tape < 1 ) {
421                         $tape = $max_slot;
422                 }
423                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1
424                         if ( --$tape < 1 ) {
425                                 $tape = $max_slot;
426                         }
427                 }
428                 &changeTape($tape);
429                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
430                 print "$currentTape $tapeDevice\n";
431         }
432         if ( $opt_slot =~ /^\d+$/ ) {
433                 &changeTape($opt_slot);
434                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
435                 print "$currentTape $tapeDevice\n";
436         }
437         if ( $opt_slot =~ /advance/ ) {
438                 $tape=$currentTape+1;
439                 if ( $tape > $max_slot ) {
440                         $tape = 1;
441                 }
442                 if ( $currentTape ) { 
443                         &Unload($currentTape);
444                 }
445                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
446                 print "$currentTape , /dev/null\n";
447         }
448
449         exit 0;
450 }
451
452 if ( $opt_info ) {
453         if ( $currentTape == 0 ) {
454                 &Load(1);                       # load random tape
455                 $currentTape = 1;
456         }
457
458         print LOG &do_time(), ": $currentTape $max_slot 1\n";
459         print "$currentTape $max_slot 1\n";
460         exit 0;
461 }
462
463 if ( $opt_reset ) {
464         &changeTape(1);
465         print LOG &do_time(), ": $currentTape $tapeDevice\n";
466         print "$currentTape $tapeDevice\n";
467         exit 0;
468 }
469
470 if ( $opt_eject ) {
471         if ( $currentTape ) { 
472                 &Unload($currentTape);
473                 print "0 $tapeDevice\n";
474                 exit 0;
475         } else {
476                 print "$progname: drive was not loaded\n";
477                 exit 1;
478         }
479 }
480
481 print "$progname: No command was received.  Exiting.\n";
482 exit 1;