Imported Upstream version 3.2.0
[debian/amanda] / device-src / amtapetype.pl
index 299bd77cc04f34114e2626b5417d71c3f835a457..bbc7d32aa34e0a8572b0aedb2f3c54a75e294734 100644 (file)
@@ -1,5 +1,5 @@
 #! @PERL@
-# Copyright (c) 2008 Zmanda Inc.  All Rights Reserved.
+# Copyright (c) 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
 #
 # This program is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License version 2 as published
@@ -14,7 +14,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 #
-# Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
+# Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
 
 # This is a tool to examine a device and generate a reasonable tapetype
@@ -22,6 +22,7 @@
 
 use lib '@amperldir@';
 use strict;
+use warnings;
 
 use File::Basename;
 use Getopt::Long;
@@ -35,7 +36,7 @@ use Amanda::Config qw( :init :getconf config_dir_relative );
 use Amanda::MainLoop;
 use Amanda::Xfer;
 use Amanda::Constants;
-use Amanda::Types;
+use Amanda::Header;
 
 # command-line options
 my $opt_only_compression = 0;
@@ -44,43 +45,67 @@ my $opt_tapetype_name = 'unknown-tapetype';
 my $opt_force = 0;
 my $opt_label = "amtapetype-".(int rand 2**31);
 my $opt_device_name;
+my $opt_config;
+my $opt_property;
 
 # global "hint" from the compression heuristic as to how fast this
 # drive is.
 my $device_speed_estimate;
 
