line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Object::Remote::Connector::LocalSudo; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
5194
|
use Object::Remote::Logging qw (:log :dlog); |
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
74
|
|
4
|
11
|
|
|
11
|
|
76
|
use Symbol qw(gensym); |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
581
|
|
5
|
11
|
|
|
11
|
|
63
|
use Module::Runtime qw(use_module); |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
69
|
|
6
|
11
|
|
|
11
|
|
463
|
use IPC::Open3; |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
542
|
|
7
|
11
|
|
|
11
|
|
60
|
use Moo; |
|
11
|
|
|
|
|
113
|
|
|
11
|
|
|
|
|
66
|
|
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
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
17
|
0
|
|
|
|
|
|
my $pw_prompt = use_module('Object::Remote::Prompt')->can('prompt_pw'); |
18
|
0
|
|
|
|
|
|
my $user = $self->target_user; |
19
|
|
|
|
|
|
|
return sub { |
20
|
0
|
|
|
0
|
|
|
$pw_prompt->("sudo password for ${user}", undef, { cache => 1 }) |
21
|
|
|
|
|
|
|
} |
22
|
0
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
has sudo_perl_command => (is => 'lazy'); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub _build_sudo_perl_command { |
27
|
0
|
|
|
0
|
|
|
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
|
0
|
|
|
|
|
|
@{$self->perl_command}, |
|
0
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
]; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub _start_perl { |
37
|
0
|
|
|
0
|
|
|
my $self = shift; |
38
|
0
|
|
|
|
|
|
my $sudo_stderr = gensym; |
39
|
|
|
|
|
|
|
my $pid = open3( |
40
|
|
|
|
|
|
|
my $foreign_stdin, |
41
|
|
|
|
|
|
|
my $foreign_stdout, |
42
|
|
|
|
|
|
|
$sudo_stderr, |
43
|
0
|
0
|
|
|
|
|
@{$self->sudo_perl_command} |
|
0
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
) or die "open3 failed: $!"; |
45
|
0
|
|
|
|
|
|
chomp(my $line = <$sudo_stderr>); |
46
|
0
|
0
|
|
|
|
|
if ($line eq "GO") { |
|
|
0
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# started already, we're good |
48
|
|
|
|
|
|
|
} elsif ($line =~ /\[sudo\]/) { |
49
|
0
|
|
|
|
|
|
my $cb = $self->password_callback; |
50
|
0
|
0
|
|
|
|
|
die "sudo sent ${line} but we have no password callback" |
51
|
|
|
|
|
|
|
unless $cb; |
52
|
0
|
|
|
|
|
|
print $foreign_stdin $cb->($line, @_), "\n"; |
53
|
0
|
|
|
|
|
|
chomp($line = <$sudo_stderr>); |
54
|
0
|
0
|
0
|
|
|
|
if ($line and $line ne 'GO') { |
|
|
0
|
|
|
|
|
|
55
|
0
|
|
|
|
|
|
die "sent password and expected newline from sudo, got ${line}"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
elsif (not $line) { |
58
|
0
|
|
|
|
|
|
chomp($line = <$sudo_stderr>); |
59
|
0
|
0
|
|
|
|
|
die "sent password but next line was ${line}" |
60
|
|
|
|
|
|
|
unless $line eq "GO"; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
} else { |
63
|
0
|
|
|
|
|
|
die "Got inexplicable line ${line} trying to sudo"; |
64
|
|
|
|
|
|
|
}; |
65
|
|
|
|
|
|
|
Object::Remote->current_loop |
66
|
|
|
|
|
|
|
->watch_io( |
67
|
|
|
|
|
|
|
handle => $sudo_stderr, |
68
|
|
|
|
|
|
|
on_read_ready => sub { |
69
|
0
|
|
|
0
|
|
|
Dlog_debug { "LocalSudo: Preparing to read data from $_" } $sudo_stderr; |
|
0
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
|
if (sysread($sudo_stderr, my $buf, 32768) > 0) { |
71
|
0
|
|
|
|
|
|
log_trace { "LocalSudo: successfully read data, printing it to STDERR" }; |
|
0
|
|
|
|
|
|
|
72
|
0
|
|
|
|
|
|
print STDERR $buf; |
73
|
0
|
|
|
|
|
|
log_trace { "LocalSudo: print() to STDERR is done" }; |
|
0
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
} else { |
75
|
0
|
|
|
|
|
|
log_debug { "LocalSudo: received EOF or error on file handle, unwatching it" }; |
|
0
|
|
|
|
|
|
|
76
|
0
|
|
|
|
|
|
Object::Remote->current_loop |
77
|
|
|
|
|
|
|
->unwatch_io( |
78
|
|
|
|
|
|
|
handle => $sudo_stderr, |
79
|
|
|
|
|
|
|
on_read_ready => 1 |
80
|
|
|
|
|
|
|
); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
0
|
|
|
|
|
|
); |
84
|
0
|
|
|
|
|
|
return ($foreign_stdin, $foreign_stdout, $pid); |
85
|
|
|
|
|
|
|
}; |
86
|
|
|
|
|
|
|
|
87
|
11
|
|
|
11
|
|
9764
|
no warnings 'once'; |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
1732
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
push @Object::Remote::Connection::Guess, sub { |
90
|
|
|
|
|
|
|
for ($_[0]) { |
91
|
|
|
|
|
|
|
# username followed by @ |
92
|
|
|
|
|
|
|
if (defined and !ref and /^ ([^\@]*?) \@ $/x) { |
93
|
|
|
|
|
|
|
shift(@_); |
94
|
|
|
|
|
|
|
return __PACKAGE__->new(@_, target_user => $1); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
return; |
98
|
|
|
|
|
|
|
}; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1; |