lintian doesn't like orphan packages with uploaders...
[debian/amanda] / server-src / amcheckdump.pl
index b0074562c06c1352f1315cdbd1adfa7db498144c..e277e24b9290e00c8ec8f03efbdbc3f9d878be0f 100644 (file)
@@ -1,9 +1,10 @@
 #! @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 
-# 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
@@ -44,7 +45,7 @@ use Amanda::Xfer qw( :constants );
 
 sub usage {
     print <<EOF;
-USAGE: amcheckdump config [ --timestamp|-t timestamp ] [-o configoption]*
+USAGE: amcheckdump [ --timestamp|-t timestamp ] [-o configoption]* <conf>
     amcheckdump validates Amanda dump images by reading them from storage
 volume(s), and verifying archive integrity if the proper tool is locally
 available. amcheckdump does not actually compare the data located in the image
@@ -70,8 +71,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 +98,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 +138,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 +153,7 @@ sub user_request {
                chomp $line;
                $buffer = "";
                $self->abort();
-               return $params{'finished_cb'}->(undef, $line);
+               return $params{'request_cb'}->(undef, $line);
            }
        }
     };
@@ -196,6 +199,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 +223,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 +254,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 +282,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 +343,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 +372,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 +397,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 +419,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 +478,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,18 +512,20 @@ 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) {
            print "All images successfully validated\n";
        } else {
-           print "Some images failed to be correclty validated.\n";
+           print "Some images failed to be correctly validated.\n";
            $exit_code = 1;
        }
 
        return $clerk->quit(finished_cb => $finished_cb);
     };
+
 }
 
 main(sub { Amanda::MainLoop::quit(); });