Merge tag 'upstream/3.3.2'
[debian/amanda] / server-src / amcheckdump.pl
index 95c7fa3426f00ac5b3dfaf20eda15562fb65d163..92382e909f96ec73bf16307b8a000f72a8a87741 100644 (file)
@@ -1,5 +1,5 @@
 #! @PERL@
-# Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
+# Copyright (c) 2007-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 
@@ -70,8 +70,10 @@ my $opt_timestamp;
 my $opt_verbose = 0;
 my $config_overrides = new_config_overrides($#ARGV+1);
 
+debug("Arguments: " . join(' ', @ARGV));
 Getopt::Long::Configure(qw(bundling));
 GetOptions(
+    'version' => \&Amanda::Util::version_opt,
     'timestamp|t=s' => \$opt_timestamp,
     'verbose|v'     => \$opt_verbose,
     'help|usage|?'  => \&usage,
@@ -95,12 +97,12 @@ if ($cfgerr_level >= $CFGERR_WARNINGS) {
 
 Amanda::Util::finish_setup($RUNNING_AS_DUMPUSER);
 
-# Interactive package
-package Amanda::Interactive::amcheckdump;
+# Interactivity package
+package Amanda::Interactivity::amcheckdump;
 use POSIX qw( :errno_h );
 use Amanda::MainLoop qw( :GIOCondition );
 use vars qw( @ISA );
-@ISA = qw( Amanda::Interactive );
+@ISA = qw( Amanda::Interactivity );
 
 sub new {
     my $class = shift;
@@ -135,12 +137,12 @@ sub user_request {
        if (!defined $n_read) {
            return if ($! == EINTR);
            $self->abort();
-           return $params{'finished_cb'}->(
+           return $params{'request_cb'}->(
                Amanda::Changer::Error->new('fatal',
                        message => "Fail to read from stdin"));
        } elsif ($n_read == 0) {
            $self->abort();
-           return $params{'finished_cb'}->(
+           return $params{'request_cb'}->(
                Amanda::Changer::Error->new('fatal',
                        message => "Aborted by user"));
        } else {
@@ -150,7 +152,7 @@ sub user_request {
                chomp $line;
                $buffer = "";
                $self->abort();
-               return $params{'finished_cb'}->(undef, $line);
+               return $params{'request_cb'}->(undef, $line);
            }
        }
     };
@@ -196,6 +198,8 @@ sub clerk_notif_holding {
 
 package main;
 
+use Amanda::MainLoop qw( :GIOCondition );
+
 # Given a dumpfile_t, figure out the command line to validate, specified
 # as an argv array
 sub find_validation_command {
@@ -218,7 +222,7 @@ sub find_validation_command {
             "TAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -tf -) ],
             "GTAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -tf -) ],
             "GNUTAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -tf -) ],
-            "SMBCLIENT" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -tf -) ],
+            "SMBCLIENT" => [ $Amanda::Constants::GNUTAR, qw(-tf -) ],
            "PKZIP" => undef,
         );
        if (!exists $validation_programs{$program}) {
@@ -249,16 +253,22 @@ sub main {
 
     my $tapelist;
     my $chg;
-    my $interactive;
+    my $interactivity;
     my $scan;
     my $clerk;
     my $plan;
     my $timestamp;
     my $all_success = 1;
     my @xfer_errs;
+    my %all_filter;
+    my $current_dump;
+    my $recovery_done;
+    my %recovery_params;
 
     my $steps = define_steps
-       cb_ref => \$finished_cb;
+       cb_ref => \$finished_cb,
+       finalize => sub { $scan->quit() if defined $scan;
+                         $chg->quit() if defined $chg    };
 
     step start => sub {
        # set up the tapelist
@@ -271,17 +281,17 @@ sub main {
            unless defined $opt_timestamp;
 
        # make an interactivity plugin
-       $interactive = Amanda::Interactive::amcheckdump->new();
+       $interactivity = Amanda::Interactivity::amcheckdump->new();
 
        # make a changer
-       $chg = Amanda::Changer->new();
+       $chg = Amanda::Changer->new(undef, tapelist => $tapelist);
        return $steps->{'quit'}->($chg)
            if $chg->isa("Amanda::Changer::Error");
 
        # make a scan
        $scan = Amanda::Recovery::Scan->new(
                            chg => $chg,
-                           interactive => $interactive);
+                           interactivity => $interactivity);
        return $steps->{'quit'}->($scan)
            if $scan->isa("Amanda::Changer::Error");
 
@@ -332,6 +342,10 @@ sub main {
 
     step check_dumpfile => sub {
        my ($dump) = @_;
+       $current_dump = $dump;
+
+       $recovery_done = 0;
+       %recovery_params = ();
 
        print "Validating image " . $dump->{hostname} . ":" .
            $dump->{diskname} . " dumped " . $dump->{dump_timestamp} . " level ".
@@ -357,11 +371,11 @@ sub main {
            if ($hdr->{'srv_encrypt'}) {
                push @filters,
                    Amanda::Xfer::Filter::Process->new(
-                       [ $hdr->{'srv_encrypt'}, $hdr->{'srv_decrypt_opt'} ], 0, 0);
+                       [ $hdr->{'srv_encrypt'}, $hdr->{'srv_decrypt_opt'} ], 0);
            } elsif ($hdr->{'clnt_encrypt'}) {
                push @filters,
                    Amanda::Xfer::Filter::Process->new(
-                       [ $hdr->{'clnt_encrypt'}, $hdr->{'clnt_decrypt_opt'} ], 0, 0);
+                       [ $hdr->{'clnt_encrypt'}, $hdr->{'clnt_decrypt_opt'} ], 0);
            } else {
                return failure("could not decrypt encrypted dump: no program specified",
                            $finished_cb);
@@ -382,17 +396,17 @@ sub main {
                # TODO: this assumes that srvcompprog takes "-d" to decrypt
                push @filters,
                    Amanda::Xfer::Filter::Process->new(
-                       [ $hdr->{'srvcompprog'}, "-d" ], 0, 0);
+                       [ $hdr->{'srvcompprog'}, "-d" ], 0);
            } elsif ($hdr->{'clntcompprog'}) {
                # TODO: this assumes that clntcompprog takes "-d" to decrypt
                push @filters,
                    Amanda::Xfer::Filter::Process->new(
-                       [ $hdr->{'clntcompprog'}, "-d" ], 0, 0);
+                       [ $hdr->{'clntcompprog'}, "-d" ], 0);
            } else {
                push @filters,
                    Amanda::Xfer::Filter::Process->new(
                        [ $Amanda::Constants::UNCOMPRESS_PATH,
-                         $Amanda::Constants::UNCOMPRESS_OPT ], 0, 0);
+                         $Amanda::Constants::UNCOMPRESS_OPT ], 0);
            }
 
            # adjust the header
@@ -404,14 +418,48 @@ sub main {
        # we need to throw out its stdout
        my $argv = find_validation_command($hdr);
        if (defined $argv) {
-           push @filters, Amanda::Xfer::Filter::Process->new($argv, 0, 0);
+           push @filters, Amanda::Xfer::Filter::Process->new($argv, 0);
        }
 
        # we always throw out stdout
        my $xfer_dest = Amanda::Xfer::Dest::Null->new(0);
 
+       # 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 $recovery_done) {
+                       $steps->{'filter_done'}->();
+                   }
+               } 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([ $xfer_src, @filters, $xfer_dest ]);
-       $xfer->start($steps->{'handle_xmsg'});
+       $xfer->start($steps->{'handle_xmsg'}, 0, $current_dump->{'bytes'});
        $clerk->start_recovery(
            xfer => $xfer,
            recovery_cb => $steps->{'recovery_cb'});
@@ -429,12 +477,17 @@ sub main {
     };
 
     step recovery_cb => sub {
-       my %params = @_;
+       %recovery_params = @_;
+       $recovery_done = 1;
+
+       $steps->{'filter_done'}->() if !%all_filter;
+    };
 
+    step filter_done => sub {
        # distinguish device errors from validation errors
-       if (@{$params{'errors'}}) {
+       if (@{$recovery_params{'errors'}}) {
            print STDERR "While reading from volumes:\n";
-           print STDERR "$_\n" for @{$params{'errors'}};
+           print STDERR "$_\n" for @{$recovery_params{'errors'}};
            return $steps->{'quit'}->("validation aborted");
        }
 
@@ -458,7 +511,8 @@ sub main {
        if ($err) {
            $exit_code = 1;
            print STDERR $err, "\n";
-           return $clerk->quit(finished_cb => $finished_cb);
+           return $clerk->quit(finished_cb => $finished_cb) if defined $clerk;
+           return $finished_cb->();
        }
 
        if ($all_success) {
@@ -470,6 +524,7 @@ sub main {
 
        return $clerk->quit(finished_cb => $finished_cb);
     };
+
 }
 
 main(sub { Amanda::MainLoop::quit(); });