localsudo connector was missing "use" for Object::Remote::Logging
[scpubgit/Object-Remote.git] / lib / Object / Remote / Connector / LocalSudo.pm
1 package Object::Remote::Connector::LocalSudo;
2         
3 use Object::Remote::Logging qw (:log :dlog);
4 use Symbol qw(gensym);
5 use Module::Runtime qw(use_module);
6 use IPC::Open3;
7 use Moo;
8
9 extends 'Object::Remote::Connector::Local';
10
11 has target_user => (is => 'ro', required => 1);
12
13 has password_callback => (is => 'lazy');
14
15 sub _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 }
23
24 has sudo_perl_command => (is => 'lazy');
25
26 sub _build_sudo_perl_command {
27   my ($self) = @_;
28   return
29     'sudo', '-S', '-u', $self->target_user, '-p', "[sudo] password please\n",
30     'perl', '-MPOSIX=dup2',
31             '-e', 'print STDERR "GO\n"; exec(@ARGV);',
32     $self->perl_command;
33 }
34
35 sub _start_perl {
36   my $self = shift;
37   my $sudo_stderr = gensym;
38   my $pid = open3(
39     my $foreign_stdin,
40     my $foreign_stdout,
41     $sudo_stderr,
42     @{$self->sudo_perl_command}
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>);
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     }
61   } else {
62     die "Got inexplicable line ${line} trying to sudo";
63   };
64   Object::Remote->current_loop
65                 ->watch_io(
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" };
71                         print STDERR $buf;
72                         log_trace { "LocalSudo: print() to STDERR is done" };                   
73                       } else {
74                         log_debug { "LocalSudo: received EOF or error on file handle, unwatching it" };
75                         Object::Remote->current_loop
76                                       ->unwatch_io(
77                                           handle => $sudo_stderr,
78                                           on_read_ready => 1
79                                         );
80                       }
81                     }
82                   );
83   return ($foreign_stdin, $foreign_stdout, $pid);
84 };
85
86 no warnings 'once';
87
88 push @Object::Remote::Connection::Guess, sub {
89   for ($_[0]) {
90     # username followed by @
91     if (defined and !ref and /^ ([^\@]*?) \@ $/x) {
92       shift(@_);
93       return __PACKAGE__->new(@_, target_user => $1);
94     }
95   }
96   return;
97 };
98
99 1;