| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
132
|
|
|
132
|
|
2040
|
use v5.36; |
|
|
132
|
|
|
|
|
709
|
|
|
2
|
|
|
|
|
|
|
package Remote::Perl::Transport; |
|
3
|
|
|
|
|
|
|
our $VERSION = '0.004'; |
|
4
|
|
|
|
|
|
|
|
|
5
|
132
|
|
|
132
|
|
838
|
use autodie qw(open close); |
|
|
132
|
|
|
|
|
219
|
|
|
|
132
|
|
|
|
|
939
|
|
|
6
|
132
|
|
|
132
|
|
68310
|
use IO::Select; |
|
|
132
|
|
|
|
|
1028
|
|
|
|
132
|
|
|
|
|
8982
|
|
|
7
|
132
|
|
|
132
|
|
82112
|
use POSIX qw(WNOHANG); |
|
|
132
|
|
|
|
|
927010
|
|
|
|
132
|
|
|
|
|
894
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# Wraps a bidirectional pipe to a child process. |
|
10
|
|
|
|
|
|
|
# |
|
11
|
|
|
|
|
|
|
# The child's stdin <- we write protocol bytes |
|
12
|
|
|
|
|
|
|
# The child's stdout -> we read protocol bytes |
|
13
|
|
|
|
|
|
|
# The child's stderr -> we read for debug/error logging (separate channel) |
|
14
|
|
|
|
|
|
|
# |
|
15
|
|
|
|
|
|
|
# All handles are set to binmode (raw bytes, no encoding layers). |
|
16
|
|
|
|
|
|
|
|
|
17
|
1522
|
|
|
1522
|
0
|
3552
|
sub new($class, %args) { |
|
|
1522
|
|
|
|
|
4808
|
|
|
|
1522
|
|
|
|
|
3664
|
|
|
|
1522
|
|
|
|
|
2875
|
|
|
18
|
|
|
|
|
|
|
# cmd => arrayref of command + arguments (no shell expansion) |
|
19
|
|
|
|
|
|
|
return bless { |
|
20
|
|
|
|
|
|
|
cmd => $args{cmd}, |
|
21
|
1522
|
|
|
|
|
23050
|
pid => undef, |
|
22
|
|
|
|
|
|
|
in_fh => undef, # write end -> child stdin |
|
23
|
|
|
|
|
|
|
out_fh => undef, # read end <- child stdout |
|
24
|
|
|
|
|
|
|
err_fh => undef, # read end <- child stderr |
|
25
|
|
|
|
|
|
|
}, $class; |
|
26
|
|
|
|
|
|
|
} |
|
27
|
|
|
|
|
|
|
|
|
28
|
1522
|
|
|
1522
|
0
|
3038
|
sub connect($self) { |
|
|
1522
|
|
|
|
|
3479
|
|
|
|
1522
|
|
|
|
|
3648
|
|
|
29
|
1522
|
|
|
|
|
36981
|
$SIG{PIPE} = 'IGNORE'; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# We fork+exec manually instead of using open3 so we can call setpgrp() |
|
32
|
|
|
|
|
|
|
# in the child. This puts the transport process (e.g. ssh) in its own |
|
33
|
|
|
|
|
|
|
# process group, preventing terminal-generated signals (Ctrl-C -> SIGINT) |
|
34
|
|
|
|
|
|
|
# from reaching it directly. Signals are instead forwarded through the |
|
35
|
|
|
|
|
|
|
# protocol by the parent's signal handlers. |
|
36
|
1522
|
50
|
|
|
|
102304
|
pipe(my $child_in_r, my $child_in_w) or die "pipe: $!\n"; |
|
37
|
1522
|
50
|
|
|
|
48183
|
pipe(my $child_out_r, my $child_out_w) or die "pipe: $!\n"; |
|
38
|
1522
|
50
|
|
|
|
47719
|
pipe(my $child_err_r, my $child_err_w) or die "pipe: $!\n"; |
|
39
|
|
|
|
|
|
|
|
|
40
|
1522
|
|
50
|
|
|
4701388
|
my $pid = fork() // die "fork: $!\n"; |
|
41
|
|
|
|
|
|
|
|
|
42
|
1522
|
100
|
|
|
|
23830
|
if ($pid == 0) { |
|
43
|
|
|
|
|
|
|
# Child: new process group so parent's signals don't reach us. |
|
44
|
99
|
|
|
|
|
8980
|
setpgrp(0, 0); |
|
45
|
|
|
|
|
|
|
|
|
46
|
99
|
|
|
|
|
10764
|
close($child_in_w); |
|
47
|
99
|
|
|
|
|
120017
|
close($child_out_r); |
|
48
|
99
|
|
|
|
|
9614
|
close($child_err_r); |
|
49
|
|
|
|
|
|
|
|
|
50
|
99
|
50
|
|
|
|
9682
|
open(STDIN, '<&', $child_in_r) or die "dup stdin: $!\n"; |
|
51
|
99
|
50
|
|
|
|
423524
|
open(STDOUT, '>&', $child_out_w) or die "dup stdout: $!\n"; |
|
52
|
99
|
50
|
|
|
|
13813
|
open(STDERR, '>&', $child_err_w) or die "dup stderr: $!\n"; |
|
53
|
99
|
|
|
|
|
15273
|
close($_) for $child_in_r, $child_out_w, $child_err_w; |
|
54
|
|
|
|
|
|
|
|
|
55
|
99
|
0
|
|
|
|
15218
|
exec(@{ $self->{cmd} }) or die "exec: $!\n"; |
|
|
99
|
|
|
|
|
0
|
|
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
1423
|
|
|
|
|
201012
|
close($child_in_r); |
|
59
|
1423
|
|
|
|
|
1492570
|
close($child_out_w); |
|
60
|
1423
|
|
|
|
|
199330
|
close($child_err_w); |
|
61
|
|
|
|
|
|
|
|
|
62
|
1423
|
|
|
|
|
85538
|
binmode($child_in_w); |
|
63
|
1423
|
|
|
|
|
8191
|
binmode($child_out_r); |
|
64
|
1423
|
|
|
|
|
4399
|
binmode($child_err_r); |
|
65
|
|
|
|
|
|
|
|
|
66
|
1423
|
|
|
|
|
10386
|
$self->{pid} = $pid; |
|
67
|
1423
|
|
|
|
|
12277
|
$self->{in_fh} = $child_in_w; |
|
68
|
1423
|
|
|
|
|
9183
|
$self->{out_fh} = $child_out_r; |
|
69
|
1423
|
|
|
|
|
362829
|
$self->{err_fh} = $child_err_r; |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# Write raw bytes to child stdin. Returns number of bytes written. |
|
73
|
8965
|
|
|
8965
|
0
|
20680
|
sub write_bytes($self, $data) { |
|
|
8965
|
|
|
|
|
17641
|
|
|
|
8965
|
|
|
|
|
25652
|
|
|
|
8965
|
|
|
|
|
13522
|
|
|
74
|
8965
|
|
|
|
|
20249
|
my $total = length($data); |
|
75
|
8965
|
|
|
|
|
16617
|
my $written = 0; |
|
76
|
8965
|
|
|
|
|
27881
|
while ($written < $total) { |
|
77
|
8965
|
|
|
|
|
1068205
|
my $n = syswrite($self->{in_fh}, $data, $total - $written, $written); |
|
78
|
8965
|
50
|
|
|
|
42461
|
die "syswrite: $!\n" unless defined $n; |
|
79
|
8965
|
|
|
|
|
29944
|
$written += $n; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
8965
|
|
|
|
|
50678
|
return $written; |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Read up to $len bytes from child stdout. |
|
85
|
|
|
|
|
|
|
# Returns undef on EOF, dies on error. |
|
86
|
8187
|
|
|
8187
|
0
|
18189
|
sub read_bytes($self, $len) { |
|
|
8187
|
|
|
|
|
19567
|
|
|
|
8187
|
|
|
|
|
14732
|
|
|
|
8187
|
|
|
|
|
12799
|
|
|
87
|
8187
|
|
|
|
|
11422
|
my $data; |
|
88
|
8187
|
|
|
|
|
107085
|
my $n = sysread($self->{out_fh}, $data, $len); |
|
89
|
8187
|
50
|
|
|
|
32180
|
die "sysread: $!\n" unless defined $n; |
|
90
|
8187
|
100
|
|
|
|
23482
|
return if $n == 0; # EOF |
|
91
|
8186
|
|
|
|
|
39713
|
return $data; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Read up to $len bytes from child stderr (non-blocking). |
|
95
|
|
|
|
|
|
|
# Returns '' if nothing available, undef on EOF. |
|
96
|
1
|
|
|
1
|
0
|
3
|
sub read_stderr($self, $len = 4096) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
5
|
|
|
97
|
1
|
50
|
|
|
|
11
|
return '' unless $self->stderr_ready(0); |
|
98
|
1
|
|
|
|
|
116
|
my $data; |
|
99
|
1
|
|
|
|
|
21
|
my $n = sysread($self->{err_fh}, $data, $len); |
|
100
|
1
|
50
|
|
|
|
7
|
die "sysread stderr: $!\n" unless defined $n; |
|
101
|
1
|
50
|
|
|
|
4
|
return if $n == 0; |
|
102
|
1
|
|
|
|
|
6
|
return $data; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Return true if child stderr has data ready within $timeout seconds. |
|
106
|
3
|
|
|
3
|
0
|
11
|
sub stderr_ready($self, $timeout = 0) { |
|
|
3
|
|
|
|
|
12
|
|
|
|
3
|
|
|
|
|
10
|
|
|
|
3
|
|
|
|
|
27
|
|
|
107
|
3
|
|
|
|
|
18
|
return IO::Select->new($self->{err_fh})->can_read($timeout); |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Expose raw filehandle and pid for callers building their own IO::Select sets. |
|
111
|
13939
|
|
|
13939
|
0
|
22171
|
sub out_fh($self) { $self->{out_fh} } |
|
|
13939
|
|
|
|
|
20296
|
|
|
|
13939
|
|
|
|
|
18959
|
|
|
|
13939
|
|
|
|
|
155286
|
|
|
112
|
4
|
|
|
4
|
0
|
16
|
sub pid($self) { $self->{pid} } |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
32
|
|
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Close stdin (signals EOF to child) and wait for it to exit. |
|
115
|
1422
|
|
|
1422
|
0
|
2895
|
sub disconnect($self) { |
|
|
1422
|
|
|
|
|
2533
|
|
|
|
1422
|
|
|
|
|
2161
|
|
|
116
|
1422
|
50
|
|
|
|
4227
|
if ($self->{in_fh}) { |
|
117
|
1422
|
|
|
|
|
11652
|
close($self->{in_fh}); |
|
118
|
1422
|
|
|
|
|
219097
|
$self->{in_fh} = undef; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
1422
|
|
|
|
|
16849
|
$self->_wait_for_child; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
1423
|
|
|
1423
|
|
2628
|
sub _wait_for_child($self) { |
|
|
1423
|
|
|
|
|
2631
|
|
|
|
1423
|
|
|
|
|
2220
|
|
|
124
|
1423
|
50
|
|
|
|
8021
|
return unless $self->{pid}; |
|
125
|
1423
|
|
|
|
|
3076
|
my $pid = $self->{pid}; |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Poll for up to 5 seconds, then escalate. |
|
128
|
1423
|
|
|
|
|
4066
|
my $deadline = time + 5; |
|
129
|
1423
|
|
|
|
|
4674
|
while (time < $deadline) { |
|
130
|
5748
|
|
|
|
|
279341
|
my $result = waitpid($pid, WNOHANG); |
|
131
|
5748
|
100
|
|
|
|
22221
|
if ($result == $pid) { |
|
132
|
1423
|
|
|
|
|
9488
|
$self->{pid} = undef; |
|
133
|
1423
|
|
|
|
|
21591
|
return $?; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
4325
|
|
|
|
|
219145081
|
select(undef, undef, undef, 0.05); # 50 ms sleep |
|
136
|
|
|
|
|
|
|
} |
|
137
|
0
|
|
|
|
|
0
|
kill('TERM', $pid); |
|
138
|
0
|
|
|
|
|
0
|
sleep 1; |
|
139
|
0
|
|
|
|
|
0
|
waitpid($pid, WNOHANG); |
|
140
|
0
|
|
|
|
|
0
|
$self->{pid} = undef; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
1400
|
|
|
1400
|
|
8570
|
sub DESTROY($self) { |
|
|
1400
|
|
|
|
|
6091
|
|
|
|
1400
|
|
|
|
|
5766
|
|
|
144
|
1400
|
100
|
|
|
|
10951
|
if ($self->{in_fh}) { |
|
145
|
1
|
|
|
|
|
17
|
close($self->{in_fh}); |
|
146
|
1
|
|
|
|
|
157
|
$self->{in_fh} = undef; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
1400
|
100
|
|
|
|
155343
|
$self->_wait_for_child if $self->{pid}; |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
1; |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
__END__ |