Imported Upstream version 3.3.3
[debian/amanda] / device-src / amtapetype.pl
1 #! @PERL@
2 # Copyright (c) 2008-2012 Zmanda, Inc.  All Rights Reserved.
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20
21 # This is a tool to examine a device and generate a reasonable tapetype
22 # entry accordingly.
23
24 use lib '@amperldir@';
25 use strict;
26 use warnings;
27
28 use File::Basename;
29 use Getopt::Long;
30 use Math::BigInt;
31 use Amanda::BigIntCompat;
32
33 use Amanda::Device qw( :constants );
34 use Amanda::Debug qw( :logging );
35 use Amanda::Util qw( :constants );
36 use Amanda::Config qw( :init :getconf config_dir_relative );
37 use Amanda::MainLoop;
38 use Amanda::Xfer;
39 use Amanda::Constants;
40 use Amanda::Header;
41
42 # command-line options
43 my $opt_only_compression = 0;
44 my $opt_blocksize;
45 my $opt_tapetype_name = 'unknown-tapetype';
46 my $opt_force = 0;
47 my $opt_label = "amtapetype-".(int rand 2**31);
48 my $opt_device_name;
49 my $opt_config;
50 my $opt_property;
51
52 # global "hint" from the compression heuristic as to how fast this
53 # drive is.
54 my $device_speed_estimate;
55
56 # open up a device, optionally check its label on the first invocation,
57 # and start it in ACCESS_WRITE.
58 my $_label_checked = 0;
59 sub open_device {
60     my $device = Amanda::Device->new($opt_device_name);
61     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
62         die("Could not open device $opt_device_name: ".$device->error()."\n");
63     }
64
65     if (!$device->configure(0)) {
66         die("Errors configuring $opt_device_name: " . $device->error_or_status());
67     }
68
69     if (defined $opt_blocksize) {
70         $device->property_set('BLOCK_SIZE', $opt_blocksize)
71             or die "Error setting blocksize: " . $device->error_or_status();
72     }
73
74     if (!$opt_force and !$_label_checked) {
75         my $read_label_status = $device->read_label();
76         if ($read_label_status & $DEVICE_STATUS_VOLUME_UNLABELED) {
77             # unlabeled is OK
78         } elsif ($read_label_status != $DEVICE_STATUS_SUCCESS) {
79             die "Error reading label: " . $device->error_or_status();
80         } elsif ($device->volume_label) {
81             die "Volume in device $opt_device_name has Amanda label '" .
82                 $device->volume_label . "'. Giving up.";
83         }
84         $_label_checked = 1;
85     }
86
87     my $start_time = time;
88     my $retries = 0;
89     my $backoff = 1;
90     while (1) {
91         last if ($device->start($ACCESS_WRITE, $opt_label, undef));
92         if (!($device->status & $DEVICE_STATUS_DEVICE_BUSY)) {
93             die("Error writing label '$opt_label': ". $device->error_or_status());
94         }
95
96         if ($retries == 0) {
97             print STDERR "Device is busy.  Amtapetype will retry forever; hit ctrl-C to quit.\n";
98         }
99
100         sleep($backoff);
101         $backoff *= 2;
102         $backoff = 120 if $backoff > 120;
103         $retries++;
104     }
105
106     if ($retries) {
107         my $elapsed = time - $start_time;
108         print STDERR "Drive was busy for $elapsed seconds.\n";
109         print STDERR "If this device is used in a changer, you may want to set timeouts appropriately.\n";
110     }
111
112     return $device;
113 }
114
115 # Write a single file to the device, and record the results in STATS.
116 # write_one_file(
117 #   STATS => $stats_hashref,    (see below)
118 #   DEVICE => $dev,             (device to write to)
119 #   PATTERN => RANDOM or FIXED, (data pattern to write)
120 #   BYTES => nn,                (number of bytes; optional)
121 #   MAX_TIME => secs);          (cancel write after this time; optional)
122 #
123 # Returns 0 on success (including EOM), "TIMEOUT" on timeout, or an error message
124 # on failure.
125 #
126 # STATS is a multi-level hashref; write_one_file adds to any values
127 # already in the data structure.
128 #   $stats->{$pattern}->{TIME} - number of seconds spent writing
129 #   $stats->{$pattern}->{FILES} - number of files written
130 #   $stats->{$pattern}->{BYTES} - number of bytes written (approximate)
131 #
132 sub write_one_file(%) {
133     my %options = @_;
134     my $stats = $options{'STATS'} || { };
135     my $device = $options{'DEVICE'};
136     my $bytes = $options{'MAX_BYTES'} || 0;
137     my $pattern = $options{'PATTERN'} || 'FIXED';
138     my $max_time = $options{'MAX_TIME'} || 0;
139
140     # get the block size now, while the device is still working
141     my $block_size = $device->property_get("block_size");
142
143     # start the device
144     my $hdr = Amanda::Header->new();
145     $hdr->{type} = $Amanda::Header::F_DUMPFILE;
146     $hdr->{name} = "amtapetype";
147     $hdr->{disk} = "/test";
148     $hdr->{datestamp} = "X";
149     $hdr->{program} = "AMTAPETYPE";
150     $device->start_file($hdr)
151         or return $device->error_or_status();
152
153     # set up the transfer
154     my ($source, $dest, $xfer);
155     if ($pattern eq 'FIXED') {
156         # a simple 256-byte pattern to dodge run length encoding.
157         my $non_random_pattern = pack("C*", 0..255);
158         $source = Amanda::Xfer::Source::Pattern->new($bytes, $non_random_pattern);
159     } elsif ($pattern eq 'RANDOM') {
160         $source = Amanda::Xfer::Source::Random->new($bytes, 1 + int rand 100);
161     } else {
162         die "Unknown PATTERN $pattern";
163     }
164     $dest = Amanda::Xfer::Dest::Device->new($device, 1);
165     $xfer = Amanda::Xfer->new([$source, $dest]);
166
167     # set up the relevant callbacks
168     my ($timeout_src, $spinner_src);
169     my $got_error = 0;
170     my $got_timeout = 0;
171
172     if ($max_time) {
173         $timeout_src = Amanda::MainLoop::timeout_source($max_time * 1000);
174         $timeout_src->set_callback(sub {
175             my ($src) = @_;
176             $got_timeout = 1;
177             $xfer->cancel(); # will result in an XFER_DONE
178         });
179     }
180
181     $spinner_src = Amanda::MainLoop::timeout_source(1000);
182     $spinner_src->set_callback(sub {
183         my ($src) = @_;
184         my ($file, $block) = ($device->file(), $device->block());
185         print STDERR "File $file, block $block    \r";
186     });
187
188     my $start_time = time();
189
190     $xfer->start(sub {
191         my ($src, $xmsg, $xfer) = @_;
192         if ($xmsg->{type} == $Amanda::Xfer::XMSG_ERROR) {
193             $got_error = $xmsg->{message};
194         } elsif ($xmsg->{'type'} == $Amanda::Xfer::XMSG_DONE) {
195             Amanda::MainLoop::quit();
196         }
197     });
198
199     Amanda::MainLoop::run();
200     $spinner_src->remove();
201     $timeout_src->remove() if ($timeout_src);
202     print STDERR " " x 60, "\r";
203
204     my $duration = time() - $start_time;
205
206     # OK, we finished, update statistics (even if we saw an error)
207     my $blocks_written = $device->block();
208     $stats->{$pattern}->{BYTES} += $blocks_written * $block_size;
209     $stats->{$pattern}->{FILES} += 1;
210     $stats->{$pattern}->{TIME}  += $duration;
211
212     # make sure the time is nonzero
213     if ($stats->{$pattern}->{TIME} == 0) {
214         $stats->{$pattern}->{TIME}++;
215     }
216
217     if ($device->status() != $Amanda::Device::DEVICE_STATUS_SUCCESS) {
218         return $device->error_or_status();
219     }
220
221     if ($got_error && $got_error =~ /LEOM detected/) {
222         return "LEOM";
223     }
224
225     if ($got_error) {
226         return $got_error;
227     }
228
229     if ($got_timeout) {
230         return "TIMEOUT";
231     }
232
233     return 0;
234 }
235
236 sub check_compression {
237     my $device = open_device();
238
239     # Check compression status here by property query. If the device can answer
240     # the question, there's no reason to investigate further.
241     my $compression_enabled = $device->property_get("compression");
242
243     if (defined $compression_enabled) {
244         return $compression_enabled;
245     }
246
247     # Need to use heuristic to find out if compression is enabled.  Also, we
248     # rewind between passes so that the second pass doesn't get some kind of
249     # buffering advantage.
250
251     print STDERR "Applying heuristic check for compression.\n";
252
253     # We base our determination on whether it's faster to write random data or
254     # patterned data.  That starts by writing random data for a short length of
255     # time, then measuring the elapsed time and total data written.  Due to
256     # potential delay in cancelling a transfer, the elapsed time will be a bit
257     # longer than the intended time.   We then write the same amount of
258     # patterned data, and again measure the elapsed time.  We can then
259     # calculate the speeds of the two operations.  If the compressible speed
260     # was faster by more than min_ratio, then we assume compression is enabled.
261
262     my $compression_check_time = 60;
263     my $compression_check_min_ratio = 1.2;
264
265     my $stats = { };
266
267     my $err = write_one_file(
268                     DEVICE => $device,
269                     STATS => $stats,
270                     MAX_TIME => $compression_check_time,
271                     PATTERN => 'RANDOM');
272
273     if ($err != 'TIMEOUT') {
274         die $err;
275     }
276     $device->finish();
277
278     # speed calculations are a little tricky: BigInt * float comes out to NaN, so we
279     # cast the BigInts to float first
280     my $random_speed = ($stats->{RANDOM}->{BYTES} . "") / $stats->{RANDOM}->{TIME};
281     print STDERR "Wrote random (uncompressible) data at $random_speed bytes/sec\n";
282
283     # sock this away for make_tapetype's use
284     $device_speed_estimate = $random_speed;
285
286     # restart the device to clear any errors and rewind it
287     $device = open_device();
288
289     $err = write_one_file(
290                     DEVICE => $device,
291                     STATS => $stats,
292                     MAX_BYTES => $stats->{'RANDOM'}->{'BYTES'},
293                     PATTERN => 'FIXED');
294     if ($err) {
295         die $err;
296     }
297     $device->finish();
298
299     my $fixed_speed = ($stats->{FIXED}->{BYTES} . "") / $stats->{FIXED}->{TIME};
300     print STDERR "Wrote fixed (compressible) data at $fixed_speed bytes/sec\n";
301
302     $compression_enabled =
303         ($fixed_speed / $random_speed > $compression_check_min_ratio);
304     return $compression_enabled;
305 }
306
307 sub data_to_null {
308     my ($device) = @_;
309     my $got_error = 0;
310
311     my $xfer = Amanda::Xfer->new([
312         Amanda::Xfer::Source::Device->new($device),
313         Amanda::Xfer::Dest::Null->new(0),
314     ]); 
315
316     $xfer->start(sub {
317         my ($src, $xmsg, $xfer) = @_;
318         if ($xmsg->{type} == $Amanda::Xfer::XMSG_ERROR) {
319             $got_error = $xmsg->{message};
320         } elsif ($xmsg->{'type'} == $Amanda::Xfer::XMSG_DONE) {
321             Amanda::MainLoop::quit();
322         }
323     });
324
325     Amanda::MainLoop::run();
326 }
327
328 sub check_property {
329     my $device = open_device();
330
331     print STDERR "Checking for FSF_AFTER_FILEMARK requirement\n";
332     my $fsf_after_filemark = $device->property_get("FSF_AFTER_FILEMARK");
333
334     # not a 'tape:' device
335     return if !defined $fsf_after_filemark;
336
337     $device->start($ACCESS_WRITE, "TEST-001", "20080706050403");
338
339     my $hdr = new Amanda::Header;
340
341     $hdr->{type} = $Amanda::Header::F_DUMPFILE;
342     $hdr->{name} = "localhost";
343     $hdr->{disk} = "/test1";
344     $hdr->{datestamp} = "20080706050403";
345     $hdr->{program} = "AMTAPETYPE";
346     $device->start_file($hdr);
347     $device->finish_file();
348
349     $hdr->{type} = $Amanda::Header::F_DUMPFILE;
350     $hdr->{name} = "localhost";
351     $hdr->{disk} = "/test2";
352     $hdr->{datestamp} = "20080706050403";
353     $hdr->{program} = "AMTAPETYPE";
354     $device->start_file($hdr);
355     $device->finish_file();
356
357     $hdr->{type} = $Amanda::Header::F_DUMPFILE;
358     $hdr->{name} = "localhost";
359     $hdr->{disk} = "/test3";
360     $hdr->{datestamp} = "20080706050403";
361     $hdr->{program} = "AMTAPETYPE";
362     $device->start_file($hdr);
363     $device->finish_file();
364
365     $device->finish();
366
367     #set fsf_after_filemark to false
368     $device->property_set('FSF_AFTER_FILEMARK', 0)
369             or die "Error setting FSF_AFTER_FILEMARK: " . $device->error_or_status();
370
371     my $need_fsf_after_filemark = 0;
372
373     if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
374         die ("Could not read label from: " . $device->error_or_status());
375     }
376     if ($device->volume_label != "TEST-001") {
377         die ("wrong label: ", $device->volume_label);
378     }
379     $device->start($ACCESS_READ, undef, undef)
380         or die ("Could not start device: " . $device->error_or_status());
381
382     $hdr = $device->seek_file(1);
383     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
384         die ("seek_file(1) failed");
385     }
386     if ($hdr->{disk} ne "/test1") {
387         die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
388     }
389     data_to_null($device);
390
391     $hdr = $device->seek_file(2);
392     if ($device->status() == $DEVICE_STATUS_SUCCESS) {
393         if ($hdr->{disk} ne "/test2") {
394             die ("Wrong disk: " . $hdr->{disk} . " expected /test2");
395         }
396         data_to_null($device);
397
398         $hdr = $device->seek_file(3);
399         if ($device->status() != $DEVICE_STATUS_SUCCESS) {
400             die("seek_file(3) failed");
401         }
402         if ($hdr->{disk} ne "/test3") {
403             die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
404         }
405         data_to_null($device);
406
407         $device->finish();
408     } else {
409         $need_fsf_after_filemark = 1;
410
411         # $device is in error, so open a new one
412         $device->finish();
413         $device = open_device();
414     }
415
416     #verify need_fsf_after_filemark
417     my $fsf_after_filemark_works = 0;
418     if ($need_fsf_after_filemark) {
419         #set fsf_after_filemark to true
420         $device->property_set('FSF_AFTER_FILEMARK', 1)
421             or die "Error setting FSF_AFTER_FILEMARK: " . $device->error_or_status();
422
423         if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
424             die ("Could not read label from: " . $device->error_or_status());
425         }
426         if ($device->volume_label != "TEST-001") {
427             die ("wrong label: ", $device->volume_label);
428         }
429         $device->start($ACCESS_READ, undef, undef)
430             or die ("Could not start device: " . $device->error_or_status());
431
432         $hdr = $device->seek_file(1);
433         if ($device->status() != $DEVICE_STATUS_SUCCESS) {
434             die ("seek_file(1) failed");
435         }
436         if ($hdr->{disk} ne "/test1") {
437             die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
438         }
439         data_to_null($device);
440
441         $hdr = $device->seek_file(2);
442         if ($device->status() == $DEVICE_STATUS_SUCCESS) {
443             if ($hdr->{disk} ne "/test2") {
444                 die ("Wrong disk: " . $hdr->{disk} . " expected /test2");
445             }
446             data_to_null($device);
447
448             $hdr = $device->seek_file(3);
449             if ($device->status() != $DEVICE_STATUS_SUCCESS) {
450                 die("seek_file(3) failed");
451             }
452             if ($hdr->{disk} ne "/test3") {
453                 die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
454             }
455             data_to_null($device);
456             $fsf_after_filemark_works = 1;
457         } else {
458             die("seek_file(2) failed");
459         }
460         $device->finish();
461     }
462
463     if ($need_fsf_after_filemark == 0 && $fsf_after_filemark_works == 0) {
464         if (defined $opt_property || $fsf_after_filemark) {
465             print STDOUT "device-property \"FSF_AFTER_FILEMARK\" \"false\"\n";
466         }
467         $device->property_set('FSF_AFTER_FILEMARK', 0);
468     } elsif ($need_fsf_after_filemark == 1 && $fsf_after_filemark_works == 1) {
469         if (defined $opt_property || !$fsf_after_filemark) {
470             print STDOUT "device-property \"FSF_AFTER_FILEMARK\" \"true\"\n";
471         }
472         $device->property_set('FSF_AFTER_FILEMARK', 1);
473     } else {
474         die ("Broken seek_file");
475     }
476
477     #Check seek to file 1 from header
478     if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
479         die ("Could not read label from: " . $device->error_or_status());
480     }
481     if ($device->volume_label != "TEST-001") {
482         die ("wrong label: ", $device->volume_label);
483     }
484     $device->start($ACCESS_READ, undef, undef)
485         or die ("Could not start device: " . $device->error_or_status());
486
487     $hdr = $device->seek_file(1);
488     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
489         die ("seek_file(1) failed");
490     }
491     if ($hdr->{disk} ne "/test1") {
492         die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
493     }
494     $device->finish();
495
496     #Check seek to file 2 from header
497     if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
498         die ("Could not read label from: " . $device->error_or_status());
499     }
500     if ($device->volume_label != "TEST-001") {
501         die ("wrong label: ", $device->volume_label);
502     }
503     $device->start($ACCESS_READ, undef, undef)
504         or die ("Could not start device: " . $device->error_or_status());
505
506     $hdr = $device->seek_file(2);
507     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
508         die ("seek_file(2) failed");
509     }
510     if ($hdr->{disk} ne "/test2") {
511         die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
512     }
513     $device->finish();
514
515     #Check seek to file 3 from header
516     if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
517         die ("Could not read label from: " . $device->error_or_status());
518     }
519     if ($device->volume_label != "TEST-001") {
520         die ("wrong label: ", $device->volume_label);
521     }
522     $device->start($ACCESS_READ, undef, undef)
523         or die ("Could not start device: " . $device->error_or_status());
524
525     $hdr = $device->seek_file(3);
526     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
527         die ("seek_file(3) failed");
528     }
529     if ($hdr->{disk} ne "/test3") {
530         die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
531     }
532     $device->finish();
533
534     #Check seek to file 3 from eof of 1
535     if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
536         die ("Could not read label from: " . $device->error_or_status());
537     }
538     if ($device->volume_label != "TEST-001") {
539         die ("wrong label: ", $device->volume_label);
540     }
541     $device->start($ACCESS_READ, undef, undef)
542         or die ("Could not start device: " . $device->error_or_status());
543
544     $hdr = $device->seek_file(1);
545     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
546         die ("seek_file(1) failed");
547     }
548     data_to_null($device);
549     $hdr = $device->seek_file(3);
550     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
551         die ("seek_file(3) failed");
552     }
553     if ($hdr->{disk} ne "/test3") {
554         die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
555     }
556     $device->finish();
557 }
558
559 sub make_tapetype {
560     my ($compression_enabled) = @_;
561
562     my $device = open_device();
563     my $blocksize = $device->property_get("BLOCK_SIZE");
564
565     # First, write one very long file to get the total tape length
566     print STDERR "Writing one file to fill the volume.\n";
567     my $stats = {};
568     my $err = write_one_file(
569                 DEVICE => $device,
570                 STATS => $stats,
571                 PATTERN => 'RANDOM');
572
573     # if we wrote almost no data, then there's probably a problem
574     # with the device, so error out
575     if ($stats->{RANDOM}->{BYTES} < 1024 * 1024) {
576         die "Wrote less than 1MB to the device: $err\n";
577     }
578     my $volume_size_estimate = $stats->{RANDOM}->{BYTES};
579     my $speed_estimate = (($stats->{RANDOM}->{BYTES}."") / 1024)
580                         / $stats->{RANDOM}->{TIME};
581     $speed_estimate = int $speed_estimate;
582     print STDERR "Wrote $volume_size_estimate bytes at $speed_estimate kb/sec\n";
583
584     my $leom = 0;
585     if ($err eq 'LEOM') {
586         print STDERR "Got LEOM indication, so drive and kernel together support LEOM\n";
587         $leom = 1;
588     }
589
590     # now we want to write about 100 filemarks; round down to the blocksize
591     # to avoid counting padding as part of the filemark
592     my $file_size = $volume_size_estimate / 100;
593     $file_size -= $file_size % $blocksize;
594
595     print STDERR "Writing smaller files ($file_size bytes) to determine filemark.\n";
596     $device->finish();
597     $device = open_device(); # re-open to rewind and clear errors
598     $stats = {};
599     while (!write_one_file(
600                         DEVICE => $device,
601                         STATS => $stats,
602                         MAX_BYTES => $file_size,
603                         PATTERN => 'RANDOM')) { }
604
605     my $filemark_estimate = ($volume_size_estimate - $stats->{RANDOM}->{BYTES})
606                           / ($stats->{RANDOM}->{FILES} - 1);
607     if ($filemark_estimate < 0) {
608         $filemark_estimate = 0;
609     }
610
611     my $comment = "Created by amtapetype; compression "
612         . ($compression_enabled? "enabled" : "disabled");
613
614     # round these parameters to the nearest kb, since the parameters' units
615     # are kb, not bytes
616     my $volume_size_estimate_kb = $volume_size_estimate/1024;
617     my $filemark_kb = $filemark_estimate/1024;
618
619     # and suggest using device-property for blocksize if it's not an even multiple
620     # of 1kb
621     my $blocksize_line;
622     if ($blocksize % 1024 == 0) {
623         $blocksize_line = "blocksize " . $blocksize/1024 . " kbytes";
624     } else {
625         $blocksize_line = "# add device-property \"BLOCK_SIZE\" \"$blocksize\" to the device";
626     }
627
628     print <<EOF;
629 define tapetype $opt_tapetype_name {
630     comment "$comment"
631     length $volume_size_estimate_kb kbytes
632     filemark $filemark_kb kbytes
633     speed $speed_estimate kps
634     $blocksize_line
635 }
636 EOF
637
638     if ($leom) {
639         print "# for this drive and kernel, LEOM is supported; add\n";
640         print "#   device-property \"LEOM\" \"TRUE\"\n";
641         print "# for this device.\n";
642     } else {
643         print "# LEOM is not supported for this drive and kernel\n";
644     }
645 }
646
647 sub usage {
648     print STDERR <<EOF;
649 Usage: amtapetype [-h] [-c] [-f] [-b blocksize] [-t typename] [-l label]
650                   [ [-o config_override] ... ] [config] device
651         -h   Display this message
652         -c   Only check hardware compression state
653         -f   Run amtapetype even if the loaded volume is already in use
654              or compression is enabled.
655         -b   Blocksize to use (default 32k)
656         -t   Name to give to the new tapetype definition
657         -l   Label to write to the tape (default is randomly generated)
658         -p   Check property of the device.
659         -o   Overwrite configuration parameter (such as device properties)
660     Blocksize can include an optional suffix (k, m, or g)
661
662     If CONFIG is specified, the device and its configuration are loaded
663     from the correspnding amanda.conf.
664 EOF
665     exit(1);
666 }
667
668 ## Application initialization
669
670 Amanda::Util::setup_application("amtapetype", "server", $CONTEXT_CMDLINE);
671 config_init(0, undef);
672
673 my $config_overrides = new_config_overrides($#ARGV+1);
674
675 debug("Arguments: " . join(' ', @ARGV));
676 Getopt::Long::Configure(qw(bundling));
677 GetOptions(
678     'version' => \&Amanda::Util::version_opt,
679     'help|usage|?|h' => \&usage,
680     'c' => \$opt_only_compression,
681     'b=s' => sub {
682         my ($num, $suff) = ($_[1] =~ /^([0-9]+)\s*(.*)$/);
683         die "Invalid blocksize '$_[1]'" unless (defined $num);
684         my $mult = (defined $suff)?
685             Amanda::Config::find_multiplier($suff) : 1;
686         die "Invalid suffix '$suff'" unless ($mult);
687         $opt_blocksize = $num * $mult;
688     },
689     't=s' => \$opt_tapetype_name,
690     'f' => \$opt_force,
691     'l' => \$opt_label,
692     'p' => \$opt_property,
693     'o=s' => sub { add_config_override_opt($config_overrides, $_[1]); },
694 ) or usage();
695 usage() if (@ARGV < 1 or @ARGV > 2);
696
697 set_config_overrides($config_overrides);
698 if (@ARGV == 2) {
699     $opt_config = shift @ARGV;
700     config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
701 } else {
702     config_init(0, undef);
703 }
704 $opt_device_name= shift @ARGV;
705
706 my ($cfgerr_level, @cfgerr_errors) = config_errors();
707 if ($cfgerr_level >= $CFGERR_WARNINGS) {
708     config_print_errors();
709     if ($cfgerr_level >= $CFGERR_ERRORS) {
710         die("errors processing config file");
711     }
712 }
713
714 Amanda::Util::finish_setup($RUNNING_AS_ANY);
715
716 # Find property of the device.
717 check_property();
718
719 if (!defined $opt_property) {
720     my $compression_enabled = check_compression();
721     print STDERR "Compression: ",
722         $compression_enabled? "enabled" : "disabled",
723         "\n";
724
725     if ($compression_enabled and !$opt_force) {
726         print STDERR "Turn off compression or run amtapetype with the -f option\n";
727         exit(1);
728     }
729
730     if (!$opt_only_compression) {
731         make_tapetype($compression_enabled);
732     }
733 }
734
735 Amanda::Util::finish_application();