X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Famvault.pl;h=de47562eec2ca23b6cd58e42f6d5ac52773c348a;hb=HEAD;hp=ebfc627e92ad496e1b2d9867f92e5e289937f259;hpb=011a59f5a54864108a16af570a6b287410597cc2;p=debian%2Famanda diff --git a/server-src/amvault.pl b/server-src/amvault.pl index ebfc627..de47562 100644 --- a/server-src/amvault.pl +++ b/server-src/amvault.pl @@ -1,9 +1,10 @@ #! @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 @@ -21,11 +22,11 @@ use lib '@amperldir@'; use strict; use warnings; -package main::Interactive; +package main::Interactivity; 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; @@ -61,12 +62,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 { @@ -76,7 +77,7 @@ sub user_request { chomp $line; $buffer = ""; $self->abort(); - return $params{'finished_cb'}->(undef, $line); + return $params{'request_cb'}->(undef, $line); } } }; @@ -93,7 +94,7 @@ sub user_request { 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; @@ -108,8 +109,8 @@ use Amanda::Changer qw( :constants ); 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 @@ -141,6 +142,7 @@ sub new { 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 @@ -148,6 +150,47 @@ sub new { }, $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) = @_; @@ -163,7 +206,20 @@ sub run { # 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; @@ -178,7 +234,7 @@ sub setup_src { my $src = $self->{'src'} = {}; # put together a clerk, which of course requires a changer, scan, - # interactive, and feedback + # interactivity, and feedback my $chg = Amanda::Changer->new(); return $self->failure("Error opening source changer: $chg") if $chg->isa('Amanda::Changer::Error'); @@ -186,11 +242,11 @@ sub setup_src { $src->{'seen_labels'} = {}; - $src->{'interactive'} = main::Interactive->new(); + $src->{'interactivity'} = main::Interactivity->new(); $src->{'scan'} = Amanda::Recovery::Scan->new( chg => $src->{'chg'}, - interactive => $src->{'interactive'}); + interactivity => $src->{'interactivity'}); $src->{'clerk'} = Amanda::Recovery::Clerk->new( changer => $src->{'chg'}, @@ -249,7 +305,7 @@ sub setup_src { # 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 @@ -313,7 +369,7 @@ sub plan_cb { $dump->{'dump_timestamp'} . " " . $dump->{'level'} . "\n"; } - $total_kb += $dump->{'kb'}; + $total_kb += int $dump->{'kb'}; } print STDOUT "Total Size: $total_kb KB\n"; @@ -341,17 +397,28 @@ sub plan_cb { sub setup_dst { my $self = shift; my $dst = $self->{'dst'} = {}; + my $tlf = Amanda::Config::config_dir_relative(getconf($CNF_TAPELIST)); + my $tl = Amanda::Tapelist->new($tlf); $dst->{'label'} = undef; $dst->{'tape_num'} = 0; - my $chg = Amanda::Changer->new($self->{'dst_changer'}); + my $chg = Amanda::Changer->new($self->{'dst_changer'}, + tapelist => $tl, + labelstr => getconf($CNF_LABELSTR), + autolabel => $self->{'dst_autolabel'}); return $self->failure("Error opening destination changer: $chg") if $chg->isa('Amanda::Changer::Error'); $dst->{'chg'} = $chg; + my $interactivity = Amanda::Interactivity->new( + name => getconf($CNF_INTERACTIVITY)); + my $scan_name = getconf($CNF_TAPERSCAN); $dst->{'scan'} = Amanda::Taper::Scan->new( + algorithm => $scan_name, changer => $dst->{'chg'}, + interactivity => $interactivity, + tapelist => $tl, labelstr => getconf($CNF_LABELSTR), autolabel => $self->{'dst_autolabel'}); @@ -374,7 +441,7 @@ sub scribe_started { my $xfers_finished = sub { my ($err) = @_; - $self->failure($err) if $err; + return $self->failure($err) if $err; $self->quit(0); }; @@ -461,7 +528,9 @@ sub xfer_dumps { # create and start the transfer $xfer = Amanda::Xfer->new([ $xfer_src, $xfer_dst ]); - $xfer->start($steps->{'handle_xmsg'}); + my $size = 0; + $size = $current->{'dump'}->{'bytes'} if exists $current->{'dump'}->{'bytes'}; + $xfer->start($steps->{'handle_xmsg'}, 0, $size); # count the "threads" running here (clerk and scribe) $n_threads = 2; @@ -564,6 +633,16 @@ sub quit { my $steps = define_steps cb_ref => \$exit_cb; + # the export may not start until we quit the scribe, so wait for it now.. + step check_exporting => sub { + # if we're exporting the final volume, wait for that to complete + if ($self->{'exporting'}) { + $self->{'call_after_export'} = $steps->{'quit_scribe'}; + } else { + $steps->{'quit_scribe'}->(); + } + }; + # we may have several resources to clean up.. step quit_scribe => sub { if ($self->{'cleanup'}{'quit_scribe'}) { @@ -571,28 +650,19 @@ sub quit { $self->{'dst'}{'scribe'}->quit( finished_cb => $steps->{'quit_scribe_finished'}); } else { - $steps->{'check_exporting'}->(); + $steps->{'quit_clerk'}->(); } }; step quit_scribe_finished => sub { + $self->{'dst'}{'scan'}->quit(); my ($err) = @_; if ($err) { print STDERR "$err\n"; $exit_status = 1; } - $steps->{'check_exporting'}->(); - }; - - # the export may not start until we quit the scribe, so wait for it now.. - step check_exporting => sub { - # if we're exporting the final volume, wait for that to complete - if ($self->{'exporting'}) { - $self->{'call_after_export'} = $steps->{'quit_clerk'}; - } else { - $steps->{'quit_clerk'}->(); - } + $steps->{'quit_clerk'}->(); }; step quit_clerk => sub { @@ -616,6 +686,14 @@ sub quit { }; 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 $$"); @@ -726,6 +804,7 @@ sub scribe_notif_log_info { my $self = shift; my %params = @_; + debug("$params{'message'}"); log_add_full($L_INFO, "taper", $params{'message'}); } @@ -739,7 +818,7 @@ sub scribe_notif_tape_done { # exports are going on simultaneously. $self->{'exporting'}++; - my $finished_cb = sub {}; + my $finished_cb = $params{'finished_cb'}; my $steps = define_steps cb_ref => \$finished_cb; @@ -860,6 +939,7 @@ sub usage { 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 @@ -895,6 +975,7 @@ my @config_overrides_opts; 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; @@ -928,6 +1009,7 @@ sub add_autolabel { usage("unknown --autolabel value '$val'"); } +debug("Arguments: " . join(' ', @ARGV)); Getopt::Long::Configure(qw{ bundling }); GetOptions( 'o=s' => sub { @@ -937,6 +1019,7 @@ GetOptions( '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, @@ -950,7 +1033,9 @@ $opt_autolabel->{'empty'} = 1 unless $opt_autolabel_seen; 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'};