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