X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FObject%2FRemote%2FConnector%2FLocalSudo.pm;h=47dead405d5bf8b2814b6f9a5eacbeb7802b3497;hb=f425c3cb155190f7113bda4ad91d831662a2ac7a;hp=6eed5e6dd04b75955c5407e2ad35dcdbe8f833b3;hpb=dac97b35627e705e87c7b9130b1c60d8ef217334;p=scpubgit%2FObject-Remote.git diff --git a/lib/Object/Remote/Connector/LocalSudo.pm b/lib/Object/Remote/Connector/LocalSudo.pm index 6eed5e6..47dead4 100644 --- a/lib/Object/Remote/Connector/LocalSudo.pm +++ b/lib/Object/Remote/Connector/LocalSudo.pm @@ -1,31 +1,46 @@ package Object::Remote::Connector::LocalSudo; +use Object::Remote::Logging qw (:log :dlog); use Symbol qw(gensym); +use Module::Runtime qw(use_module); use IPC::Open3; use Moo; extends 'Object::Remote::Connector::Local'; -has password_callback => (is => 'ro'); +has target_user => (is => 'ro', required => 1); -sub _sudo_perl_command { - my ($self, $stderr_fdno, $target_user) = @_; - return - 'sudo', '-S', '-u', $target_user, '-p', "[sudo] password please\n", +has password_callback => (is => 'lazy'); + +sub _build_password_callback { + my ($self) = @_; + my $pw_prompt = use_module('Object::Remote::Prompt')->can('prompt_pw'); + my $user = $self->target_user; + return sub { + $pw_prompt->("sudo password for ${user}", undef, { cache => 1 }) + } +} + +has sudo_perl_command => (is => 'lazy'); + +sub _build_sudo_perl_command { + my ($self) = @_; + return [ + 'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n", 'perl', '-MPOSIX=dup2', - '-e', 'print STDERR "GO\n"; dup2(shift(@ARGV), 2); exec(@ARGV);', - $stderr_fdno, $self->_perl_command($target_user); + '-e', 'print STDERR "GO\n"; exec(@ARGV);', + @{$self->perl_command}, + ]; } sub _start_perl { my $self = shift; - open my $stderr_dup, '>&', \*STDERR or die "Couldn't dup STDERR: $!"; my $sudo_stderr = gensym; my $pid = open3( my $foreign_stdin, my $foreign_stdout, $sudo_stderr, - $self->_sudo_perl_command(fileno($stderr_dup), @_) + @{$self->sudo_perl_command} ) or die "open3 failed: $!"; chomp(my $line = <$sudo_stderr>); if ($line eq "GO") { @@ -36,14 +51,36 @@ sub _start_perl { unless $cb; print $foreign_stdin $cb->($line, @_), "\n"; chomp($line = <$sudo_stderr>); - die "sent password and expected newline from sudo, got ${line}" - if $line; - chomp($line = <$sudo_stderr>); - die "sent password but next line was ${line}" - unless $line eq "GO"; + if ($line and $line ne 'GO') { + die "sent password and expected newline from sudo, got ${line}"; + } + elsif (not $line) { + chomp($line = <$sudo_stderr>); + die "sent password but next line was ${line}" + unless $line eq "GO"; + } } else { die "Got inexplicable line ${line} trying to sudo"; }; + Object::Remote->current_loop + ->watch_io( + handle => $sudo_stderr, + on_read_ready => sub { + Dlog_debug { "LocalSudo: Preparing to read data from $_" } $sudo_stderr; + if (sysread($sudo_stderr, my $buf, 32768) > 0) { + log_trace { "LocalSudo: successfully read data, printing it to STDERR" }; + print STDERR $buf; + log_trace { "LocalSudo: print() to STDERR is done" }; + } else { + log_debug { "LocalSudo: received EOF or error on file handle, unwatching it" }; + Object::Remote->current_loop + ->unwatch_io( + handle => $sudo_stderr, + on_read_ready => 1 + ); + } + } + ); return ($foreign_stdin, $foreign_stdout, $pid); }; @@ -53,7 +90,8 @@ push @Object::Remote::Connection::Guess, sub { for ($_[0]) { # username followed by @ if (defined and !ref and /^ ([^\@]*?) \@ $/x) { - return __PACKAGE__->new->connect($1); + shift(@_); + return __PACKAGE__->new(@_, target_user => $1); } } return;