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