password callback working
[scpubgit/Object-Remote.git] / lib / Object / Remote / Connector / LocalSudo.pm
1 package Object::Remote::Connector::LocalSudo;
2
3 use Symbol qw(gensym);
4 use IPC::Open3;
5 use Moo;
6
7 extends 'Object::Remote::Connector::Local';
8
9 has password_callback => (is => 'ro');
10
11 sub _sudo_perl_command {
12   my ($self, $stderr_fdno, $target_user) = @_;
13   return
14     'sudo', '-S', '-u', $target_user, '-p', "[sudo] password please\n",
15     'perl', '-MPOSIX=dup2',
16             '-e', 'print STDERR "GO\n"; dup2(shift(@ARGV), 2); exec(@ARGV);',
17     $stderr_fdno, $self->_perl_command($target_user);
18 }
19
20 sub _start_perl {
21   my $self = shift;
22   open my $stderr_dup, '>&', \*STDERR or die "Couldn't dup STDERR: $!";
23   my $sudo_stderr = gensym;
24   my $pid = open3(
25     my $foreign_stdin,
26     my $foreign_stdout,
27     $sudo_stderr,
28     $self->_sudo_perl_command(fileno($stderr_dup), @_)
29   ) or die "open3 failed: $!";
30   chomp(my $line = <$sudo_stderr>);
31   if ($line eq "GO") {
32     # started already, we're good
33   } elsif ($line =~ /\[sudo\]/) {
34     my $cb = $self->password_callback;
35     die "sudo sent ${line} but we have no password callback"
36       unless $cb;
37     print $foreign_stdin $cb->($line, @_), "\n";
38     chomp($line = <$sudo_stderr>);
39     die "sent password and expected newline from sudo, got ${line}"
40       if $line;
41     chomp($line = <$sudo_stderr>);
42     die "sent password but next line was ${line}"
43       unless $line eq "GO";
44   } else {
45     die "Got inexplicable line ${line} trying to sudo";
46   };
47   return ($foreign_stdin, $foreign_stdout, $pid);
48 };
49
50 no warnings 'once';
51
52 push @Object::Remote::Connection::Guess, sub {
53   for ($_[0]) {
54     # username followed by @
55     if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
56       return __PACKAGE__->new->connect($1);
57     }
58   }
59   return;
60 };
61
62 1;