working automatic prompting
Matt S Trout [Wed, 18 Jul 2012 18:39:35 +0000 (18:39 +0000)]
lib/Object/Remote/Connector/LocalSudo.pm
lib/Object/Remote/Prompt.pm [new file with mode: 0644]
lib/Object/Remote/Role/Connector/PerlInterpreter.pm
xt/local-sudo.t

index aa19ced..0ff34b3 100644 (file)
@@ -1,20 +1,32 @@
 package Object::Remote::Connector::LocalSudo;
 
 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);
+
+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 })
+  }
+}
 
 sub _sudo_perl_command {
-  my ($self, $target_user) = @_;
+  my ($self) = @_;
   return
-    'sudo', '-S', '-u', $target_user, '-p', "[sudo] password please\n",
+    'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n",
     'perl', '-MPOSIX=dup2',
             '-e', 'print STDERR "GO\n"; exec(@ARGV);',
-    $self->_perl_command($target_user);
+    $self->_perl_command($self->target_user);
 }
 
 sub _start_perl {
@@ -70,7 +82,7 @@ push @Object::Remote::Connection::Guess, sub {
   for ($_[0]) {
     # username followed by @
     if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
-      return __PACKAGE__->new->connect($1);
+      return __PACKAGE__->new(target_user => $1)->connect;
     }
   }
   return;
diff --git a/lib/Object/Remote/Prompt.pm b/lib/Object/Remote/Prompt.pm
new file mode 100644 (file)
index 0000000..29a8661
--- /dev/null
@@ -0,0 +1,72 @@
+package Object::Remote::Prompt;
+
+use strictures 1;
+use IO::Handle;
+use Exporter;
+
+our @EXPORT = qw(prompt prompt_pw);
+
+our ($prompt, $prompt_pw);
+
+sub _local_prompt {
+  _local_prompt_core(0, @_);
+}
+
+sub _local_prompt_pw {
+  _local_prompt_core(1, @_);
+}
+
+our %Prompt_Cache;
+
+sub _local_prompt_core {
+  my ($pw, $message, $default, $opts) = @_;
+
+  if ($opts->{cache} and my $hit = $Prompt_Cache{$message}) {
+    return $hit;
+  }
+
+  STDOUT->autoflush(1);
+
+  system('stty -echo') if $pw;
+
+  print STDOUT "${message}: ";
+  chomp(my $res = <STDIN>);
+
+  print STDOUT "\n"   if $pw;
+  system('stty echo') if $pw;
+
+  $Prompt_Cache{$message} = $res if $opts->{cache};
+
+  return $res;
+}
+
+sub prompt {
+  die "User input wanted - $_[0] - but no prompt available"
+    unless $prompt;
+  goto &$prompt;
+}
+
+sub prompt_pw {
+  die "User input wanted - $_[0] - but no password prompt available"
+    unless $prompt_pw;
+  goto &$prompt_pw;
+}
+
+if (-t STDIN) {
+  $prompt = \&_local_prompt;
+  $prompt_pw = \&_local_prompt_pw;
+}
+
+sub set_local_prompt_command {
+  ($prompt, $prompt_pw) = @_;
+  return;
+}
+
+sub maybe_set_prompt_command_on {
+  return unless $prompt;
+  my ($conn) = @_;
+  $conn->remote_sub('Object::Remote::Prompt::set_local_prompt_command')
+       ->($prompt, $prompt_pw);
+}
+
+1;
index 312ee4c..f304a8d 100644 (file)
@@ -26,6 +26,8 @@ around connect => sub {
     class => 'Object::Remote::ModuleLoader',
     args => { module_sender => $self->module_sender }
   )->disarm_free;
+  require Object::Remote::Prompt;
+  Object::Remote::Prompt::maybe_set_prompt_command_on($conn);
   return $conn;
 };
 
index b7e28c0..ef2f1fd 100644 (file)
@@ -4,21 +4,12 @@ use Test::More;
 use lib 'xt/lib';
 
 use Object::Remote;
-use Object::Remote::Connector::LocalSudo;
 
 
 my $user = $ENV{TEST_SUDOUSER}
     or plan skip_all => q{Requires TEST_SUDOUSER to be set};
 
-my $pw;
-
-my $connector = Object::Remote::Connector::LocalSudo->new(
-  password_callback => sub {
-    $pw ||= prompt 'Sudo password', -echo => '*';
-  }
-);
-
-my $remote = TestFindUser->new::on($connector->connect($user));
+my $remote = TestFindUser->new::on("${user}\@");
 my $remote_user = $remote->user;
 like $remote_user, qr/^\d+$/, 'returned an int';
 isnt $remote_user, $<, 'ran as different user';