X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Famfetchdump.pl;fp=server-src%2Famfetchdump.pl;h=df12800e7f605a8376dd06656c0ea5a29854f0b7;hb=d28952249e392eb31bc8eecc53f6c477f30c617b;hp=652f619b9f21be9daedb783f80a053cf81ace073;hpb=949b8910a5e23c4285d0b1aedacfc82a14dc97a5;p=debian%2Famanda diff --git a/server-src/amfetchdump.pl b/server-src/amfetchdump.pl index 652f619..df12800 100644 --- a/server-src/amfetchdump.pl +++ b/server-src/amfetchdump.pl @@ -1,9 +1,10 @@ #! @PERL@ # Copyright (c) 2009-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 @@ -22,6 +23,9 @@ use strict; use warnings; use Getopt::Long; +use File::Basename; +use XML::Simple; +use IPC::Open3; use Amanda::Device qw( :constants ); use Amanda::Debug qw( :logging ); @@ -37,6 +41,7 @@ use Amanda::Xfer qw( :constants ); use Amanda::Recovery::Planner; use Amanda::Recovery::Clerk; use Amanda::Recovery::Scan; +use Amanda::Extract; # Interactivity package package Amanda::Interactivity::amfetchdump; @@ -113,10 +118,12 @@ sub usage { my ($msg) = @_; print STDERR < \$opt_decompress, 'server-decompress' => \$opt_server_decompress, 'client-decompress' => \$opt_client_decompress, + 'extract' => \$opt_extract, + 'directory=s' => \$opt_directory, + 'data-path=s' => \$opt_data_path, + 'application-property=s' => \%application_property, + 'exact-match' => \$opt_exact_match, 'b=s' => \$opt_blocksize, 'd=s' => \$opt_device, 'O=s' => \$opt_chdir, @@ -187,6 +201,10 @@ usage("-h, --header-file, and --header-fd are mutually incompatible") if (($opt_header and ($opt_header_file or $opt_header_fd)) or ($opt_header_file and $opt_header_fd)); + $opt_data_path = lc($opt_data_path) if defined ($opt_data_path); +usage("--data_path must be 'amanda' or 'directtcp'") + if (defined $opt_data_path and $opt_data_path ne 'directtcp' and $opt_data_path ne 'amanda'); + if (defined $opt_leave) { if (defined $opt_decrypt and $opt_decrypt) { print STDERR "-l is incompatible with --decrypt\n"; @@ -214,6 +232,38 @@ if (defined $opt_leave) { } } +if (( defined $opt_directory and !defined $opt_extract) or + (!defined $opt_directory and defined $opt_extract)) { + print STDERR "Both --directorty and --extract must be set\n"; + usage(); +} +if (defined $opt_directory and defined $opt_extract) { + $opt_decrypt = 1; + if (defined $opt_server_decrypt or defined $opt_client_decrypt) { + print STDERR "--server_decrypt or --client-decrypt is incompatible with --extract\n"; + usage(); + } + $opt_decompress = 1; + if (defined $opt_server_decompress || defined $opt_client_decompress) { + print STDERR "--server-decompress r --client-decompress is incompatible with --extract\n"; + usage(); + } + if (defined($opt_leave) + + defined($opt_compress) + + defined($opt_compress_best)) { + print STDERR "Can't use -l -c or -C with --extract\n"; + usage(); + } + if (defined $opt_pipe) { + print STDERR "--pipe is incompatible with --extract\n"; + usage(); + } + if (defined $opt_header) { + print STDERR "--header is incompatible with --extract\n"; + usage(); + } +} + if (defined($opt_decrypt) + defined($opt_server_decrypt) + defined($opt_client_decrypt) > 1) { @@ -260,8 +310,10 @@ $decompress = $ONLY_SERVER if defined $opt_server_decompress; $decompress = $ONLY_CLIENT if defined $opt_client_decompress; usage("must specify at least a hostname") unless @ARGV; -@opt_dumpspecs = Amanda::Cmdline::parse_dumpspecs([@ARGV], - $Amanda::Cmdline::CMDLINE_PARSE_DATESTAMP | $Amanda::Cmdline::CMDLINE_PARSE_LEVEL); +my $cmd_flags = $Amanda::Cmdline::CMDLINE_PARSE_DATESTAMP | + $Amanda::Cmdline::CMDLINE_PARSE_LEVEL; +$cmd_flags |= $Amanda::Cmdline::CMDLINE_EXACT_MATCH if $opt_exact_match; +@opt_dumpspecs = Amanda::Cmdline::parse_dumpspecs([@ARGV], $cmd_flags); set_config_overrides($config_overrides); config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config); @@ -277,9 +329,11 @@ Amanda::Util::finish_setup($RUNNING_AS_DUMPUSER); my $exit_status = 0; my $clerk; +use Data::Dumper; sub failure { my ($msg, $finished_cb) = @_; print STDERR "ERROR: $msg\n"; + debug("FAILURE: $msg"); $exit_status = 1; if ($clerk) { $clerk->quit(finished_cb => sub { @@ -338,6 +392,8 @@ sub main { my $timer; my $is_tty; my $delay; + my $directtcp = 0; + my @directtcp_command; my $steps = define_steps cb_ref => \$finished_cb; @@ -446,10 +502,105 @@ sub main { my ($errs, $hdr, $xfer_src, $directtcp_supported) = @_; return failure(join("; ", @$errs), $finished_cb) if $errs; + my $dle_str = $hdr->{'dle_str'}; + my $p1 = XML::Simple->new(); + my $dle = $p1->XMLin($dle_str); + # and set up the destination.. my $dest_fh; - if ($opt_pipe) { + my $xfer_dest; + my @filters; + + if (defined $opt_data_path and $opt_data_path eq 'directtcp' and !$directtcp_supported) { + return failure("The device can't do directtcp", $finished_cb); + } + $directtcp_supported = 0 if defined $opt_data_path and $opt_data_path eq 'amanda'; + if ($opt_extract) { + my $program = uc(basename($hdr->{program})); + my @argv; + if ($program ne "APPLICATION") { + $directtcp_supported = 0; + my %validation_programs = ( + "STAR" => [ $Amanda::Constants::STAR, qw(-x -f -) ], + "DUMP" => [ $Amanda::Constants::RESTORE, qw(xbf 2 -) ], + "VDUMP" => [ $Amanda::Constants::VRESTORE, qw(xf -) ], + "VXDUMP" => [ $Amanda::Constants::VXRESTORE, qw(xbf 2 -) ], + "XFSDUMP" => [ $Amanda::Constants::XFSRESTORE, qw(-v silent) ], + "TAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -xf -) ], + "GTAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -xf -) ], + "GNUTAR" => [ $Amanda::Constants::GNUTAR, qw(--ignore-zeros -xf -) ], + "SMBCLIENT" => [ $Amanda::Constants::GNUTAR, qw(-xf -) ], + "PKZIP" => undef, + ); + if (!exists $validation_programs{$program}) { + return failure("Unknown program '$program' in header; no validation to perform", + $finished_cb); + } + @argv = $validation_programs{$program}; + } else { + if (!defined $hdr->{application}) { + return failure("Application not set", $finished_cb); + } + my $program_path = $Amanda::Paths::APPLICATION_DIR . "/" . + $hdr->{application}; + if (!-x $program_path) { + return failure("Application '" . $hdr->{application} . + "($program_path)' not available on the server", + $finished_cb); + } + my %bsu_argv; + $bsu_argv{'application'} = $hdr->{application}; + $bsu_argv{'config'} = $opt_config; + $bsu_argv{'host'} = $hdr->{'name'}; + $bsu_argv{'disk'} = $hdr->{'disk'}; + $bsu_argv{'device'} = $dle->{'diskdevice'} if defined $dle->{'diskdevice'}; + my ($bsu, $err) = Amanda::Extract::BSU(%bsu_argv); + if (defined $opt_data_path and $opt_data_path eq 'directtcp' and + !$bsu->{'data-path-directtcp'}) { + return failure("The application can't do directtcp", $finished_cb); + } + if ($directtcp_supported and !$bsu->{'data-path-directtcp'}) { + # application do not support directtcp + $directtcp_supported = 0; + } + + push @argv, $program_path, "restore"; + push @argv, "--config", $opt_config; + push @argv, "--host", $hdr->{'name'}; + push @argv, "--disk", $hdr->{'disk'}; + push @argv, "--device", $dle->{'diskdevice'} if defined ($dle->{'diskdevice'}); + push @argv, "--level", $hdr->{'dumplevel'}; + push @argv, "--directory", $opt_directory; + + # add application_property + while (my($name, $value) = each(%application_property)) { + push @argv, "--".$name, $value if $value; + } + + #merge property from header; + while (my($name, $value) = each (%{$dle->{'backup-program'}->{'property'}})) { + if (!exists $application_property{$name}) { + push @argv, "--".$name, $value->{'value'}; + } + } + + } + $directtcp = $directtcp_supported; + if ($directtcp_supported) { + $xfer_dest = Amanda::Xfer::Dest::DirectTCPListen->new(); + @directtcp_command = @argv; + } else { + # set up the extraction command as a filter element, since + # we need its stderr. + debug("Running: ". join(' ',@argv)); + push @filters, Amanda::Xfer::Filter::Process->new(\@argv, 0); + + $dest_fh = \*STDOUT; + $xfer_dest = Amanda::Xfer::Dest::Fd->new($dest_fh); + } + } elsif ($opt_pipe) { $dest_fh = \*STDOUT; + $xfer_dest = Amanda::Xfer::Dest::Fd->new($dest_fh); } else { my $filename = sprintf("%s.%s.%s.%d", $hdr->{'name'}, @@ -469,6 +620,7 @@ sub main { if (!open($dest_fh, ">", $filename)) { return failure("Could not open '$filename' for writing: $!", $finished_cb); } + $xfer_dest = Amanda::Xfer::Dest::Fd->new($dest_fh); } $timer = Amanda::MainLoop::timeout_source($delay); @@ -481,12 +633,7 @@ sub main { } }); - my $xfer_dest = Amanda::Xfer::Dest::Fd->new($dest_fh); - - my $dle = $hdr->get_dle(); - # set up any filters that need to be applied; decryption first - my @filters; if ($hdr->{'encrypted'} and (($hdr->{'srv_encrypt'} and ($decrypt == $ALWAYS || $decrypt == $ONLY_SERVER)) || ($hdr->{'clnt_encrypt'} and ($decrypt == $ALWAYS || $decrypt == $ONLY_CLIENT)))) { @@ -514,10 +661,10 @@ sub main { if ($hdr->{'compressed'} and not $opt_compress and (($hdr->{'srvcompprog'} and ($decompress == $ALWAYS || $decompress == $ONLY_SERVER)) || ($hdr->{'clntcompprog'} and ($decompress == $ALWAYS || $decompress == $ONLY_CLIENT)) || - ($dle->{'compress'} == $Amanda::Config::COMP_SERVER_FAST and ($decompress == $ALWAYS || $decompress == $ONLY_SERVER)) || - ($dle->{'compress'} == $Amanda::Config::COMP_SERVER_BEST and ($decompress == $ALWAYS || $decompress == $ONLY_SERVER)) || - ($dle->{'compress'} == $Amanda::Config::COMP_FAST and ($decompress == $ALWAYS || $decompress == $ONLY_CLIENT)) || - ($dle->{'compress'} == $Amanda::Config::COMP_BEST and ($decompress == $ALWAYS || $decompress == $ONLY_CLIENT)))) { + ($dle->{'compress'} and $dle->{'compress'} eq "SERVER-FAST" and ($decompress == $ALWAYS || $decompress == $ONLY_SERVER)) || + ($dle->{'compress'} and $dle->{'compress'} eq "SERVER-BEST" and ($decompress == $ALWAYS || $decompress == $ONLY_SERVER)) || + ($dle->{'compress'} and $dle->{'compress'} eq "FAST" and ($decompress == $ALWAYS || $decompress == $ONLY_CLIENT)) || + ($dle->{'compress'} and $dle->{'compress'} eq "BEST" and ($decompress == $ALWAYS || $decompress == $ONLY_CLIENT)))) { # need to uncompress this file if ($hdr->{'encrypted'}) { print "Not decompressing because the backup image is not decrypted\n"; @@ -598,20 +745,74 @@ sub main { $buffer .= $b; if ($b eq "\n") { my $line = $buffer; - print STDERR "filter stderr: $line"; chomp $line; - debug("filter stderr: $line"); + if (length($line) > 1) { + print STDERR "filter stderr: $line\n"; + debug("filter stderr: $line"); + } $buffer = ""; } } }); } - my $xfer = Amanda::Xfer->new([ $xfer_src, @filters, $xfer_dest ]); + my $xfer; + if (@filters) { + $xfer = Amanda::Xfer->new([ $xfer_src, @filters, $xfer_dest ]); + } else { + $xfer = Amanda::Xfer->new([ $xfer_src, $xfer_dest ]); + } $xfer->start($steps->{'handle_xmsg'}, 0, $current_dump->{'bytes'}); $clerk->start_recovery( xfer => $xfer, recovery_cb => $steps->{'recovery_cb'}); + if ($directtcp) { + my $addr = $xfer_dest->get_addrs(); + push @directtcp_command, "--data-path", "DIRECTTCP"; + push @directtcp_command, "--direct-tcp", "$addr->[0]->[0]:$addr->[0]->[1]"; + debug("Running: ". join(' ', @directtcp_command)); + + my ($wtr, $rdr); + my $err = Symbol::gensym; + my $amndmp_pid = open3($wtr, $rdr, $err, @directtcp_command); + $amndmp_pid = $amndmp_pid; + my $file_to_close = 2; + my $amndmp_stdout_src = Amanda::MainLoop::fd_source($rdr, + $G_IO_IN|$G_IO_HUP|$G_IO_ERR); + my $amndmp_stderr_src = Amanda::MainLoop::fd_source($err, + $G_IO_IN|$G_IO_HUP|$G_IO_ERR); + + $amndmp_stdout_src->set_callback( sub { + my $line = <$rdr>; + if (!defined $line) { + $file_to_close--; + $amndmp_stdout_src->remove(); + if ($file_to_close == 0) { + #abort the xfer + $xfer->cancel() if $xfer->get_status != $XFER_DONE; + } + return; + } + chomp $line; + debug("amndmp stdout: $line"); + print "$line\n"; + }); + $amndmp_stderr_src->set_callback( sub { + my $line = <$err>; + if (!defined $line) { + $file_to_close--; + $amndmp_stderr_src->remove(); + if ($file_to_close == 0) { + #abort the xfer + $xfer->cancel() if $xfer->get_status != $XFER_DONE; + } + return; + } + chomp $line; + debug("amndmp stderr: $line"); + print STDERR "$line\n"; + }); + } }; step handle_xmsg => sub {