1 package Object::Remote::Connector::LocalSudo;
4 use Module::Runtime qw(use_module);
8 extends 'Object::Remote::Connector::Local';
10 has target_user => (is => 'ro', required => 1);
12 has password_callback => (is => 'lazy');
14 sub _build_password_callback {
16 my $pw_prompt = use_module('Object::Remote::Prompt')->can('prompt_pw');
17 my $user = $self->target_user;
19 $pw_prompt->("sudo password for ${user}", undef, { cache => 1 })
23 has sudo_perl_command => (is => 'lazy');
25 sub _build_sudo_perl_command {
28 'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n",
29 'perl', '-MPOSIX=dup2',
30 '-e', 'print STDERR "GO\n"; exec(@ARGV);',
36 my $sudo_stderr = gensym;
41 @{$self->sudo_perl_command}
42 ) or die "open3 failed: $!";
43 chomp(my $line = <$sudo_stderr>);
45 # started already, we're good
46 } elsif ($line =~ /\[sudo\]/) {
47 my $cb = $self->password_callback;
48 die "sudo sent ${line} but we have no password callback"
50 print $foreign_stdin $cb->($line, @_), "\n";
51 chomp($line = <$sudo_stderr>);
52 if ($line and $line ne 'GO') {
53 die "sent password and expected newline from sudo, got ${line}";
56 chomp($line = <$sudo_stderr>);
57 die "sent password but next line was ${line}"
61 die "Got inexplicable line ${line} trying to sudo";
63 Object::Remote->current_loop
65 handle => $sudo_stderr,
66 on_read_ready => sub {
67 #TODO is there a specific reason sysread() and syswrite() aren't
68 #a part of ::MiniLoop? It's one spot to handle errors and other
69 #logic involving filehandles
70 Dlog_debug { "LocalSudo: Preparing to read data from $_" } $sudo_stderr;
71 if (sysread($sudo_stderr, my $buf, 32768) > 0) {
72 log_trace { "LocalSudo: successfully read data, printing it to STDERR" };
74 log_trace { "LocalSudo: print() to STDERR is done" };
76 log_debug { "LocalSudo: received EOF or error on file handle, unwatching it" };
77 Object::Remote->current_loop
79 handle => $sudo_stderr,
85 return ($foreign_stdin, $foreign_stdout, $pid);
90 push @Object::Remote::Connection::Guess, sub {
92 # username followed by @
93 if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
95 return __PACKAGE__->new(@_, target_user => $1);