# Copyright (c) 2009-2012 Zmanda, Inc. All Rights Reserved.
# Copyright (c) 2013-2016 Carbonite, 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
# 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
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
# Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
use strict;
use warnings;
package Amanda::FetchDump::Message;
use Amanda::Message;
use vars qw( @ISA );
@ISA = qw( Amanda::Message );
sub local_message {
my $self = shift;
if ($self->{'code'} == 3300003) {
return "Reading label '$self->{'label'}' filenum $self->{'filenum'}\n$self->{'header_summary'}";
} elsif ($self->{'code'} == 3300004) {
return "Reading '$self->{'holding_file'}'\n$self->{'header_summary'}";
} elsif ($self->{'code'} == 3300005) {
return "Don't know how to extract the dump";
} elsif ($self->{'code'} == 3300057) {
return "Running a Fetchdump";
} elsif ($self->{'code'} == 3300058) {
return "Failed to fork the FetchDump process";
} elsif ($self->{'code'} == 3300059) {
return "The message filename is '$self->{'message_filename'}'";
} elsif ($self->{'code'} == 3300062) {
return "Exit status: $self->{'exit_status'}";
} elsif ($self->{'code'} == 3300063) {
return "amservice error message: $self->{'amservice_error'}";
} elsif ($self->{'code'} == 3300064) {
return "expecting $self->{'expect'} line, got: $self->{'line'}";
} else {
return "No mesage for code '$self->{'code'}'";
}
}
package Amanda::FetchDump;
use Amanda::Recovery::Clerk;
use parent -norequire, 'Amanda::Recovery::Clerk::Feedback';
use POSIX qw(strftime);
use Amanda::Device qw( :constants );
use Amanda::Debug qw( :logging );
use Amanda::Config qw( :init :getconf config_dir_relative );
use Amanda::Util qw( :constants :quoting );
use Amanda::Constants;
use Amanda::MainLoop qw( :GIOCondition );
use Amanda::Cmdline;
use Amanda::Restore;
use Amanda::Extract;
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{'is_tty'} = -t STDERR;
my $logdir = $self->{'logdir'} = config_dir_relative(getconf($CNF_LOGDIR));
my @now = localtime;
my $timestamp = strftime "%Y%m%d%H%M%S", @now;
$self->{'pid'} = $$;
$self->{'timestamp'} = Amanda::Logfile::make_logname("fetchdump", $timestamp);
$self->{'trace_log_filename'} = Amanda::Logfile::get_logname();
debug("beginning trace log: $self->{'trace_log_filename'}");
$self->{'message_filename'} = "fetchdump.$timestamp";
$self->{'message_pathname'} = "$logdir/fetchdump.$timestamp";
return $self;
# must return undef on error
# must call user_message to print error
}
sub run {
my $self = shift;
my %params = @_;
$self->{'target'} = $params{'target'};
$self->{'extract-client'} = $params{'extract-client'};
$self->{'include-file'} = $params{'include-file'};
$self->{'include-list'} = $params{'include-list'};
$self->{'include-list-glob'} = $params{'include-list-glob'};
$self->{'exclude-file'} = $params{'exclude-file'};
$self->{'exclude-list'} = $params{'exclude-list'};
$self->{'exclude-list-glob'} = $params{'exclude-list-glob'};
$self->{'prev-level'} = $params{'prev-level'};
$self->{'next-level'} = $params{'next-level'};
($self->{'restore'}, my $result_message) = Amanda::Restore->new(
message_pathname => $self->{'message_pathname'});
if (@$result_message) {
foreach my $message (@$result_message) {
$self->user_message($message);
}
return $params{'finished_cb'}->(1);
}
$self->{'message_filename'} = $self->{'restore'}->{'message_filename'};
$self->{'restore'}->restore(
'application_property' => $params{'application_property'},
'assume' => $params{'assume'},
'chdir' => $params{'chdir'},
'client-decompress' => $params{'client-decompress'},
'client-decrypt' => $params{'client-decrypt'},
'compress' => $params{'compress'},
'compress-best' => $params{'compress-best'},
'data-path' => $params{'data-path'},
'decompress' => $params{'decompress'},
'decrypt' => $params{'decrypt'},
'device' => $params{'device'},
'target' => $params{'target'},
'dumpspecs' => $params{'dumpspecs'},
'exact-match' => $params{'exact-match'},
'extract' => $params{'extract'},
'extract-client' => $params{'extract-client'},
'header' => $params{'header'},
'header-fd' => $params{'header-fd'},
'header-file' => $params{'header-file'},
'init' => $params{'init'},
'leave' => $params{'leave'},
'no-reassembly' => $params{'no-reassembly'},
'pipe-fd' => $params{'pipe-fd'} ? 1 : undef,
'restore' => $params{'restore'},
'server-decompress' => $params{'server-decompress'},
'server-decrypt' => $params{'server-decrypt'},
'finished_cb' => $params{'finished_cb'},
'interactivity' => $params{'interactivity'},
'reserve-tapes' => $params{'reserve-tapes'},
'release-tapes' => $params{'release-tapes'},
'feedback' => $self);
}
sub set_feedback {
my $self = shift;
my %params = @_;
$self->{'chg'} = $params{'chg'} if exists $params{'chg'};
$self->{'dev_name'} = $params{'dev_name'} if exists $params{'dev_name'};
return $self;
}
sub clerk_notif_part {
my $self = shift;
my ($label, $filenum, $header) = @_;
$self->user_message(Amanda::FetchDump::Message->new(
source_filename => __FILE__,
source_line => __LINE__,
code => 3300003,
severity => $Amanda::Message::INFO,
label => $label,
filenum => $filenum,
header_summary => $header->summary()));
}
sub clerk_notif_holding {
my $self = shift;
my ($filename, $header) = @_;
# this used to give the fd from which the holding file was being read.. why??
$self->user_message(Amanda::FetchDump::Message->new(
source_filename => __FILE__,
source_line => __LINE__,
code => 3300004,
severity => $Amanda::Message::INFO,
holding_file => $filename,
header_summary => $header->summary()));
}
sub start_read_dar
{
my $self = shift;
my $xfer_dest = shift;
my $cb_data = shift;
my $cb_done = shift;
my $text = shift;
my $fd = $xfer_dest->get_dar_fd();
$fd.="";
$fd = int($fd);
my $src = Amanda::MainLoop::fd_source($fd,
$G_IO_IN|$G_IO_HUP|$G_IO_ERR);
my $buffer = "";
$self->{'fetchdump'}->{'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 $self->{'fetchdump'}->{'all_filter'}->{$src};
$cb_data->("DAR -1:0");
$src->remove();
POSIX::close($fd);
if (!%{$self->{'fetchdump'}->{'all_filter'}} and $self->{'recovery_done'}) {
$cb_done->();
}
} else {
$buffer .= $b;
if ($b eq "\n") {
my $line = $buffer;
chomp $line;
if (length($line) > 1) {
$cb_data->($line);
}
$buffer = "";
}
}
});
}
sub user_message {
my $self = shift;
my $message = shift;
if ($message->{'code'} == 4900000) { #SIZE
if ($self->{'is_tty'}) {
print STDERR "\r$message ";
$self->{'last_is_size'} = 1;
} else {
print STDERR "READ SIZE: $message\n";
}
} elsif ($message->{'code'} == 4900012) { #READ SIZE
print STDERR "\r$message \n";
} else {
if ($message->{'code'} == 3300003 || $message->{'code'} == 3300004) {
print STDERR "\n";
}
print STDERR "\n" if $self->{'is_tty'} and $self->{'last_is_size'};
print STDERR "$message\n";
$self->{'last_is_size'} = 0;
if ($message->{'code'} == 4900002 && !$self->{'assume'}) {
print STDERR "Press enter when ready\n";
my $resp = <STDIN>;
}
}
}
1;