#! @PERL@
-# Copyright (c) 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
+# Copyright (c) 2008-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
package Amvault;
use Amanda::Config qw( :getconf config_dir_relative );
-use Amanda::Debug qw( :logging );
+use Amanda::Debug qw( :logging debug );
use Amanda::Xfer qw( :constants );
use Amanda::Header qw( :constants );
use Amanda::MainLoop;
use Amanda::Cmdline;
use Amanda::Paths;
use Amanda::Logfile qw( :logtype_t log_add log_add_full
- log_rename $amanda_log_trace_log make_stats
- match_datestamp match_level );
+ log_rename $amanda_log_trace_log make_stats );
+use Amanda::Util qw ( match_datestamp match_level );
use base qw(
Amanda::Recovery::Clerk::Feedback
exporting => 0, # is an export in progress?
call_after_export => undef, # call this when export complete
config_overrides_opts => $params{'config_overrides_opts'},
+ trace_log_filename => getconf($CNF_LOGDIR) . "/log",
# called when the operation is complete, with the exit
# status
}, $class;
}
+sub run_subprocess {
+ my ($proc, @args) = @_;
+
+ my $pid = POSIX::fork();
+ if ($pid == 0) {
+ my $null = POSIX::open("/dev/null", POSIX::O_RDWR);
+ POSIX::dup2($null, 0);
+ POSIX::dup2($null, 1);
+ POSIX::dup2($null, 2);
+ exec $proc, @args;
+ die "Could not exec $proc: $!";
+ }
+ waitpid($pid, 0);
+ my $s = $? >> 8;
+ debug("$proc exited with code $s: $!");
+}
+
+sub do_amcleanup {
+ my $self = shift;
+
+ return 1 unless -f $self->{'trace_log_filename'};
+
+ # logfiles are still around. First, try an amcleanup -p to see if
+ # the actual processes are already dead
+ debug("runing amcleanup -p");
+ run_subprocess("$sbindir/amcleanup", '-p', $self->{'config_name'},
+ $self->{'config_overrides_opts'});
+
+ return 1 unless -f $self->{'trace_log_filename'};
+
+ return 0;
+}
+
+sub bail_already_running() {
+ my $self = shift;
+ my $msg = "An Amanda process is already running - please run amcleanup manually";
+ print "$msg\n";
+ debug($msg);
+ $self->{'exit_cb'}->(1);
+}
+
sub run {
my $self = shift;
my ($exit_cb) = @_;
# open up a trace log file and put our imprimatur on it, unless dry_runing
if (!$self->{'opt_dry_run'}) {
+ if (!$self->do_amcleanup()) {
+ return $self->bail_already_running();
+ }
log_add($L_INFO, "amvault pid $$");
+
+ # Check we own the log file
+ open(my $tl, "<", $self->{'trace_log_filename'})
+ or die("could not open trace log file '$self->{'trace_log_filename'}': $!");
+ if (<$tl> !~ /^INFO amvault amvault pid $$/) {
+ debug("another amdump raced with this one, and won");
+ close($tl);
+ return $self->bail_already_running();
+ }
+ close($tl);
log_add($L_START, "date " . $self->{'dst_write_timestamp'});
Amanda::Debug::add_amanda_log_handler($amanda_log_trace_log);
$self->{'cleanup'}{'roll_trace_log'} = 1;
# convert the timestamp and level to a dumpspec
my $level = $self->{'fulls_only'}? "0" : undef;
push @dumpspecs, Amanda::Cmdline::dumpspec_t->new(
- undef, undef, $self->{'src_write_timestamp'}, $level, undef);
+ undef, undef, undef, $level, $self->{'src_write_timestamp'});
}
# if we ignored all of the dumpspecs and didn't create any, then dump
$dump->{'dump_timestamp'} . " " .
$dump->{'level'} . "\n";
}
- $total_kb += $dump->{'kb'};
+ $total_kb += int $dump->{'kb'};
}
print STDOUT "Total Size: $total_kb KB\n";
my $xfers_finished = sub {
my ($err) = @_;
- $self->failure($err) if $err;
+ return $self->failure($err) if $err;
$self->quit(0);
};
};
step roll_log => sub {
+ if (defined $self->{'src'}->{'chg'}) {
+ $self->{'src'}->{'chg'}->quit();
+ $self->{'src'}->{'chg'} = undef;
+ }
+ if (defined $self->{'dst'}->{'chg'}) {
+ $self->{'dst'}->{'chg'}->quit();
+ $self->{'dst'}->{'chg'} = undef;
+ }
if ($self->{'cleanup'}{'roll_trace_log'}) {
log_add_full($L_FINISH, "driver", "fake driver finish");
log_add($L_INFO, "pid-done $$");
my $self = shift;
my %params = @_;
+ debug("$params{'message'}");
log_add_full($L_INFO, "taper", $params{'message'});
}
Usage: amvault [-o configoption...] [-q] [--quiet] [-n] [--dry-run]
[--fulls-only] [--export] [--src-timestamp src-timestamp]
+ [--exact-match]
--label-template label-template --dst-changer dst-changer
[--autolabel autolabel-arg...]
config
my $opt_quiet = 0;
my $opt_dry_run = 0;
my $opt_fulls_only = 0;
+my $opt_exact_match = 0;
my $opt_export = 0;
my $opt_autolabel = {};
my $opt_autolabel_seen = 0;
usage("unknown --autolabel value '$val'");
}
+debug("Arguments: " . join(' ', @ARGV));
Getopt::Long::Configure(qw{ bundling });
GetOptions(
'o=s' => sub {
'q|quiet' => \$opt_quiet,
'n|dry-run' => \$opt_dry_run,
'fulls-only' => \$opt_fulls_only,
+ 'exact-match' => \$opt_exact_match,
'export' => \$opt_export,
'label-template=s' => \&set_label_template,
'autolabel=s' => \&add_autolabel,
usage("not enough arguments") unless (@ARGV >= 1);
my $config_name = shift @ARGV;
-my @opt_dumpspecs = parse_dumpspecs(\@ARGV, $CMDLINE_PARSE_DATESTAMP|$CMDLINE_PARSE_LEVEL)
+my $cmd_flags = $CMDLINE_PARSE_DATESTAMP|$CMDLINE_PARSE_LEVEL;
+$cmd_flags |= $CMDLINE_EXACT_MATCH if $opt_exact_match;
+my @opt_dumpspecs = parse_dumpspecs(\@ARGV, $cmd_flags)
if (@ARGV);
usage("no --label-template given") unless $opt_autolabel->{'template'};