Imported Upstream version 2.5.2p1
[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
290         # wait for tape to load
291         $count = 1800;
292         while ( $count > 0 &&
293                 system("$MT $MTF $tapeDevice status > /dev/null 2>&1" ) ) {
294                 print LOG &do_time(), ": waiting for tape to load\n";
295                 sleep 30;
296                 $count -= 30;
297         }
298
299         print LOG &do_time(), ": leave: Load\n";
300 }
301
302 sub Unload {
303         my($tape) = @_;
304         print LOG &do_time(), ": enter: Unload: $tape\n";
305
306         #
307         # Unload the tape from drive 0 and put it into the slot specified by
308         # $tape.
309         #
310
311         #
312         # Ecrix AutoPAK devices (based on the Spectra Logics 215 changer)
313         # can lock up if you try to move a tape from a drive to an open slot
314         # without first rewinding and ejecting the tape.  This appears to
315         # occur when the operation times out and the ch driver sends a device
316         # or bus reset. Ecrix claims this is about to be fixed with a new
317         # firmware rev but for now it's safest to just explicitly eject
318         # the tape before moving the cartridge.
319         #
320         if ( system ("$MT $MTF $tapeDevice offline") ) {
321                 print "$progname: Warning, failed to eject the tape with '$MT $MTF $tapeDevice offline'\n";
322                 # NB: not fatal; let chio try it's thing
323         }
324
325         if ( system("@CHIO@ -f $changerDevice move drive 0 slot ".($tape-1)." ") ) {
326                 print "$progname: cannot '@CHIO@ -f $changerDevice move' tape $tape from drive 0\n";
327                 exit(2);
328         }
329         print LOG &do_time(), ": leave: Unload\n";
330 }
331
332 sub changeTape {
333         my($tape) = @_;
334         print LOG &do_time(), ": enter: changeTape: $tape\n";
335
336         #
337         # Unload current tape and load a new tape from slot $tape.
338         #
339
340         if ($tape != $currentTape) {
341
342                 &testTape($tape);
343
344                 if( $currentTape != 0 ) {
345                         &Unload($currentTape);
346                 }
347                 &Load($tape);
348                 $currentTape = $tape;
349         }
350         print LOG &do_time(), ": leave: changeTape\n";
351 }
352
353
354 #
355 # Main program
356 #
357
358 #
359 # Initialise
360 #
361
362 ($max_slot, $max_drive) = &getTapeParams();
363
364 $opt_slot = 0;                                  # perl -w fodder
365 $opt_info = 0;                                  # perl -w fodder
366 $opt_reset = 0;                                 # perl -w fodder
367 $opt_eject = 0;                                 # perl -w fodder
368
369 GetOptions("slot=s", "info", "reset", "eject"); 
370
371 $nr_tapes = &getTapeStatus();
372
373 #
374 # Before we do anything with the tape changer we'll have to rewind the tape
375 #
376
377 if (-x "$sbindir/ammt$SUF") {
378         $MT="$sbindir/ammt$SUF";
379         $MTF="-f";
380 } elsif (-x "@MT@") {
381         $MT="@MT@";
382         $MTF="@MT_FILE_FLAG@";
383 } else {
384         print LOG &do_time(), ": mt program not found\n";
385         print "<none> mt program not found\n";
386         exit(1);
387 }
388 print LOG &do_time(), ": MT -> $MT $MTF\n";
389
390 system ("$MT $MTF $tapeDevice rewind")
391         unless $currentTape == 0;
392
393
394 if ( $opt_slot ) {
395         if ( $opt_slot =~ /first/ ) {
396                 &changeTape(1);
397                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
398                 print "$currentTape $tapeDevice\n";
399         }
400         if ( $opt_slot =~ /last/ ) {
401                 &changeTape($max_slot);
402                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
403                 print "$currentTape $tapeDevice\n";
404         }
405         if ( $opt_slot =~ /current/ ) {
406                 &changeTape($currentTape);
407                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
408                 print "$currentTape $tapeDevice\n";
409         }
410         if ( $opt_slot =~ /next/ ) {
411                 $tape = $currentTape+1;
412                 if ( $tape > $max_slot ) {
413                         $tape = 1;
414                 }
415                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1 
416                         if ( ++$tape > $max_slot ) {
417                                 $tape = 1;
418                         }
419                 }
420                 &changeTape($tape);
421                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
422                 print "$currentTape $tapeDevice\n";
423         }
424         if ( $opt_slot =~ /prev/ ) {
425                 $tape = $currentTape-1;
426                 if ( $tape < 1 ) {
427                         $tape = $max_slot;
428                 }
429                 while ( $slots[$tape-1] == 0 ) {        # there is at least 1
430                         if ( --$tape < 1 ) {
431                                 $tape = $max_slot;
432                         }
433                 }
434                 &changeTape($tape);
435                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
436                 print "$currentTape $tapeDevice\n";
437         }
438         if ( $opt_slot =~ /^\d+$/ ) {
439                 &changeTape($opt_slot);
440                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
441                 print "$currentTape $tapeDevice\n";
442         }
443         if ( $opt_slot =~ /advance/ ) {
444                 $tape=$currentTape+1;
445                 if ( $tape > $max_slot ) {
446                         $tape = 1;
447                 }
448                 if ( $currentTape ) { 
449                         &Unload($currentTape);
450                 }
451                 print LOG &do_time(), ": $currentTape $tapeDevice\n";
452                 print "$currentTape , /dev/null\n";
453         }
454
455         exit 0;
456 }
457
458 if ( $opt_info ) {
459         if ( $currentTape == 0 ) {
460                 &Load(1);                       # load random tape
461                 $currentTape = 1;
462         }
463
464         print LOG &do_time(), ": $currentTape $max_slot 1\n";
465         print "$currentTape $max_slot 1\n";
466         exit 0;
467 }
468
469 if ( $opt_reset ) {
470         &changeTape(1);
471         print LOG &do_time(), ": $currentTape $tapeDevice\n";
472         print "$currentTape $tapeDevice\n";
473         exit 0;
474 }
475
476 if ( $opt_eject ) {
477         if ( $currentTape ) { 
478                 &Unload($currentTape);
479                 print "0 $tapeDevice\n";
480                 exit 0;
481         } else {
482                 print "$progname: drive was not loaded\n";
483                 exit 1;
484         }
485 }
486
487 print "$progname: No command was received.  Exiting.\n";
488 exit 1;