X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=application-src%2Famsamba.pl;h=db5c47516595e06846f2a1dd39156feb11ec2a4f;hb=d28952249e392eb31bc8eecc53f6c477f30c617b;hp=ba45e274e190abd639a1039c194d52c9711cc50e;hpb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;p=debian%2Famanda diff --git a/application-src/amsamba.pl b/application-src/amsamba.pl index ba45e27..db5c475 100644 --- a/application-src/amsamba.pl +++ b/application-src/amsamba.pl @@ -1,9 +1,10 @@ #!@PERL@ -# Copyright (c) 2005-2008 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 @@ -14,11 +15,12 @@ # 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 +# Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com use lib '@amperldir@'; use strict; +use warnings; use Getopt::Long; package Amanda::Application::Amsamba; @@ -30,16 +32,18 @@ use IPC::Open3; use Sys::Hostname; use Symbol; use IO::Handle; +use MIME::Base64 (); use Amanda::Constants; use Amanda::Config qw( :init :getconf config_dir_relative ); use Amanda::Debug qw( :logging ); use Amanda::Paths; use Amanda::Util qw( :constants :quoting); +use Amanda::MainLoop qw( :GIOCondition ); sub new { my $class = shift; - my ($config, $host, $disk, $device, $level, $index, $message, $collection, $record, $calcsize, $gnutar_path, $smbclient_path, $amandapass, $exclude_file, $exclude_list, $exclude_optional, $include_file, $include_list, $include_optional, $recover_mode) = @_; - my $self = $class->SUPER::new(); + my ($config, $host, $disk, $device, $level, $index, $message, $collection, $record, $calcsize, $gnutar_path, $smbclient_path, $amandapass, $exclude_file, $exclude_list, $exclude_optional, $include_file, $include_list, $include_optional, $recover_mode, $allow_anonymous, $directory) = @_; + my $self = $class->SUPER::new($config); if (defined $gnutar_path) { $self->{gnutar} = $gnutar_path; @@ -52,15 +56,23 @@ sub new { $self->{smbclient} = $Amanda::Constants::SAMBA_CLIENT; } if (defined $amandapass) { - $self->{amandapass} = $amandapass; + $self->{amandapass} = config_dir_relative($amandapass); } else { - $self->{amandapass} = "$Amanda::Paths::sysconfdir/amandapass"; + $self->{amandapass} = "$Amanda::Paths::CONFIG_DIR/amandapass"; } $self->{config} = $config; $self->{host} = $host; - $self->{disk} = $disk; - $self->{device} = $device; + if (defined $disk) { + $self->{disk} = $disk; + } else { + $self->{disk} = $device; + } + if (defined $device) { + $self->{device} = $device; + } else { + $self->{device} = $disk; + } $self->{level} = [ @{$level} ]; $self->{index} = $index; $self->{message} = $message; @@ -74,6 +86,8 @@ sub new { $self->{include_list} = [ @{$include_list} ]; $self->{include_optional} = $include_optional; $self->{recover_mode} = $recover_mode; + $self->{allow_anonymous} = $allow_anonymous; + $self->{directory} = $directory; return $self; } @@ -86,13 +100,13 @@ sub new { #on exit: # $self->{exclude} # $self->{include} +# $self->{include_filename} sub validate_inexclude { my $self = shift; if ($#{$self->{exclude_file}} + $#{$self->{exclude_list}} >= -1 && $#{$self->{include_file}} + $#{$self->{include_list}} >= -1) { - $self->print_to_server_and_die($self->{action}, - "Can't have both include and exclude", + $self->print_to_server_and_die("Can't have both include and exclude", $Amanda::Script_App::ERROR); } @@ -102,8 +116,7 @@ sub validate_inexclude { foreach my $file (@{$self->{exclude_list}}) { if (!open(FF, $file)) { if ($self->{action} eq 'check' && !$self->{exclude_optional}) { - $self->print_to_server($self->{action}, - "Open of '$file' failed: $!", + $self->print_to_server("Open of '$file' failed: $!", $Amanda::Script_App::ERROR); } next; @@ -114,53 +127,133 @@ sub validate_inexclude { } close(FF); } - if ($#{$self->{include_file}} >= 0) { - $self->{include} = [ @{$self->{include_file}} ]; - } - foreach my $file (@{$self->{include_list}}) { - if (open(FF, $file)) { - if ($self->{action} eq 'check') { - $self->print_to_server($self->{action}, - "Open of '$file' failed: $!", - $Amanda::Script_App::ERROR); + + if ($self->{action} eq "restore" and defined $self->{'include_list'}) { + # put all include in a single file $self->{'include_filename'} + $self->{'include_filename'} = "$AMANDA_TMPDIR/amsamba.$$.include"; + open INC_FILE, ">$self->{'include_filename'}"; + if ($#{$self->{include_file}} >= 0) { + print INC_FILE "$self->{include_file}\n"; + } + foreach my $file (@{$self->{include_list}}) { + if (!open(FF, $file)) { + if ($self->{action} eq 'check' && !$self->{include_optional}) { + $self->print_to_server("Open of '$file' failed: $!", + $Amanda::Script_App::ERROR); + } + next; } - next; + while () { + if (defined $self->{'subdir'}) { + $_ =~ s/^\./$self->{'subdir'}/; + } + print INC_FILE; + } + close(FF); } - while () { - chomp; - push @{$self->{include}}, $_; + + # add command line include for amrestore + for(my $i=1;defined $ARGV[$i]; $i++) { + my $param = $ARGV[$i]; + $param =~ /^(.*)$/; + $_ = $1; + if (defined $self->{'subdir'}) { + $_ =~ s/^\./$self->{'subdir'}/; + } + print INC_FILE "$_\n"; + } + + close INC_FILE; + } else { + # put all include in $self->{'include'} they will be added on + # command line. + if ($#{$self->{include_file}} >= 0) { + $self->{include} = [ @{$self->{include_file}} ]; + } + + foreach my $file (@{$self->{include_list}}) { + if (!open(FF, $file)) { + if ($self->{action} eq 'check' && !$self->{include_optional}) { + $self->print_to_server("Open of '$file' failed: $!", + $Amanda::Script_App::ERROR); + } + next; + } + while () { + chomp; + if ($self->{action} eq "restore" and + defined $self->{'subdir'}) { + $_ =~ s/^\./$self->{'subdir'}/; + } + push @{$self->{include}}, $_; + } + close(FF); + } + + # add command line include for amrestore + if ($self->{action} eq "restore") { + for(my $i=1;defined $ARGV[$i]; $i++) { + my $param = $ARGV[$i]; + $param =~ /^(.*)$/; + $_ = $1; + if (defined $self->{'subdir'}) { + $_ =~ s/^\./$self->{'subdir'}/; + } + push @{$self->{include}}, $1; + } } - close(FF); } } # on entry: -# $self->disk == //host/share/subdir +# $self->{directory} == //host/share/subdir \\host\share\subdir +# or +# $self->{device} == //host/share/subdir \\host\share\subdir # on exit: -# self->{cifshost} = //host -# $self->{share} = //host/share -# $self->{sambashare} = \\host\share -# $self->{subdir} = subdir +# $self->{cifshost} = //host \\host +# $self->{share} = //host/share \\host\share +# $self->{sambashare} = \\host\share \\host\share +# $self->{subdir} = subdir subdir sub parsesharename { my $self = shift; + my $to_parse = $self->{directory}; + $to_parse = $self->{device} if !defined $to_parse;; - return if !defined $self->{disk}; + return if !defined $to_parse; + if ($to_parse =~ /^\\\\/) { + $self->{unc} = 1; + } else { + $self->{unc} = 0; + } - if ($self->{disk} =~ m,^(//[^/]+/[^/]+)/(.*)$,) { - $self->{share} = $1; - $self->{subdir} = $2 + if ($self->{unc}) { + if ($to_parse =~ m,^(\\\\[^\\]+\\[^\\]+)\\(.*)$,) { + $self->{share} = $1; + $self->{subdir} = $2 + } else { + $self->{share} = $to_parse + } + $self->{sambashare} = $self->{share}; + $to_parse =~ m,^(\\\\[^\\]+)\\[^\\]+,; + $self->{cifshost} = $1; } else { - $self->{share} = $self->{disk}; + if ($to_parse =~ m,^(//[^/]+/[^/]+)/(.*)$,) { + $self->{share} = $1; + $self->{subdir} = $2 + } else { + $self->{share} = $to_parse + } + $self->{sambashare} = $self->{share}; + $self->{sambashare} =~ s,/,\\,g; + $to_parse =~ m,^(//[^/]+)/[^/]+,; + $self->{cifshost} = $1; } - $self->{sambashare} = $self->{share}; - $self->{sambashare} =~ s,/,\\,g; - $self->{disk} =~ m,^(//[^/]+)/[^/]+,; - $self->{cifshost} = $1; } # Read $self->{amandapass} file. # on entry: +# $self->{cifshost} == //host/share # $self->{share} == //host/share # on exit: # $self->{domain} = domain to connect to. @@ -172,27 +265,64 @@ sub findpass { my $amandapass; my $line; - open($amandapass, $self->{amandapass}); + $self->{domain} = undef; + $self->{username} = undef; + $self->{password} = undef; + + debug("amandapass: $self->{amandapass}"); + if (!open($amandapass, $self->{amandapass})) { + if ($self->{allow_anonymous}) { + $self->{username} = $self->{allow_anonymous}; + debug("cannot open password file '$self->{amandapass}': $!\n"); + debug("Using anonymous user: $self->{username}"); + return; + } else { + $self->print_to_server_and_die( + "cannot open password file '$self->{amandapass}': $!", + $Amanda::Script_App::ERROR); + } + } + while ($line = <$amandapass>) { chomp $line; next if $line =~ /^#/; - my ($diskname, $userdomain) = Amanda::Util::skip_quoted_string($line); + my ($diskname, $userpasswd, $domain, $extra) = Amanda::Util::split_quoted_string_friendly($line); + if ($extra) { + debug("Trailling characters ignored in amandapass line"); + } if (defined $diskname && ($diskname eq '*' || - ($diskname =~ m,^(//[^/]+)/\*$, && $1 eq $self->{cifshost}) || + ($self->{unc}==0 && $diskname =~ m,^(//[^/]+)/\*$, && $1 eq $self->{cifshost}) || + ($self->{unc}==1 && $diskname =~ m,^(\\\\[^\\]+)\\\*$, && $1 eq $self->{cifshost}) || $diskname eq $self->{share} || $diskname eq $self->{sambashare})) { - my ($userpasswd, $domain) = split ' ', $userdomain; - $self->{domain} = $domain; - my ($username, $password) = split('%', $userpasswd); - $self->{username} = $username; - $self->{password} = $password; + if (defined $userpasswd && $userpasswd ne "") { + $self->{domain} = $domain if defined $domain && $domain ne ""; + my ($username, $password) = split('%', $userpasswd, 2); + $self->{username} = $username; + if ($password =~ /^6G\!dr(.*)/) { + my $base64 = $1; + $password = MIME::Base64::decode($base64); + } + $self->{password} = $password; + $self->{password} = undef if (defined $password && $password eq ""); + } else { + $self->{username} = "guest"; + } close($amandapass); return; } } close($amandapass); - $self->print_to_server_and_die($self->{action},"Cannot find password for share $self->{share} in $self->{amandapass}", $Amanda::Script_App::ERROR); + if ($self->{allow_anonymous}) { + $self->{username} = $self->{allow_anonymous}; + debug("Cannot find password for share $self->{share} in $self->{amandapass}"); + debug("Using anonymous user: $self->{username}"); + return; + } + $self->print_to_server_and_die( + "Cannot find password for share $self->{share} in $self->{amandapass}", + $Amanda::Script_App::ERROR); } sub command_support { @@ -210,6 +340,7 @@ sub command_support { print "COLLECTION NO\n"; print "MULTI-ESTIMATE NO\n"; print "CALCSIZE NO\n"; + print "CLIENT-ESTIMATE YES\n"; print "EXCLUDE-FILE YES\n"; print "EXCLUDE-LIST YES\n"; print "EXCLUDE-OPTIONAL YES\n"; @@ -222,33 +353,76 @@ sub command_support { sub command_selfcheck { my $self = shift; - $self->{action} = 'check'; + $self->print_to_server("disk " . quote_string($self->{disk}), + $Amanda::Script_App::GOOD); + + $self->print_to_server("amsamba version " . $Amanda::Constants::VERSION, + $Amanda::Script_App::GOOD); + #check binary + if (!defined($self->{smbclient}) || $self->{smbclient} eq "") { + $self->print_to_server( + "smbclient not set; you must define the SMBCLIENT-PATH property", + $Amanda::Script_App::ERROR); + } + elsif (! -e $self->{smbclient}) { + $self->print_to_server("$self->{smbclient} doesn't exist", + $Amanda::Script_App::ERROR); + } + elsif (! -x $self->{smbclient}) { + $self->print_to_server("$self->{smbclient} is not executable", + $Amanda::Script_App::ERROR); + } else { + my @sv = `$self->{smbclient} --version`; + if ($? >> 8 == 0) { + $sv[0] =~ /^[^0-9]*(.*)$/; + my $sv = $1; + $self->print_to_server("amsamba smbclient-version $sv", + $Amanda::Script_App::GOOD); + } else { + $self->print_to_server( + "[Can't get " . $self->{smbclient} . " version]\n", + $Amanda::Script_App::ERROR); + } + } + + $self->print_to_server("$self->{smbclient}", + $Amanda::Script_App::GOOD); + if (!defined $self->{disk} || !defined $self->{device}) { + return; + } $self->parsesharename(); $self->findpass(); $self->validate_inexclude(); print "OK " . $self->{share} . "\n"; - print "OK " . $self->{disk} . "\n"; print "OK " . $self->{device} . "\n"; - - my ($child_rdr, $parent_wtr); - $^F=10; - pipe($child_rdr, $parent_wtr); - $parent_wtr->autoflush(1); + print "OK " . $self->{directory} . "\n" if defined $self->{directory}; + + my ($password_rdr, $password_wtr); + if (defined $self->{password}) { + # Don't set close-on-exec + $^F=10; + pipe($password_rdr, $password_wtr); + $^F=2; + $password_wtr->autoflush(1); + } my($wtr, $rdr, $err); $err = Symbol::gensym; my $pid = open3($wtr, $rdr, $err, "-"); if ($pid == 0) { #child - my $ff = $child_rdr->fileno; - debug("child_rdr $ff"); - $parent_wtr->close(); - $ENV{PASSWD_FD} = $child_rdr->fileno; + if (defined $self->{password}) { + my $ff = $password_rdr->fileno; + debug("password_rdr $ff"); + $password_wtr->close(); + $ENV{PASSWD_FD} = $password_rdr->fileno; + } close(1); close(2); my @ARGV = (); - push @ARGV, $self->{smbclient}, $self->{share}, - "-U", $self->{username}, + push @ARGV, $self->{smbclient}, $self->{share}; + push @ARGV, "" if (!defined $self->{password}); + push @ARGV, "-U", $self->{username}, "-E"; if (defined $self->{domain}) { push @ARGV, "-W", $self->{domain}, @@ -262,24 +436,28 @@ sub command_selfcheck { exec {$self->{smbclient}} @ARGV; } #parent - my $ff = $parent_wtr->fileno; - debug("parent_wtr $ff"); - debug("password $self->{password}"); - $parent_wtr->print($self->{password}); - $parent_wtr->close(); - $child_rdr->close(); + if (defined $self->{password}) { + my $ff = $password_wtr->fileno; + debug("password_wtr $ff"); + $password_wtr->print($self->{password}); + $password_wtr->close(); + $password_rdr->close(); + } else { + debug("No password"); + } close($wtr); close($rdr); while (<$err>) { chomp; debug("stderr: " . $_); next if /^Domain=/; - $self->print_to_server($self->{action}, "smbclient: $_", + # message if samba server is configured with 'security = share' + next if /Server not using user level security and no password supplied./; + $self->print_to_server("smbclient: $_", $Amanda::Script_App::ERROR); } close($err); waitpid($pid, 0); - #check binary #check statefile #check amdevice } @@ -287,30 +465,36 @@ sub command_selfcheck { sub command_estimate { my $self = shift; - $self->{action} = 'estimate'; $self->parsesharename(); $self->findpass(); $self->validate_inexclude(); my $level = $self->{level}[0]; - my ($child_rdr, $parent_wtr); - $^F=10; - pipe($child_rdr, $parent_wtr); - $parent_wtr->autoflush(1); + my ($password_rdr, $password_wtr); + if (defined $self->{password}) { + # Don't set close-on-exec + $^F=10; + pipe($password_rdr, $password_wtr); + $^F=2; + $password_wtr->autoflush(1); + } my($wtr, $rdr, $err); $err = Symbol::gensym; my $pid = open3($wtr, $rdr, $err, "-"); if ($pid == 0) { #child - my $ff = $child_rdr->fileno; - debug("child_rdr $ff"); - $parent_wtr->close(); - $ENV{PASSWD_FD} = $child_rdr->fileno; + if (defined $self->{password}) { + my $ff = $password_rdr->fileno; + debug("password_rdr $ff"); + $password_wtr->close(); + $ENV{PASSWD_FD} = $password_rdr->fileno; + } close(0); close(1); my @ARGV = (); - push @ARGV, $self->{smbclient}, $self->{share}, - "-d", "0", + push @ARGV, $self->{smbclient}, $self->{share}; + push @ARGV, "" if (!defined($self->{password})); + push @ARGV, "-d", "0", "-U", $self->{username}, "-E"; if (defined $self->{domain}) { @@ -329,12 +513,14 @@ sub command_estimate { exec {$self->{smbclient}} @ARGV; } #parent - my $ff = $parent_wtr->fileno; - debug("parent_wtr $ff"); - debug("password $self->{password}"); - $parent_wtr->print($self->{password}); - $parent_wtr->close(); - $child_rdr->close(); + if (defined $self->{password}) { + my $ff = $password_wtr->fileno; + debug("password_wtr $ff"); + debug("password $self->{password}"); + $password_wtr->print($self->{password}); + $password_wtr->close(); + $password_rdr->close(); + } close($wtr); close($rdr); my $size = $self->parse_estimate($err); @@ -355,12 +541,14 @@ sub parse_estimate { next if /^\s*$/; next if /^Domain=/; next if /dumped \d+ files and directories/; + # message if samba server is configured with 'security = share' + next if /Server not using user level security and no password supplied./; debug("stderr: $_"); if ($_ =~ /^Total number of bytes: (\d*)/) { $size = $1; last; } else { - $self->print_to_server($self->{action}, "smbclient: $_", + $self->print_to_server("smbclient: $_", $Amanda::Script_App::ERROR); } } @@ -381,49 +569,54 @@ sub output_size { } } +sub send_empty_tar_file { + my $self = shift; + my ($out1, $out2) = @_; + my $out; + my $buf; + my $size; + + Amanda::Debug::debug("Create empty archive with: tar --create --file=- --files-from=/dev/null"); + open2($out, undef, "tar", "--create", "--file=-", "--files-from=/dev/null"); + + while(($size = sysread($out, $buf, 32768))) { + syswrite($out1, $buf, $size); + syswrite($out2, $buf, $size); + } +} + sub command_backup { my $self = shift; - $self->{action} = 'backup'; + my $level = $self->{level}[0]; + $self->parsesharename(); $self->findpass(); $self->validate_inexclude(); - my $level = $self->{level}[0]; - my $mesgout_fd; - open($mesgout_fd, '>&=3') || die(); - $self->{mesgout} = $mesgout_fd; - - my $pid_tee = open3(\*INDEX_IN, '>&STDOUT', \*INDEX_TEE, "-"); - if ($pid_tee == 0) { - close(INDEX_IN); - close(INDEX_TEE); - my $buf; - my $size = -1; - while (($size = POSIX::read(0, $buf, 32768)) > 0) { - POSIX::write(1, $buf, $size); - POSIX::write(2, $buf, $size); - } - exit 0; - } - my ($child_rdr, $parent_wtr); - $^F=10; - pipe($child_rdr, $parent_wtr); - $^F=2; - $parent_wtr->autoflush(1); - my($wtr, $err); - $err = Symbol::gensym; - my $pid = open3($wtr, ">&INDEX_IN", $err, "-"); + my ($password_rdr, $password_wtr); + if (defined $self->{password}) { + # Don't set close-on-exec + $^F=10; + pipe($password_rdr, $password_wtr); + $^F=2; + $password_wtr->autoflush(1); + } + my($smbclient_wtr, $smbclient_rdr, $smbclient_err); + $smbclient_err = Symbol::gensym; + my $pid = open3($smbclient_wtr, $smbclient_rdr, $smbclient_err, "-"); if ($pid == 0) { #child - my $ff = $child_rdr->fileno; - debug("child_rdr $ff"); - $parent_wtr->close(); - $ENV{PASSWD_FD} = $child_rdr->fileno; - close(0); + if (defined $self->{password}) { + my $ff = $password_rdr->fileno; + debug("password_rdr $ff"); + $password_wtr->close(); + $ENV{PASSWD_FD} = $password_rdr->fileno; + } my @ARGV = (); - push @ARGV, $self->{smbclient}, $self->{share}, - "-d", "0", + push @ARGV, $self->{smbclient}, $self->{share}; + push @ARGV, "" if (!defined($self->{password})); + push @ARGV, "-d", "0", "-U", $self->{username}, "-E"; if (defined $self->{domain}) { @@ -432,82 +625,166 @@ sub command_backup { if (defined $self->{subdir}) { push @ARGV, "-D", $self->{subdir}, } + my $comm ; if ($level == 0) { - $comm = "-Tqca"; + $comm = "tarmode full reset hidden system quiet;"; } else { - $comm = "-Tqcg"; + $comm = "tarmode inc noreset hidden system quiet;"; } + $comm .= " tar c"; if ($#{$self->{exclude}} >= 0) { $comm .= "X"; } if ($#{$self->{include}} >= 0) { $comm .= "I"; } - push @ARGV, $comm, "-"; + $comm .= " -"; if ($#{$self->{exclude}} >= 0) { - push @ARGV, @{$self->{exclude}}; + $comm .= " " . join(" ", @{$self->{exclude}}); } if ($#{$self->{include}} >= 0) { - push @ARGV, @{$self->{include}}; + $comm .= " " . join(" ", @{$self->{include}}); } + push @ARGV, "-c", $comm; debug("execute: " . $self->{smbclient} . " " . join(" ", @ARGV)); exec {$self->{smbclient}} @ARGV; } - my $ff = $parent_wtr->fileno; - debug("parent_wtr $ff"); - debug("password $self->{password}"); - $parent_wtr->print($self->{password}); - $parent_wtr->close(); - $child_rdr->close(); - close($wtr); + if (defined $self->{password}) { + my $ff = $password_wtr->fileno; + debug("password_wtr $ff"); + $password_wtr->print($self->{password}); + $password_wtr->close(); + $password_rdr->close(); + } else { + debug("No password"); + } + close($smbclient_wtr); #index process - my $index; + my $index_rdr; + my $index_wtr; debug("$self->{gnutar} -tf -"); - my $pid_index1 = open2($index, '<&INDEX_TEE', $self->{gnutar}, "-tf", "-"); - close(INDEX_IN); + my $pid_index1 = open2($index_rdr, $index_wtr, $self->{gnutar}, "-tf", "-"); my $size = -1; - my $index_fd = $index->fileno; + my $index_fd = $index_rdr->fileno; debug("index $index_fd"); + my $indexout_fd; if (defined($self->{index})) { - my $indexout_fd; - open($indexout_fd, '>&=4') || die(); - $self->parse_backup($index, $mesgout_fd, $indexout_fd); - close($indexout_fd); + open($indexout_fd, '>&=4') || + $self->print_to_server_and_die("Can't open indexout_fd: $!", + $Amanda::Script_App::ERROR); } - else { - $self->parse_backup($index_fd, $mesgout_fd, undef); - } - close($index); - while (<$err>) { - chomp; - debug("stderr: " . $_); - next if /^Domain=/; - next if /dumped (\d+) files and directories/; - if (/^Total bytes written: (\d*)/) { + my $file_to_close = 3; + my $smbclient_stdout_src = Amanda::MainLoop::fd_source($smbclient_rdr, + $G_IO_IN|$G_IO_HUP|$G_IO_ERR); + my $smbclient_stderr_src = Amanda::MainLoop::fd_source($smbclient_err, + $G_IO_IN|$G_IO_HUP|$G_IO_ERR); + my $index_tar_stdout_src = Amanda::MainLoop::fd_source($index_rdr, + $G_IO_IN|$G_IO_HUP|$G_IO_ERR); + + my $smbclient_stdout_done = 0; + my $smbclient_stderr_done = 0; + my $data_size = 0; + my $nb_files = 0; + $smbclient_stdout_src->set_callback(sub { + my $buf; + my $blocksize = -1; + $blocksize = sysread($smbclient_rdr, $buf, 32768); + if (!$blocksize) { + $file_to_close--; + $smbclient_stdout_src->remove(); + $smbclient_stdout_done = 1; + if ($smbclient_stderr_done) { + if ($data_size == 0 and $nb_files == 0 and $size == 0) { + $self->send_empty_tar_file(*STDOUT, $index_wtr); + } + close($index_wtr); + close(STDOUT); + } + close($smbclient_rdr); + Amanda::MainLoop::quit() if $file_to_close == 0; + return; + } + $data_size += $blocksize; + syswrite(STDOUT, $buf, $blocksize); + syswrite($index_wtr, $buf, $blocksize); + }); + + $smbclient_stderr_src->set_callback(sub { + my $line = <$smbclient_err>; + if (!defined $line) { + $file_to_close--; + $smbclient_stderr_src->remove(); + $smbclient_stderr_done = 1; + if ($smbclient_stdout_done) { + if ($data_size == 0 and $nb_files == 0 and $size == 0) { + $self->send_empty_tar_file(*STDOUT, $index_wtr); + } + close($index_wtr); + close(STDOUT); + } + close ($smbclient_err); + Amanda::MainLoop::quit() if $file_to_close == 0; + return; + } + chomp $line; + debug("stderr: " . $line); + return if $line =~ /^Domain=/; + return if $line =~ /^tarmode is now /; + if ($line =~ /dumped (\d+) files and directories/) { + $nb_files = $1; + return; + } + # message if samba server is configured with 'security = share' + return if $line =~$line =~ /Server not using user level security and no password supplied./; + if ($line =~ /^Total bytes written: (\d*)/) { $size = $1; + return; + } + $self->print_to_server("smbclient: $line", $Amanda::Script_App::ERROR); + }); + + $index_tar_stdout_src->set_callback(sub { + my $line = <$index_rdr>; + if (!defined $line) { + $file_to_close--; + $index_tar_stdout_src->remove(); + close($index_rdr); + close($indexout_fd); + Amanda::MainLoop::quit() if $file_to_close == 0; + return; + } + if ($line =~ /^\.\//) { + if(defined($indexout_fd)) { + if(defined($self->{index})) { + $line =~ s/^\.//; + print $indexout_fd $line; + } + } } else { - $self->print_to_server($self->{action}, "smbclient: $_", - $Amanda::Script_App::ERROR); + chomp $line; + $self->print_to_server($line, $Amanda::Script_App::ERROR); } - } + }); + + Amanda::MainLoop::run(); + if ($size >= 0) { my $ksize = $size / 1024; if ($ksize < 32) { $ksize = 32; } - print $mesgout_fd "sendbackup: size $ksize\n"; - print $mesgout_fd "sendbackup: end\n"; + print {$self->{mesgout}} "sendbackup: size $ksize\n"; + print {$self->{mesgout}} "sendbackup: end\n"; } waitpid $pid, 0; if ($? != 0) { - $self->print_to_server_and_die($self->{action}, - "smbclient returned error", + $self->print_to_server_and_die("smbclient returned error", $Amanda::Script_App::ERROR); } exit 0; @@ -551,7 +828,9 @@ sub index_from_output { sub command_index_from_image { my $self = shift; my $index_fd; - open($index_fd, "$self->{gnutar} --list --file - |") || die(); + open($index_fd, "$self->{gnutar} --list --file - |") || + $self->print_to_server_and_die("Can't run $self->{gnutar}: $!", + $Amanda::Script_App::ERROR); index_from_output($index_fd, 1); } @@ -559,29 +838,42 @@ sub command_restore { my $self = shift; my @cmd = (); - $self->{restore} = 'backup'; $self->parsesharename(); chdir(Amanda::Util::get_original_cwd()); if ($self->{recover_mode} eq "smb") { + $self->validate_inexclude(); $self->findpass(); - push @cmd, $self->{smbclient}, $self->{share}, - "-d", "0", + push @cmd, $self->{smbclient}, $self->{share}; + push @cmd, "-D", $self->{'subdir'} if defined $self->{'subdir'}; + push @cmd, "" if (!defined $self->{password}); + push @cmd, "-d", "0", "-U", $self->{username}; if (defined $self->{domain}) { push @cmd, "-W", $self->{domain}; } - push @cmd, "-Tx", "-"; - for(my $i=1;defined $ARGV[$i]; $i++) { - my $param = $ARGV[$i]; - $param =~ /^(.*)$/; - push @cmd, $1; + if (defined $self->{'include_filename'}) { + push @cmd, "-TFx", "-", "$self->{'include_filename'}"; + } else { + push @cmd, "-Tx", "-"; + if ($#{$self->{include}} >= 0) { + push @cmd, @{$self->{include}}; + } + for(my $i=1;defined $ARGV[$i]; $i++) { + my $param = $ARGV[$i]; + $param =~ /^(.*)$/; + push @cmd, $1; + } } my ($parent_rdr, $child_wtr); - $^F=10; - pipe($parent_rdr, $child_wtr); - $child_wtr->autoflush(1); + if (defined $self->{password}) { + # Don't set close-on-exec + $^F=10; + pipe($parent_rdr, $child_wtr); + $^F=2; + $child_wtr->autoflush(1); + } my($wtr, $rdr, $err); $err = Symbol::gensym; my $pid = open3($wtr, $rdr, $err, "-"); @@ -590,13 +882,34 @@ sub command_restore { $child_wtr->close(); exit 0; } - $child_wtr->close(); - $ENV{PASSWD_FD} = $parent_rdr->fileno; + if (defined $self->{password}) { + $child_wtr->close(); + $ENV{PASSWD_FD} = $parent_rdr->fileno; + } debug("cmd:" . join(" ", @cmd)); exec { $cmd[0] } @cmd; die("Can't exec '", $cmd[0], "'"); } else { push @cmd, $self->{gnutar}, "-xpvf", "-"; + if (defined $self->{directory}) { + if (!-d $self->{directory}) { + $self->print_to_server_and_die( + "Directory $self->{directory}: $!", + $Amanda::Script_App::ERROR); + } + if (!-w $self->{directory}) { + $self->print_to_server_and_die( + "Directory $self->{directory}: $!", + $Amanda::Script_App::ERROR); + } + push @cmd, "--directory", $self->{directory}; + } + if ($#{$self->{include_list}} == 0) { + push @cmd, "--files-from", $self->{include_list}[0]; + } + if ($#{$self->{exclude_list}} == 0) { + push @cmd, "--exclude-from", $self->{exclude_list}[0]; + } for(my $i=1;defined $ARGV[$i]; $i++) { my $param = $ARGV[$i]; $param =~ /^(.*)$/; @@ -611,13 +924,19 @@ sub command_restore { sub command_validate { my $self = shift; - $self->{validate} = 'backup'; + if (!defined($self->{gnutar}) || !-x $self->{gnutar}) { + return $self->default_validate(); + } + my(@cmd) = ($self->{gnutar}, "-tf", "-"); debug("cmd:" . join(" ", @cmd)); - my $pid = open3('>&STDIN', '>&STDOUT', '>&STDERR', @cmd) || die("validate", "Unable to run @cmd"); + my $pid = open3('>&STDIN', '>&STDOUT', '>&STDERR', @cmd) || + $self->print_to_server_and_die("Unable to run @cmd: $!", + $Amanda::Script_App::ERROR); waitpid $pid, 0; if( $? != 0 ){ - die("validate", "$self->{gnutar} returned error"); + $self->print_to_server_and_die("$self->{gnutar} returned error", + $Amanda::Script_App::ERROR); } exit(0); } @@ -655,6 +974,8 @@ my @opt_include_file; my @opt_include_list; my $opt_include_optional; my $opt_recover_mode; +my $opt_allow_anonymous; +my $opt_directory; Getopt::Long::Configure(qw{bundling}); GetOptions( @@ -669,9 +990,9 @@ GetOptions( 'collection=s' => \$opt_collection, 'record' => \$opt_record, 'calcsize' => \$opt_calcsize, - 'gnutar_path' => \$opt_gnutar_path, - 'smbclient_path' => \$opt_smbclient_path, - 'amandapass' => \$opt_amandapass, + 'gnutar-path=s' => \$opt_gnutar_path, + 'smbclient-path=s' => \$opt_smbclient_path, + 'amandapass=s' => \$opt_amandapass, 'exclude-file=s' => \@opt_exclude_file, 'exclude-list=s' => \@opt_exclude_list, 'exclude-optional=s' => \$opt_exclude_optional, @@ -679,6 +1000,8 @@ GetOptions( 'include-list=s' => \@opt_include_list, 'include-optional=s' => \$opt_include_optional, 'recover-mode=s' => \$opt_recover_mode, + 'allow-anonymous=s' => \$opt_allow_anonymous, + 'directory=s' => \$opt_directory, ) or usage(); if (defined $opt_version) { @@ -686,6 +1009,6 @@ if (defined $opt_version) { exit(0); } -my $application = Amanda::Application::Amsamba->new($opt_config, $opt_host, $opt_disk, $opt_device, \@opt_level, $opt_index, $opt_message, $opt_collection, $opt_record, $opt_calcsize, $opt_gnutar_path, $opt_smbclient_path, $opt_amandapass, \@opt_exclude_file, \@opt_exclude_list, $opt_exclude_optional, \@opt_include_file, \@opt_include_list, $opt_include_optional, $opt_recover_mode); +my $application = Amanda::Application::Amsamba->new($opt_config, $opt_host, $opt_disk, $opt_device, \@opt_level, $opt_index, $opt_message, $opt_collection, $opt_record, $opt_calcsize, $opt_gnutar_path, $opt_smbclient_path, $opt_amandapass, \@opt_exclude_file, \@opt_exclude_list, $opt_exclude_optional, \@opt_include_file, \@opt_include_list, $opt_include_optional, $opt_recover_mode, $opt_allow_anonymous, $opt_directory); $application->do($ARGV[0]);