successfully wrapping sudo
Matt S Trout [Sat, 2 Jun 2012 16:30:41 +0000 (16:30 +0000)]
lib/Object/Remote/Connector/Local.pm
lib/Object/Remote/Connector/LocalSudo.pm
lib/Object/Remote/Connector/SSH.pm
lib/Object/Remote/Role/Connector/PerlInterpreter.pm
xt/local-sudo.t

index ce96d9a..bfe6639 100644 (file)
@@ -4,6 +4,8 @@ use Moo;
 
 with 'Object::Remote::Role::Connector::PerlInterpreter';
 
+no warnings 'once';
+
 push @Object::Remote::Connection::Guess, sub {
   if (($_[0]||'') eq '-') { __PACKAGE__->new->connect }
 };
index c23f2e9..c6bad11 100644 (file)
@@ -1,14 +1,51 @@
 package Object::Remote::Connector::LocalSudo;
 
+use Symbol qw(gensym);
+use IPC::Open3;
 use Moo;
 
 extends 'Object::Remote::Connector::Local';
 
-around _perl_command => sub {
-  my ($orig, $self, $target_user) = @_;
-  return 'sudo', '-u', $target_user, $self->$orig($target_user);
+has password_callback => (is => 'ro');
+
+sub _sudo_perl_command {
+  my ($self, $stderr_fdno, $target_user) = @_;
+  return
+    'sudo', '-S', '-u', $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);
+}
+
+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), @_)
+  ) or die "open3 failed: $!";
+  chomp(my $line = <$sudo_stderr>);
+  if ($line eq "GO") {
+    # started already, we're good
+  } elsif ($line =~ /\[sudo\]/) {
+    my $cb = $self->password_callback;
+    die "sudo sent ${line} but we have no password callback"
+      unless $cb;
+    print $foreign_stdin $cb->($line, @_), "\n";
+    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";
+  };
+  return ($foreign_stdin, $foreign_stdout, $pid);
 };
 
+no warnings 'once';
+
 push @Object::Remote::Connection::Guess, sub {
   for ($_[0]) {
     # username followed by @
index 3d89564..a90270f 100644 (file)
@@ -11,6 +11,8 @@ around _perl_command => sub {
   return 'ssh', $target, $self->$orig($target);
 };
 
+no warnings 'once';
+
 push @Object::Remote::Connection::Guess, sub { 
   for ($_[0]) {
     # 0-9 a-z _ - first char, those or . subsequent - hostnamish
index 62d0153..5a9f64e 100644 (file)
@@ -30,13 +30,19 @@ around connect => sub {
 
 sub _perl_command { 'perl', '-' }
 
-sub _open2_for {
+sub _start_perl {
   my $self = shift;
   my $pid = open2(
     my $foreign_stdout,
     my $foreign_stdin,
     $self->_perl_command(@_),
   ) or die "Failed to run perl at '$_[0]': $!";
+  return ($foreign_stdin, $foreign_stdout, $pid);
+}
+
+sub _open2_for {
+  my $self = shift;
+  my ($foreign_stdin, $foreign_stdout, $pid) = $self->_start_perl(@_);
   require Object::Remote::FatNode;
   print $foreign_stdin $Object::Remote::FatNode::DATA, "__END__\n"
     or die "Failed to send fatpacked data to new node on '$_[0]': $!";
index cd0af39..b1a7fa6 100644 (file)
@@ -1,8 +1,6 @@
 use strictures 1;
 use Test::More;
-use FindBin;
-
-use lib "$FindBin::Bin/lib";
+use lib 'xt/lib';
 
 use Object::Remote;