working automatic prompting
[scpubgit/Object-Remote.git] / lib / Object / Remote / Connector / LocalSudo.pm
CommitLineData
a9fdb55e 1package Object::Remote::Connector::LocalSudo;
2
7efea51f 3use Symbol qw(gensym);
1b315002 4use Module::Runtime qw(use_module);
7efea51f 5use IPC::Open3;
a9fdb55e 6use Moo;
7
8extends 'Object::Remote::Connector::Local';
9
1b315002 10has target_user => (is => 'ro', required => 1);
11
12has password_callback => (is => 'lazy');
13
14sub _build_password_callback {
15 my ($self) = @_;
16 my $pw_prompt = use_module('Object::Remote::Prompt')->can('prompt_pw');
17 my $user = $self->target_user;
18 return sub {
19 $pw_prompt->("sudo password for ${user}", undef, { cache => 1 })
20 }
21}
7efea51f 22
23sub _sudo_perl_command {
1b315002 24 my ($self) = @_;
7efea51f 25 return
1b315002 26 'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n",
7efea51f 27 'perl', '-MPOSIX=dup2',
859f4451 28 '-e', 'print STDERR "GO\n"; exec(@ARGV);',
1b315002 29 $self->_perl_command($self->target_user);
7efea51f 30}
31
32sub _start_perl {
33 my $self = shift;
7efea51f 34 my $sudo_stderr = gensym;
35 my $pid = open3(
36 my $foreign_stdin,
37 my $foreign_stdout,
38 $sudo_stderr,
859f4451 39 $self->_sudo_perl_command(@_)
7efea51f 40 ) or die "open3 failed: $!";
41 chomp(my $line = <$sudo_stderr>);
42 if ($line eq "GO") {
43 # started already, we're good
44 } elsif ($line =~ /\[sudo\]/) {
45 my $cb = $self->password_callback;
46 die "sudo sent ${line} but we have no password callback"
47 unless $cb;
48 print $foreign_stdin $cb->($line, @_), "\n";
49 chomp($line = <$sudo_stderr>);
aa052874 50 if ($line and $line ne 'GO') {
51 die "sent password and expected newline from sudo, got ${line}";
52 }
53 elsif (not $line) {
54 chomp($line = <$sudo_stderr>);
55 die "sent password but next line was ${line}"
56 unless $line eq "GO";
57 }
7efea51f 58 } else {
59 die "Got inexplicable line ${line} trying to sudo";
60 };
859f4451 61 Object::Remote->current_loop
62 ->watch_io(
63 handle => $sudo_stderr,
64 on_read_ready => sub {
65 if (sysread($sudo_stderr, my $buf, 1024) > 0) {
66 print STDERR $buf;
67 } else {
68 Object::Remote->current_loop
69 ->unwatch_io(
70 handle => $sudo_stderr,
71 on_read_ready => 1
72 );
73 }
74 }
75 );
7efea51f 76 return ($foreign_stdin, $foreign_stdout, $pid);
a9fdb55e 77};
78
7efea51f 79no warnings 'once';
80
a9fdb55e 81push @Object::Remote::Connection::Guess, sub {
82 for ($_[0]) {
83 # username followed by @
84 if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
1b315002 85 return __PACKAGE__->new(target_user => $1)->connect;
a9fdb55e 86 }
87 }
88 return;
89};
90
911;