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
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 {
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 $check_done;
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");
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 $check_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([ $xfer_src, @filters, $xfer_dest ]);
$xfer->start($steps->{'handle_xmsg'});
$clerk->start_recovery(
if ($err) {
$exit_code = 1;
print STDERR $err, "\n";
- return $clerk->quit(finished_cb => $finished_cb);
+ return $clerk->quit(finished_cb => $steps->{'quit1'}) if defined $clerk;;
+ return $steps->{'quit1'}->();
}
if ($all_success) {
$exit_code = 1;
}
- return $clerk->quit(finished_cb => $finished_cb);
+ return $clerk->quit(finished_cb => $steps->{'quit1'});
};
+
+ step quit1 => sub {
+ $check_done = 1;
+
+ if (!%all_filter) {
+ $finished_cb->();
+ }
+ }
}
main(sub { Amanda::MainLoop::quit(); });