lintian doesn't like orphan packages with uploaders...
[debian/amanda] / server-src / amrestore.pl
index c2ec9d441bfaefcafedded12accb2554755f5964..b9cde45d12add14f8ba59c86389d1ed5776fc6c3 100644 (file)
@@ -1,9 +1,10 @@
 #! @PERL@
-# Copyright (c) 2009, 2010 Zmanda, Inc.  All Rights Reserved.
+# Copyright (c) 2009-2012 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
-# by the Free Software Foundation.
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -30,9 +31,11 @@ use Amanda::Util qw( :constants );
 use Amanda::Changer;
 use Amanda::Constants;
 use Amanda::MainLoop;
+use Amanda::MainLoop qw( :GIOCondition );
 use Amanda::Header;
 use Amanda::Holding;
 use Amanda::Cmdline;
+use Amanda::Tapelist;
 use Amanda::Xfer qw( :constants );
 
 sub usage {
@@ -40,7 +43,7 @@ sub usage {
     print STDERR "$msg\n" if $msg;
     print STDERR <<EOF;
 Usage: amrestore [--config config] [-b blocksize] [-r|-c|-C] [-p] [-h]
-    [-f filenum] [-l label] [-o configoption]*
+    [-f filenum] [-l label] [--exact-match] [-o configoption]*
     {device | [--holding] holdingfile}
     [hostname [diskname [datestamp [hostname [diskname [datestamp ... ]]]]]]"));
 EOF
@@ -55,13 +58,16 @@ Amanda::Util::setup_application("amrestore", "server", $CONTEXT_CMDLINE);
 my $config_overrides = new_config_overrides($#ARGV+1);
 
 my ($opt_config, $opt_blocksize, $opt_raw, $opt_compress, $opt_compress_best,
-    $opt_pipe, $opt_header, $opt_filenum, $opt_label, $opt_holding, $opt_restore_src);
+    $opt_pipe, $opt_header, $opt_filenum, $opt_label, $opt_holding, $opt_restore_src, $opt_exact_match);
+
+debug("Arguments: " . join(' ', @ARGV));
 Getopt::Long::Configure(qw(bundling));
 GetOptions(
     'version' => \&Amanda::Util::version_opt,
     'help|usage|?' => \&usage,
     'config=s' => \$opt_config,
     'holding' => \$opt_holding,
+    'exact-match' => \$opt_exact_match,
     'b=i' => \$opt_blocksize,
     'r' => \$opt_raw,
     'c' => \$opt_compress,
@@ -83,8 +89,9 @@ if (!$opt_holding) {
        if (Amanda::Holding::get_header($opt_restore_src));
 }
 
-my @opt_dumpspecs = Amanda::Cmdline::parse_dumpspecs([@ARGV],
-    $Amanda::Cmdline::CMDLINE_PARSE_DATESTAMP);
+my $cmd_flags = $Amanda::Cmdline::CMDLINE_PARSE_DATESTAMP;
+$cmd_flags |= $Amanda::Cmdline::CMDLINE_EXACT_MATCH if $opt_exact_match;
+my @opt_dumpspecs = Amanda::Cmdline::parse_dumpspecs([@ARGV], $cmd_flags);
 
 usage("Cannot check a label on a holding-disk file")
     if ($opt_holding and $opt_label);
@@ -132,12 +139,16 @@ sub main {
 
     my $dev;
     my $hdr;
+    my $chg;
     my $filenum = $opt_filenum;
     $filenum = 1 if (!$filenum);
     $filenum = 0 + "$filenum"; # convert to integer
+    my %all_filter;
+    my $restore_done;
 
     my $steps = define_steps
-       cb_ref => \$finished_cb;
+       cb_ref => \$finished_cb,
+       finalize => sub { $chg->quit() if defined $chg };
 
     step start => sub {
        # first, return to the original working directory we were started in
@@ -148,7 +159,9 @@ sub main {
        if ($opt_holding) {
            $steps->{'read_header'}->();
        } else {
-           my $chg = Amanda::Changer->new($opt_restore_src);
+           my $tlf = Amanda::Config::config_dir_relative(getconf($CNF_TAPELIST));
+           my $tl = Amanda::Tapelist->new($tlf);
+           $chg = Amanda::Changer->new($opt_restore_src, tapelist => $tl);
            if ($chg->isa("Amanda::Changer::Error")) {
                return failure($chg, $finished_cb);
            }
@@ -163,6 +176,16 @@ sub main {
        return failure($err, $finished_cb) if $err;
 
        $dev = $res->{'device'};
+
+       if ($opt_blocksize) {
+           if ( !$dev->property_set("BLOCK_SIZE", $opt_blocksize)) {
+               return failure($dev->error_or_status, $finished_cb);
+           }
+
+           # re-read the label with the correct blocksize
+           $dev->read_label();
+       }
+
        if ($dev->status != $DEVICE_STATUS_SUCCESS) {
            return failure($dev->error_or_status, $finished_cb);
        }
@@ -175,11 +198,6 @@ sub main {
            return failure($dev->error_or_status, $finished_cb);
        }
 
-       $res->set_label(label => $dev->volume_label,
-                       finished_cb => $steps->{'set_labeled'});
-    };
-
-    step set_labeled => sub {
        if ($opt_label) {
            if ($dev->volume_label ne $opt_label) {
                my $got = $dev->volume_label;
@@ -348,7 +366,41 @@ sub main {
        # write the header to the destination if requested
        if ($opt_header) {
            $hdr->{'blocksize'} = Amanda::Holding::DISK_BLOCK_BYTES;
-           print $dest_fh $hdr->to_string(32768, 32768);
+           $dest_fh->syswrite($hdr->to_string(32768, 32768));
+       }
+
+       # start reading all filter stderr
+       foreach my $filter (@filters) {
+           my $fd = $filter->get_stderr_fd();
+           $fd.="";
+           $fd = int($fd);
+           my $src = Amanda::MainLoop::fd_source($fd,
+                                                 $G_IO_IN|$G_IO_HUP|$G_IO_ERR);
+           my $buffer = "";
+           $all_filter{$src} = 1;
+           $src->set_callback( sub {
+               my $b;
+               my $n_read = POSIX::read($fd, $b, 1);
+               if (!defined $n_read) {
+                   return;
+               } elsif ($n_read == 0) {
+                   delete $all_filter{$src};
+                   $src->remove();
+                   POSIX::close($fd);
+                   if (!%all_filter and $restore_done) {
+                       $finished_cb->();
+                   }
+               } else {
+                   $buffer .= $b;
+                   if ($b eq "\n") {
+                       my $line = $buffer;
+                       print STDERR "filter stderr: $line";
+                       chomp $line;
+                       debug("filter stderr: $line");
+                       $buffer = "";
+                   }
+               }
+           });
        }
        
        my $xfer = Amanda::Xfer->new([ $src, @filters, $dest ]);
@@ -372,13 +424,14 @@ sub main {
        my ($err) = @_;
        return failure($err, $finished_cb) if $err;
 
-       $steps->{'next_file'}->();
+       $steps->{'next_file'}->('extracted');
     };
 
     step next_file => sub {
-       # amrestore does not loop over multiple files when reading from
-       # holding or when outputting to a pipe
-       if ($opt_holding or $opt_pipe) {
+       my ($extracted) = @_;
+       # amrestore does not loop over multiple files when reading from holding
+       # when outputting to a pipe amrestore extracts only the first file
+       if ($opt_holding or ($opt_pipe and $extracted)) {
            return $steps->{'finished'}->();
        }
 
@@ -397,10 +450,12 @@ sub main {
     step quit => sub {
        my ($err) = @_;
        $res = undef;
-
+       $restore_done = 1;
        return failure($err, $finished_cb) if $err;
 
-       $finished_cb->();
+       if (!%all_filter) {
+           $finished_cb->();
+       }
     };
 }
 main(\&Amanda::MainLoop::quit);