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