1 package Object::Remote::Connector::LocalSudo;
3 use Object::Remote::Logging qw (:log :dlog);
5 use Module::Runtime qw(use_module);
9 extends 'Object::Remote::Connector::Local';
11 has target_user => (is => 'ro', required => 1);
13 has password_callback => (is => 'lazy');
15 sub _build_password_callback {
17 my $pw_prompt = use_module('Object::Remote::Prompt')->can('prompt_pw');
18 my $user = $self->target_user;
20 $pw_prompt->("sudo password for ${user}", undef, { cache => 1 })
24 has sudo_perl_command => (is => 'lazy');
26 sub _build_sudo_perl_command {
29 'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n",
30 'perl', '-MPOSIX=dup2',
31 '-e', 'print STDERR "GO\n"; exec(@ARGV);',
37 my $sudo_stderr = gensym;
42 @{$self->sudo_perl_command}
43 ) or die "open3 failed: $!";
44 chomp(my $line = <$sudo_stderr>);
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"
51 print $foreign_stdin $cb->($line, @_), "\n";
52 chomp($line = <$sudo_stderr>);
53 if ($line and $line ne 'GO') {
54 die "sent password and expected newline from sudo, got ${line}";
57 chomp($line = <$sudo_stderr>);
58 die "sent password but next line was ${line}"
62 die "Got inexplicable line ${line} trying to sudo";
64 Object::Remote->current_loop
66 handle => $sudo_stderr,
67 on_read_ready => sub {
68 Dlog_debug { "LocalSudo: Preparing to read data from $_" } $sudo_stderr;
69 if (sysread($sudo_stderr, my $buf, 32768) > 0) {
70 log_trace { "LocalSudo: successfully read data, printing it to STDERR" };
72 log_trace { "LocalSudo: print() to STDERR is done" };
74 log_debug { "LocalSudo: received EOF or error on file handle, unwatching it" };
75 Object::Remote->current_loop
77 handle => $sudo_stderr,
83 return ($foreign_stdin, $foreign_stdout, $pid);
88 push @Object::Remote::Connection::Guess, sub {
90 # username followed by @
91 if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
93 return __PACKAGE__->new(@_, target_user => $1);
103 Object::Remote::Connector::LocalSudo - A connector for a local Perl process with
108 Used to create a connector that talks to a Perl process started on the local
109 machine, via sudo as a specific user. Invoked by L<Object::Remote/connect> if
110 the connection spec is a username followed by a @.
114 Inherits arguments from L<Object::Remote::Connector::Local> and provides the
119 When invoked via L<Object::Remote/connect>, specified via the connection spec,
122 The name of the user to run the process as.
124 =head2 password_callback
126 A function that returns the password to be passed on to sudo. Defaults to asking
127 the operator via command line.
129 =head2 sudo_perl_command
131 An arrayref containing a list of strings to be passed to L<IPC::Open3> to open
132 the perl process. Defaults to a specific sudo incantation.