-# open up a device, optionally check its label, and start it in ACCESS_WRITE.
+# open up a device, optionally check its label on the first invocation,
+# and start it in ACCESS_WRITE.
+my $_label_checked = 0;
 sub open_device {
     my $device = Amanda::Device->new($opt_device_name);
     if ($device->status() != $DEVICE_STATUS_SUCCESS) {
        die("Could not open device $opt_device_name: ".$device->error()."\n");
     }
 
+    if (!$device->configure(0)) {
+       die("Errors configuring $opt_device_name: " . $device->error_or_status());
+    }
+
     if (defined $opt_blocksize) {
        $device->property_set('BLOCK_SIZE', $opt_blocksize)
            or die "Error setting blocksize: " . $device->error_or_status();
     }
 
-    if (!$opt_force) {
+    if (!$opt_force and !$_label_checked) {
        my $read_label_status = $device->read_label();
        if ($read_label_status & $DEVICE_STATUS_VOLUME_UNLABELED) {
-           if ($device->volume_label) {
-               die "Volume in device $opt_device_name has Amanda label '" .
-                   {$device->volume_label} . "'. Giving up.";
-           }
+           # unlabeled is OK
        } elsif ($read_label_status != $DEVICE_STATUS_SUCCESS) {
            die "Error reading label: " . $device->error_or_status();
+       } elsif ($device->volume_label) {
+           die "Volume in device $opt_device_name has Amanda label '" .
+               $device->volume_label . "'. Giving up.";
        }
+       $_label_checked = 1;
     }
 
-    return $device;
-}
+    my $start_time = time;
+    my $retries = 0;
+    my $backoff = 1;
+    while (1) {
+       last if ($device->start($ACCESS_WRITE, $opt_label, undef));
+       if (!($device->status & $DEVICE_STATUS_DEVICE_BUSY)) {
+           die("Error writing label '$opt_label': ". $device->error_or_status());
+       }
 
-sub start_device {
-    my ($device) = @_;
+       if ($retries == 0) {
+           print STDERR "Device is busy.  Amtapetype will retry forever; hit ctrl-C to quit.\n";
+       }
+
+       sleep($backoff);
+       $backoff *= 2;
+       $backoff = 120 if $backoff > 120;
+       $retries++;
+    }
 
-    if (!$device->start($ACCESS_WRITE, $opt_label, undef)) {
-       die("Error writing label '$opt_label': ". $device->error_or_status());
+    if ($retries) {
+       my $elapsed = time - $start_time;
+       print STDERR "Drive was busy for $elapsed seconds.\n";
+       print STDERR "If this device is used in a changer, you may want to set timeouts appropriately.\n";
     }
 
     return $device;
@@ -111,12 +136,16 @@ sub write_one_file(%) {
     my $pattern = $options{'PATTERN'} || 'FIXED';
     my $max_time = $options{'MAX_TIME'} || 0;
 
+    # get the block size now, while the device is still working
+    my $block_size = $device->property_get("block_size");
+
     # start the device
-    my $hdr = Amanda::Types::dumpfile_t->new();
-    $hdr->{type} = $Amanda::Types::F_DUMPFILE;
+    my $hdr = Amanda::Header->new();
+    $hdr->{type} = $Amanda::Header::F_DUMPFILE;
     $hdr->{name} = "amtapetype";
     $hdr->{disk} = "/test";
     $hdr->{datestamp} = "X";
+    $hdr->{program} = "AMTAPETYPE";
     $device->start_file($hdr)
        or return $device->error_or_status();
 
@@ -131,25 +160,14 @@ sub write_one_file(%) {
     } else {
        die "Unknown PATTERN $pattern";
     }
-    $dest = Amanda::Xfer::Dest::Device->new($device, 0);
+    $dest = Amanda::Xfer::Dest::Device->new($device, 1);
     $xfer = Amanda::Xfer->new([$source, $dest]);
 
     # set up the relevant callbacks
-    my ($timeout_src, $xfer_src, $spinner_src);
+    my ($timeout_src, $spinner_src);
     my $got_error = 0;
     my $got_timeout = 0;
 
-    $xfer_src = $xfer->get_source();
-    $xfer_src->set_callback(sub {
-       my ($src, $xmsg, $xfer) = @_;
-       if ($xmsg->{type} == $Amanda::Xfer::XMSG_ERROR) {
-           $got_error = $xmsg->{message};
-       }
-       if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
-           Amanda::MainLoop::quit();
-       }
-    });
-
     if ($max_time) {
        $timeout_src = Amanda::MainLoop::timeout_source($max_time * 1000);
        $timeout_src->set_callback(sub {
@@ -168,9 +186,16 @@ sub write_one_file(%) {
 
     my $start_time = time();
 
-    $xfer->start();
+    $xfer->start(sub {
+       my ($src, $xmsg, $xfer) = @_;
+       if ($xmsg->{type} == $Amanda::Xfer::XMSG_ERROR) {
+           $got_error = $xmsg->{message};
+       } elsif ($xmsg->{'type'} == $Amanda::Xfer::XMSG_DONE) {
+           Amanda::MainLoop::quit();
+       }
+    });
+
     Amanda::MainLoop::run();
-    $xfer_src->remove();
     $spinner_src->remove();
     $timeout_src->remove() if ($timeout_src);
     print STDERR " " x 60, "\r";
@@ -179,15 +204,23 @@ sub write_one_file(%) {
 
     # OK, we finished, update statistics (even if we saw an error)
     my $blocks_written = $device->block();
-    my $block_size = $device->property_get("block_size");
     $stats->{$pattern}->{BYTES} += $blocks_written * $block_size;
     $stats->{$pattern}->{FILES} += 1;
     $stats->{$pattern}->{TIME}  += $duration;
 
+    # make sure the time is nonzero
+    if ($stats->{$pattern}->{TIME} == 0) {
+       $stats->{$pattern}->{TIME}++;
+    }
+
     if ($device->status() != $Amanda::Device::DEVICE_STATUS_SUCCESS) {
        return $device->error_or_status();
     }
 
+    if ($got_error && $got_error =~ /LEOM detected/) {
+       return "LEOM";
+    }
+
     if ($got_error) {
        return $got_error;
     }
@@ -200,7 +233,7 @@ sub write_one_file(%) {
 }
 
 sub check_compression {
-    my ($device) = @_;
+    my $device = open_device();
 
     # Check compression status here by property query. If the device can answer
     # the question, there's no reason to investigate further.
@@ -230,8 +263,6 @@ sub check_compression {
 
     my $stats = { };
 
-    start_device($device);
-
     my $err = write_one_file(
                    DEVICE => $device,
                    STATS => $stats,
@@ -241,9 +272,18 @@ sub check_compression {
     if ($err != 'TIMEOUT') {
        die $err;
     }
+    $device->finish();
+
+    # speed calculations are a little tricky: BigInt * float comes out to NaN, so we
+    # cast the BigInts to float first
+    my $random_speed = ($stats->{RANDOM}->{BYTES} . "") / $stats->{RANDOM}->{TIME};
+    print STDERR "Wrote random (uncompressible) data at $random_speed bytes/sec\n";
+
+    # sock this away for make_tapetype's use
+    $device_speed_estimate = $random_speed;
 
-    # restart the device to rewind it
-    start_device($device);
+    # restart the device to clear any errors and rewind it
+    $device = open_device();
 
     $err = write_one_file(
                    DEVICE => $device,
@@ -253,38 +293,286 @@ sub check_compression {
     if ($err) {
        die $err;
     }
+    $device->finish();
 
-    # speed calculations are a little tricky: BigInt * float comes out to NaN, so we
-    # cast the BigInts to float first
-    my $random_speed = ($stats->{RANDOM}->{BYTES} . "") / $stats->{RANDOM}->{TIME};
     my $fixed_speed = ($stats->{FIXED}->{BYTES} . "") / $stats->{FIXED}->{TIME};
-
-    print STDERR "Wrote random (uncompressible) data at $random_speed bytes/sec\n";
     print STDERR "Wrote fixed (compressible) data at $fixed_speed bytes/sec\n";
 
-    # sock this away for make_tapetype's use
-    $device_speed_estimate = $random_speed;
-
     $compression_enabled =
        ($fixed_speed / $random_speed > $compression_check_min_ratio);
     return $compression_enabled;
 }
 
+sub data_to_null {
+    my ($device) = @_;
+    my $got_error = 0;
+
+    my $xfer = Amanda::Xfer->new([
+        Amanda::Xfer::Source::Device->new($device),
+        Amanda::Xfer::Dest::Null->new(0),
+    ]); 
+
+    $xfer->start(sub {
+       my ($src, $xmsg, $xfer) = @_;
+       if ($xmsg->{type} == $Amanda::Xfer::XMSG_ERROR) {
+           $got_error = $xmsg->{message};
+       } elsif ($xmsg->{'type'} == $Amanda::Xfer::XMSG_DONE) {
+           Amanda::MainLoop::quit();
+       }
+    });
+
+    Amanda::MainLoop::run();
+}
+
+sub check_property {
+    my $device = open_device();
+
+    print STDERR "Checking for FSF_AFTER_FILEMARK requirement\n";
+    my $fsf_after_filemark = $device->property_get("FSF_AFTER_FILEMARK");
+
+    # not a 'tape:' device
+    return if !defined $fsf_after_filemark;
+
+    $device->start($ACCESS_WRITE, "TEST-001", "20080706050403");
+
+    my $hdr = new Amanda::Header;
+
+    $hdr->{type} = $Amanda::Header::F_DUMPFILE;
+    $hdr->{name} = "localhost";
+    $hdr->{disk} = "/test1";
+    $hdr->{datestamp} = "20080706050403";
+    $hdr->{program} = "AMTAPETYPE";
+    $device->start_file($hdr);
+    $device->finish_file();
+
+    $hdr->{type} = $Amanda::Header::F_DUMPFILE;
+    $hdr->{name} = "localhost";
+    $hdr->{disk} = "/test2";
+    $hdr->{datestamp} = "20080706050403";
+    $hdr->{program} = "AMTAPETYPE";
+    $device->start_file($hdr);
+    $device->finish_file();
+
+    $hdr->{type} = $Amanda::Header::F_DUMPFILE;
+    $hdr->{name} = "localhost";
+    $hdr->{disk} = "/test3";
+    $hdr->{datestamp} = "20080706050403";
+    $hdr->{program} = "AMTAPETYPE";
+    $device->start_file($hdr);
+    $device->finish_file();
+
+    $device->finish();
+
+    #set fsf_after_filemark to false
+    $device->property_set('FSF_AFTER_FILEMARK', 0)
+           or die "Error setting FSF_AFTER_FILEMARK: " . $device->error_or_status();
+
+    my $need_fsf_after_filemark = 0;
+
+    if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+       die ("Could not read label from: " . $device->error_or_status());
+    }
+    if ($device->volume_label != "TEST-001") {
+       die ("wrong label: ", $device->volume_label);
+    }
+    $device->start($ACCESS_READ, undef, undef)
+       or die ("Could not start device: " . $device->error_or_status());
+
+    $hdr = $device->seek_file(1);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(1) failed");
+    }
+    if ($hdr->{disk} ne "/test1") {
+       die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
+    }
+    data_to_null($device);
+
+    $hdr = $device->seek_file(2);
+    if ($device->status() == $DEVICE_STATUS_SUCCESS) {
+       if ($hdr->{disk} ne "/test2") {
+           die ("Wrong disk: " . $hdr->{disk} . " expected /test2");
+       }
+       data_to_null($device);
+
+       $hdr = $device->seek_file(3);
+       if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+            die("seek_file(3) failed");
+       }
+       if ($hdr->{disk} ne "/test3") {
+           die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
+       }
+       data_to_null($device);
+
+       $device->finish();
+    } else {
+       $need_fsf_after_filemark = 1;
+
+       # $device is in error, so open a new one
+       $device->finish();
+       $device = open_device();
+    }
+
+    #verify need_fsf_after_filemark
+    my $fsf_after_filemark_works = 0;
+    if ($need_fsf_after_filemark) {
+       #set fsf_after_filemark to true
+       $device->property_set('FSF_AFTER_FILEMARK', 1)
+           or die "Error setting FSF_AFTER_FILEMARK: " . $device->error_or_status();
+
+       if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+           die ("Could not read label from: " . $device->error_or_status());
+       }
+       if ($device->volume_label != "TEST-001") {
+           die ("wrong label: ", $device->volume_label);
+       }
+       $device->start($ACCESS_READ, undef, undef)
+           or die ("Could not start device: " . $device->error_or_status());
+
+       $hdr = $device->seek_file(1);
+       if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+           die ("seek_file(1) failed");
+       }
+       if ($hdr->{disk} ne "/test1") {
+           die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
+       }
+       data_to_null($device);
+
+       $hdr = $device->seek_file(2);
+       if ($device->status() == $DEVICE_STATUS_SUCCESS) {
+           if ($hdr->{disk} ne "/test2") {
+               die ("Wrong disk: " . $hdr->{disk} . " expected /test2");
+           }
+           data_to_null($device);
+
+           $hdr = $device->seek_file(3);
+           if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+               die("seek_file(3) failed");
+           }
+           if ($hdr->{disk} ne "/test3") {
+               die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
+           }
+           data_to_null($device);
+           $fsf_after_filemark_works = 1;
+        } else {
+           die("seek_file(2) failed");
+        }
+       $device->finish();
+    }
+
+    if ($need_fsf_after_filemark == 0 && $fsf_after_filemark_works == 0) {
+       if (defined $opt_property || $fsf_after_filemark) {
+           print STDOUT "device-property \"FSF_AFTER_FILEMARK\" \"false\"\n";
+       }
+       $device->property_set('FSF_AFTER_FILEMARK', 0);
+    } elsif ($need_fsf_after_filemark == 1 && $fsf_after_filemark_works == 1) {
+       if (defined $opt_property || !$fsf_after_filemark) {
+           print STDOUT "device-property \"FSF_AFTER_FILEMARK\" \"true\"\n";
+       }
+       $device->property_set('FSF_AFTER_FILEMARK', 1);
+    } else {
+       die ("Broken seek_file");
+    }
+
+    #Check seek to file 1 from header
+    if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+       die ("Could not read label from: " . $device->error_or_status());
+    }
+    if ($device->volume_label != "TEST-001") {
+       die ("wrong label: ", $device->volume_label);
+    }
+    $device->start($ACCESS_READ, undef, undef)
+       or die ("Could not start device: " . $device->error_or_status());
+
+    $hdr = $device->seek_file(1);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(1) failed");
+    }
+    if ($hdr->{disk} ne "/test1") {
+       die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
+    }
+    $device->finish();
+
+    #Check seek to file 2 from header
+    if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+       die ("Could not read label from: " . $device->error_or_status());
+    }
+    if ($device->volume_label != "TEST-001") {
+       die ("wrong label: ", $device->volume_label);
+    }
+    $device->start($ACCESS_READ, undef, undef)
+       or die ("Could not start device: " . $device->error_or_status());
+
+    $hdr = $device->seek_file(2);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(2) failed");
+    }
+    if ($hdr->{disk} ne "/test2") {
+       die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
+    }
+    $device->finish();
+
+    #Check seek to file 3 from header
+    if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+       die ("Could not read label from: " . $device->error_or_status());
+    }
+    if ($device->volume_label != "TEST-001") {
+       die ("wrong label: ", $device->volume_label);
+    }
+    $device->start($ACCESS_READ, undef, undef)
+       or die ("Could not start device: " . $device->error_or_status());
+
+    $hdr = $device->seek_file(3);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(3) failed");
+    }
+    if ($hdr->{disk} ne "/test3") {
+       die ("Wrong disk: " . $hdr->{disk} . " expected /test1");
+    }
+    $device->finish();
+
+    #Check seek to file 3 from eof of 1
+    if ($device->read_label() != $DEVICE_STATUS_SUCCESS) {
+       die ("Could not read label from: " . $device->error_or_status());
+    }
+    if ($device->volume_label != "TEST-001") {
+       die ("wrong label: ", $device->volume_label);
+    }
+    $device->start($ACCESS_READ, undef, undef)
+       or die ("Could not start device: " . $device->error_or_status());
+
+    $hdr = $device->seek_file(1);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(1) failed");
+    }
+    data_to_null($device);
+    $hdr = $device->seek_file(3);
+    if ($device->status() != $DEVICE_STATUS_SUCCESS) {
+       die ("seek_file(3) failed");
+    }
+    if ($hdr->{disk} ne "/test3") {
+       die ("Wrong disk: " . $hdr->{disk} . " expected /test3");
+    }
+    $device->finish();
+}
+
 sub make_tapetype {
-    my ($device, $compression_enabled) = @_;
+    my ($compression_enabled) = @_;
+
+    my $device = open_device();
     my $blocksize = $device->property_get("BLOCK_SIZE");
 
     # First, write one very long file to get the total tape length
     print STDERR "Writing one file to fill the volume.\n";
     my $stats = {};
-    start_device($device);
     my $err = write_one_file(
                DEVICE => $device,
                STATS => $stats,
                PATTERN => 'RANDOM');
 
-    if ($stats->{RANDOM}->{BYTES} < 1024 * 1024 * 100) {
-       die "Wrote less than 100MB to the device: $err\n";
+    # if we wrote almost no data, then there's probably a problem
+    # with the device, so error out
+    if ($stats->{RANDOM}->{BYTES} < 1024 * 1024) {
+       die "Wrote less than 1MB to the device: $err\n";
     }
     my $volume_size_estimate = $stats->{RANDOM}->{BYTES};
     my $speed_estimate = (($stats->{RANDOM}->{BYTES}."") / 1024)
@@ -292,14 +580,21 @@ sub make_tapetype {
     $speed_estimate = int $speed_estimate;
     print STDERR "Wrote $volume_size_estimate bytes at $speed_estimate kb/sec\n";
 
+    my $leom = 0;
+    if ($err eq 'LEOM') {
+       print STDERR "Got LEOM indication, so drive and kernel together support LEOM\n";
+       $leom = 1;
+    }
+
     # now we want to write about 100 filemarks; round down to the blocksize
     # to avoid counting padding as part of the filemark
     my $file_size = $volume_size_estimate / 100;
     $file_size -= $file_size % $blocksize;
 
     print STDERR "Writing smaller files ($file_size bytes) to determine filemark.\n";
+    $device->finish();
+    $device = open_device(); # re-open to rewind and clear errors
     $stats = {};
-    start_device($device);
     while (!write_one_file(
                        DEVICE => $device,
                        STATS => $stats,
@@ -315,21 +610,43 @@ sub make_tapetype {
     my $comment = "Created by amtapetype; compression "
        . ($compression_enabled? "enabled" : "disabled");
 
-    print <<EOF;
-    define tapetype $opt_tapetype_name {
-       comment "$comment"
-       length $volume_size_estimate bytes
-       filemark $filemark_estimate bytes
-       speed $speed_estimate kps
-       blocksize $blocksize bytes
+    # round these parameters to the nearest kb, since the parameters' units
+    # are kb, not bytes
+    my $volume_size_estimate_kb = $volume_size_estimate/1024;
+    my $filemark_kb = $filemark_estimate/1024;
+
+    # and suggest using device-property for blocksize if it's not an even multiple
+    # of 1kb
+    my $blocksize_line;
+    if ($blocksize % 1024 == 0) {
+       $blocksize_line = "blocksize " . $blocksize/1024 . " kbytes";
+    } else {
+       $blocksize_line = "# add device-property \"BLOCK_SIZE\" \"$blocksize\" to the device";
     }
+
+    print <<EOF;
+define tapetype $opt_tapetype_name {
+    comment "$comment"
+    length $volume_size_estimate_kb kbytes
+    filemark $filemark_kb kbytes
+    speed $speed_estimate kps
+    $blocksize_line
+}
 EOF
+
+    if ($leom) {
+       print "# for this drive and kernel, LEOM is supported; add\n";
+       print "#   device-property \"LEOM\" \"TRUE\"\n";
+       print "# for this device.\n";
+    } else {
+       print "# LEOM is not supported for this drive and kernel\n";
+    }
 }
 
 sub usage {
     print STDERR <<EOF;
 Usage: amtapetype [-h] [-c] [-f] [-b blocksize] [-t typename] [-l label]
-                 [ [-o config_overwrite] ... ] device
+                 [ [-o config_override] ... ] [config] device
         -h   Display this message
         -c   Only check hardware compression state
         -f   Run amtapetype even if the loaded volume is already in use
@@ -337,8 +654,12 @@ Usage: amtapetype [-h] [-c] [-f] [-b blocksize] [-t typename] [-l label]
         -b   Blocksize to use (default 32k)
         -t   Name to give to the new tapetype definition
         -l   Label to write to the tape (default is randomly generated)
+        -p   Check property of the device.
         -o   Overwrite configuration parameter (such as device properties)
     Blocksize can include an optional suffix (k, m, or g)
+
+    If CONFIG is specified, the device and its configuration are loaded
+    from the correspnding amanda.conf.
 EOF
     exit(1);
 }
@@ -348,7 +669,7 @@ EOF
 Amanda::Util::setup_application("amtapetype", "server", $CONTEXT_CMDLINE);
 config_init(0, undef);
 
-my $config_overwrites = new_config_overwrites($#ARGV+1);
+my $config_overrides = new_config_overrides($#ARGV+1);
 
 Getopt::Long::Configure(qw(bundling));
 GetOptions(
@@ -365,35 +686,47 @@ GetOptions(
     't=s' => \$opt_tapetype_name,
     'f' => \$opt_force,
     'l' => \$opt_label,
-    'o=s' => sub { add_config_overwrite_opt($config_overwrites, $_[1]); },
+    'p' => \$opt_property,
+    'o=s' => sub { add_config_override_opt($config_overrides, $_[1]); },
 ) or usage();
-usage() if (@ARGV != 1);
-
+usage() if (@ARGV < 1 or @ARGV > 2);
+
+set_config_overrides($config_overrides);
+if (@ARGV == 2) {
+    $opt_config = shift @ARGV;
+    config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
+} else {
+    config_init(0, undef);
+}
 $opt_device_name= shift @ARGV;
 
-apply_config_overwrites($config_overwrites);
 my ($cfgerr_level, @cfgerr_errors) = config_errors();
 if ($cfgerr_level >= $CFGERR_WARNINGS) {
     config_print_errors();
     if ($cfgerr_level >= $CFGERR_ERRORS) {
-       die("errors processing configuration options");
+       die("errors processing config file");
     }
 }
 
 Amanda::Util::finish_setup($RUNNING_AS_ANY);
 
-my $device = open_device();
+# Find property of the device.
+check_property();
 
-my $compression_enabled = check_compression($device);
-print STDERR "Compression: ",
-    $compression_enabled? "enabled" : "disabled",
-    "\n";
+if (!defined $opt_property) {
+    my $compression_enabled = check_compression();
+    print STDERR "Compression: ",
+        $compression_enabled? "enabled" : "disabled",
+        "\n";
 
-if ($compression_enabled and !$opt_force) {
-    print STDERR "Turn off compression or run amtapetype with the -f option\n";
-    exit(1);
-}
+    if ($compression_enabled and !$opt_force) {
+        print STDERR "Turn off compression or run amtapetype with the -f option\n";
+        exit(1);
+    }
 
-if (!$opt_only_compression) {
-    make_tapetype($device, $compression_enabled);
+    if (!$opt_only_compression) {
+        make_tapetype($compression_enabled);
+    }
 }
+
+Amanda::Util::finish_application();