Imported Upstream version 2.5.1p3
[debian/amanda] / changer-src / chg-chio.pl.in
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 if ( "@USE_VERSION_SUFFIXES@" eq "yes" ) {
67     $SUF = "-@VERSION@";
68 } else {
69     $SUF = "";
70 }
71
72 chomp ($tapeDevice = `$sbindir/amgetconf$SUF tapedev 2>&1`);
73 die "tapedev not found in amanda.conf"
74         if !$tapeDevice or $tapeDevice eq "" or
75             $tapeDevice =~ m/no such parameter/;
76 chomp ($changerDevice = `$sbindir/amgetconf$SUF changerdev 2>&1`);
77 chomp $changerDevice;
78 die "changerdev not found in amanda.conf"
79         if !$changerDevice or $changerDevice eq "" or
80             $changerDevice =~ m/no such parameter/;
81
82 #
83 # Initialise a few global variables
84 #
85
86 @slots = ();
87 @drives = ();
88 $max_slot = 0;
89 $max_drive = 0;
90 $nr_tapes = 0;
91
92 @dow = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
93 @moy = ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
94         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
95
96 sub do_time {
97         my (@t);
98         my ($r);
99
100         ###
101         # Get the local time for the value.
102         ###
103
104         @t = localtime (time ());
105
106         ###
107         # Return the result.
108         ###
109
110         $r = sprintf "%s %s %2d %2d:%02d:%02d %4d",
111           $dow[$t[6]],
112           $moy[$t[4]],
113           $t[3],
114           $t[2], $t[1], $t[0],
115           1900 + $t[5];
116
117         return $r;
118 }
119
120 sub getCurrentTape {
121         print LOG &do_time(), ": enter: getCurrentTape\n";
122
123         #
124         # Determines the slot number for the tape that is currently in the
125         # drive. getTapeParams and getTapeStatus should have been called.
126         # If there is no tape in the drive, no current tape, 0 is returned.
127         #
128
129         my($slot, $i);
130
131         if ( !$drives[0] ) {            # drive empty
132                 $i = 0;
133         } elsif ( $nr_tapes == 1 ) {    # one tape -> comes from slot max_slot
134                 $i = $max_slot;
135         } else {                        # find first empty slot
136                 $i = 0;
137                 while ( $i < $#slots and $slots[$i] ) {
138                         $i++
139                 }
140                 $i++;
141         }
142
143         print LOG &do_time(), ": leave: getCurrentTape: $i\n";
144         return $i;
145 }
146
147 sub getTapeStatus {
148         print LOG &do_time(), ": enter: getTapeStatus\n";
149
150         #
151         # Sets $nr_tapes, @slots, @drives, $current_tape
152         #
153
154         my($type,$num,$status);
155
156         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice status\n";
157         if ( !(open(FH,"@CHIO@ -f $changerDevice status|")) ) {
158                 print "$progname: '@CHIO@ -f $changerDevice status' failed, $!\n";
159                 exit(2);
160         }
161
162         #
163         # This routine requires the format of the output of 'chio status' to 
164         # be as follows:
165         #   picker 0: 
166         #   slot 0: <ACCESS>
167         #   slot 1: <ACCESS,FULL>
168         #   slot 2: <ACCESS,FULL>
169         #   (etc.)
170         #   drive 0: <ACCESS,FULL>
171
172
173         @slots=();
174         @drives=();
175
176         while( defined ($line = <FH>) ) {
177                 chomp( $line );
178                 print LOG &do_time(), ": $line\n";
179                 next unless $line =~ m/(\w+)\s+(\d+):\s*<([^>]+)>/;
180                 ($type,$num,$status) = ($1,$2,$3);
181                 if ( $type =~ m/slot/i ) {
182                         $slots[$num] = ( $status =~ m/full/i ) ? 1 : 0;
183                         if ($slots[ $num ]) { $nr_tapes++ }
184                 } elsif ( $type =~ m/drive/i ) {
185                         $drives[$num] = 0;
186                         if (  $status =~ m/full/i ) {
187                                 $drives[$num] = 1;
188                                 $nr_tapes++;
189                         }
190                 } else {
191                         # ignore 'picker', empty ones, etc...
192                 }
193         }
194         close(FH);
195
196         if ( $nr_tapes == 0 ) {
197                 print "$progname: No tapes in changer!\n";
198                 exit(2);
199         }
200
201         $currentTape = &getCurrentTape(); 
202
203         print LOG &do_time(), ": leave: getTapeStatus: $nr_tapes\n";
204         return($nr_tapes);
205 }
206
207 sub getTapeParams {
208         print LOG &do_time(), ": enter: getTapeParams\n";
209   
210         #
211         # Requests information on the number of slots, pickers and drives
212         # from the changer.
213         #
214
215         my($max_slot,$max_drive,$max_picker);
216   
217         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice params\n";
218         if ( !open(FH,"@CHIO@ -f $changerDevice params|") ) {
219                 print "$progname: '@CHIO@ -f $changerDevice params' failed, $!\n";
220                 exit(2);
221         }
222   
223         #
224         # the format of the output of 'chio params' should be
225         #  /dev/ch0: 8 slots, 1 drive, 1 picker
226         #  /dev/ch0: current picker: 0
227         #
228
229         $max_slot = 0;
230         $max_picker = -1;
231         $max_drive = 0;
232
233         while( defined ($line = <FH>) ) {
234                 chomp $line;
235                 print LOG &do_time(), ": $line\n";
236                 $max_slot       = $1 if $line =~ m/(\d+) slot/i;
237                 $max_drive      = $1 if $line =~ m/(\d+) drive/i;
238                 $max_picker     = $1 if $line =~ m/(\d+) picker/i;
239
240         }
241         close(FH);
242         if ( $max_drive == 0 or $max_picker == -1 ) {
243                 print "$progname: No drive or picker ? ($max_drive/$max_picker)\n";
244                 exit(2);
245         }
246
247         print LOG &do_time(), ": leave: getTapeParams: $max_slot, $max_drive, $max_picker\n";
248         return ($max_slot, $max_drive, $max_picker);
249 }
250
251 sub testTape {
252         my($tape) = @_;
253
254         #
255         # Check a few parameters to avoid the most serious problems
256         #
257
258         return
259                 if $currentTape == $tape;
260
261         if( $slots[$tape-1] == 0 ) {
262                 print "<none> $progname: no tape in slot requested\n";
263                 exit(1);
264         }
265         if( $tape > $max_slot ) {
266                 print $tape," $progname: requested a tape > $max_slot\n";
267                 exit(2);
268         }
269         if( $tape < 1 ) {
270                 print $tape," $progname: requested a tape < 1\n";
271                 exit(2);
272         }
273         return;
274 }
275
276 sub Load {
277         my($tape) = @_;
278         print LOG &do_time(), ": enter: Load: $tape\n";
279
280         #
281         # Load tape $tape into drive 0
282         #
283
284         print LOG &do_time(), ": running: @CHIO@ -f $changerDevice move slot ", $tape - 1, " drive 0\n";
285         if ( system("@CHIO@ -f $changerDevice move slot ".($tape-1)." drive 0") ) {
286                 print "$progname: cannot '@CHIO@ -f $changerDevice move' tape $tape into drive 0\n";
287                 exit(2);
288         }
289         print LOG &do_time(), ": leave: Load\n";
290 }
291
292 sub Unload {
293         my($tape) = @_;
294         print LOG &do_time(), ": enter: Unload: $tape\n";
295
296         #
297         # Unload the tape from drive 0 and put it into the slot specified by
298         # $tape.
299         #
300
301         #
302         # Ecrix AutoPAK devices (based on the Spectra Logics 215 changer)
303         # can lock up if you try to move a tape from a drive to an open slot
304         # without first rewinding and ejecting the tape.  This appears to
305         # occur when the operation times out and the ch driver sends a device
306         # or bus reset. Ecrix claims this is about to be fixed with a new
307         # firmware rev but for now it's safest to just explicitly eject
308         # the tape before moving the cartridge.
309         #
310         if ( system ("@MT@ @MT_FILE_FLAG@ $tapeDevice offline") ) {
311                 print "$progname: Warning, failed to eject the tape with '@MT@ @MT_FILE_FLAG@ $tapeDevice offline'\n";
312                 # NB: not fatal; let chio try it's thing
313         }
314
315         if ( system("@CHIO@ -f $changerDevice move drive 0 slot ".($tape-1)." ") ) {
316                 print "$progname: cannot '@CHIO@ -f $changerDevice move' tape $tape from drive 0\n";
317                 exit(2);
318         }
319         print LOG &do_time(), ": leave: Unload\n";
320 }
321
322 sub changeTape {
323         my($tape) = @_;
324         print LOG &do_time(), ": enter: changeTape: $tape\n";
325
326         #
327         # Unload current tape and load a new tape from slot $tape.
328         #
329
330         if ($tape != $currentTape) {
331
332                 &testTape($tape);
333
334                 if( $currentTape != 0 ) {
335                         &Unload($currentTape);
336                 }
337                 &Load($tape);
338                 $currentTape = $tape;
339         }
340         print LOG &do_time(), ": leave: changeTape\n";
341 }
342
343
344 #
345 # Main program
346 #
347
348 #
349 # Initialise
350 #
351
352 ($max_slot, $max_drive) = &getTapeParams();
353
354 $opt_slot = 0;                                  # perl -w fodder
355 $opt_info = 0;                                  # perl -w fodder
356 $opt_reset = 0;                                 # perl -w fodder
357 $opt_eject = 0;                                 # perl -w fodder
358
359 GetOptions("slot=s", "info", "reset", "eject"); 
360
361 $nr_tapes = &getTapeStatus();
362
363 #
364 # Before we do anything with the tape changer we'll have to rewind the tape
365 #
366
367 if (-x "$sbindir/ammt$SUF") {
368         $MT="$sbindir/ammt$SUF";
369         $MTF="-f";
370 } elsif (-x "@MT@") {
371         $MT="@MT@";
372         $MTF="@MT_FILE_FLAG@";
373 } else {
374         print LOG &do_time(), ": mt program not found\n";
375         print "<none> mt program not found\n";
376         exit(1);
377 }
378 print LOG &do_time(), "MT -> $MT $MTF\n";
379
380 system ("$MT $MTF $tapeDevice rewind")
381         unless $currentTape == 0;
382
383
384 if ( $opt_slot ) {
385         if ( $opt_slot =~ /first/ ) {
386                 &changeTape(1);
387                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
388                 print "$currentTape $tapeDevice\n";
389         }
390         if ( $opt_slot =~ /last/ ) {
391                 &changeTape($max_slot);
392                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
393                 print "$currentTape $tapeDevice\n";
394         }
395         if ( $opt_slot =~ /current/ ) {
396                 &changeTape($currentTape);
397                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
398                 print "$currentTape $tapeDevice\n";
399         }
400         if ( $opt_slot =~ /next/ ) {
401                 $tape = $currentTape+1;
402                 if ( $tape > $max_slot ) {
403                         $tape = 1;
404                 }
405                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1 
406                         if ( ++$tape > $max_slot ) {
407                                 $tape = 1;
408                         }
409                 }
410                 &changeTape($tape);
411                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
412                 print "$currentTape $tapeDevice\n";
413         }
414         if ( $opt_slot =~ /prev/ ) {
415                 $tape = $currentTape-1;
416                 if ( $tape < 1 ) {
417                         $tape = $max_slot;
418                 }
419                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1
420                         if ( --$tape < 1 ) {
421                                 $tape = $max_slot;
422                         }
423                 }
424                 &changeTape($tape);
425                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
426                 print "$currentTape $tapeDevice\n";
427         }
428         if ( $opt_slot =~ /^\d+$/ ) {
429                 &changeTape($opt_slot);
430                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
431                 print "$currentTape $tapeDevice\n";
432         }
433         if ( $opt_slot =~ /advance/ ) {
434                 $tape=$currentTape+1;
435                 if ( $tape > $max_slot ) {
436                         $tape = 1;
437                 }
438                 if ( $currentTape ) { 
439                         &Unload($currentTape);
440                 }
441                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
442                 print "$currentTape , /dev/null\n";
443         }
444
445         exit 0;
446 }
447
448 if ( $opt_info ) {
449         if ( $currentTape == 0 ) {
450                 &Load(1);                       # load random tape
451                 $currentTape = 1;
452         }
453
454         print LOG &do_time(), ": $currentTape $max_slot 1\n";
455         print "$currentTape $max_slot 1\n";
456         exit 0;
457 }
458
459 if ( $opt_reset ) {
460         &changeTape(1);
461         print LOG &do_time(), ": $currentTape $tapeDevice\n";
462         print "$currentTape $tapeDevice\n";
463         exit 0;
464 }
465
466 if ( $opt_eject ) {
467         if ( $currentTape ) { 
468                 &Unload($currentTape);
469                 print "0 $tapeDevice\n";
470                 exit 0;
471         } else {
472                 print "$progname: drive was not loaded\n";
473                 exit 1;
474         }
475 }
476
477 print "$progname: No command was received.  Exiting.\n";
478 exit 1;