#! @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
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,
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;
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 {
chomp $line;
$buffer = "";
$self->abort();
- return $params{'finished_cb'}->(undef, $line);
+ return $params{'request_cb'}->(undef, $line);
}
}
};
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 {
"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}) {
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
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");
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 ".
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);
# 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
# 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'});
};
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");
}
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) {
return $clerk->quit(finished_cb => $finished_cb);
};
+
}
main(sub { Amanda::MainLoop::quit(); });