line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3306
|
|
|
3306
|
|
288136198
|
use 5.006; |
|
3306
|
|
|
|
|
15395
|
|
2
|
3306
|
|
|
3306
|
|
21393
|
use strict; |
|
3306
|
|
|
|
|
8468
|
|
|
3306
|
|
|
|
|
80795
|
|
3
|
3306
|
|
|
3306
|
|
22148
|
use warnings; |
|
3306
|
|
|
|
|
9505
|
|
|
3306
|
|
|
|
|
175385
|
|
4
|
|
|
|
|
|
|
package Capture::Tiny; |
5
|
|
|
|
|
|
|
# ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs |
6
|
|
|
|
|
|
|
our $VERSION = '0.47'; # TRIAL |
7
|
3306
|
|
|
3306
|
|
27473
|
use Carp (); |
|
3306
|
|
|
|
|
8225
|
|
|
3306
|
|
|
|
|
80567
|
|
8
|
3306
|
|
|
3306
|
|
26730
|
use Exporter (); |
|
3306
|
|
|
|
|
9752
|
|
|
3306
|
|
|
|
|
62306
|
|
9
|
3306
|
|
|
3306
|
|
1911356
|
use IO::Handle (); |
|
3306
|
|
|
|
|
21964113
|
|
|
3306
|
|
|
|
|
85442
|
|
10
|
3306
|
|
|
3306
|
|
27499
|
use File::Spec (); |
|
3306
|
|
|
|
|
8868
|
|
|
3306
|
|
|
|
|
74148
|
|
11
|
3306
|
|
|
3306
|
|
2508855
|
use File::Temp qw/tempfile tmpnam/; |
|
3306
|
|
|
|
|
52895363
|
|
|
3306
|
|
|
|
|
288028
|
|
12
|
3306
|
|
|
3306
|
|
33876
|
use Scalar::Util qw/reftype blessed/; |
|
3306
|
|
|
|
|
10806
|
|
|
3306
|
|
|
|
|
371898
|
|
13
|
|
|
|
|
|
|
# Get PerlIO or fake it |
14
|
|
|
|
|
|
|
BEGIN { |
15
|
3306
|
|
|
3306
|
|
29101
|
local $@; |
16
|
3306
|
|
|
|
|
22242
|
eval { require PerlIO; PerlIO->can('get_layers') } |
|
3306
|
|
|
|
|
1208711
|
|
17
|
3306
|
50
|
|
|
|
9438
|
or *PerlIO::get_layers = sub { return () }; |
|
0
|
|
|
|
|
0
|
|
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
21
|
|
|
|
|
|
|
# create API subroutines and export them |
22
|
|
|
|
|
|
|
# [do STDOUT flag, do STDERR flag, do merge flag, do tee flag] |
23
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
my %api = ( |
26
|
|
|
|
|
|
|
capture => [1,1,0,0], |
27
|
|
|
|
|
|
|
capture_stdout => [1,0,0,0], |
28
|
|
|
|
|
|
|
capture_stderr => [0,1,0,0], |
29
|
|
|
|
|
|
|
capture_merged => [1,1,1,0], |
30
|
|
|
|
|
|
|
tee => [1,1,0,1], |
31
|
|
|
|
|
|
|
tee_stdout => [1,0,0,1], |
32
|
|
|
|
|
|
|
tee_stderr => [0,1,0,1], |
33
|
|
|
|
|
|
|
tee_merged => [1,1,1,1], |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
for my $sub ( keys %api ) { |
37
|
|
|
|
|
|
|
my $args = join q{, }, @{$api{$sub}}; |
38
|
563047
|
|
|
563047
|
1
|
2387020366
|
eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic |
|
563047
|
|
|
85211
|
1
|
7252429
|
|
|
85211
|
|
|
88506
|
1
|
296940085
|
|
|
85211
|
|
|
85211
|
1
|
676870
|
|
|
88506
|
|
|
139643
|
1
|
15422019
|
|
|
88506
|
|
|
12320
|
1
|
496385
|
|
|
85211
|
|
|
29365
|
1
|
4215523
|
|
|
85211
|
|
|
40865
|
1
|
597207
|
|
|
139643
|
|
|
|
|
7610954
|
|
|
139643
|
|
|
|
|
1178635
|
|
|
12320
|
|
|
|
|
634490
|
|
|
12320
|
|
|
|
|
113383
|
|
|
29365
|
|
|
|
|
1558970
|
|
|
29365
|
|
|
|
|
266566
|
|
|
40865
|
|
|
|
|
2262949
|
|
|
40865
|
|
|
|
|
376799
|
|
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
our @ISA = qw/Exporter/; |
42
|
|
|
|
|
|
|
our @EXPORT_OK = keys %api; |
43
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
46
|
|
|
|
|
|
|
# constants and fixtures |
47
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my $IS_WIN32 = $^O eq 'MSWin32'; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
##our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG}; |
52
|
|
|
|
|
|
|
## |
53
|
|
|
|
|
|
|
##my $DEBUGFH; |
54
|
|
|
|
|
|
|
##open $DEBUGFH, "> DEBUG" if $DEBUG; |
55
|
|
|
|
|
|
|
## |
56
|
|
|
|
|
|
|
##*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0}; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
our $TIMEOUT = 30; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
61
|
|
|
|
|
|
|
# command to tee output -- the argument is a filename that must |
62
|
|
|
|
|
|
|
# be opened to signal that the process is ready to receive input. |
63
|
|
|
|
|
|
|
# This is annoying, but seems to be the best that can be done |
64
|
|
|
|
|
|
|
# as a simple, portable IPC technique |
65
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
66
|
|
|
|
|
|
|
my @cmd = ($^X, '-C0', '-e', <<'HERE'); |
67
|
|
|
|
|
|
|
use Fcntl; |
68
|
|
|
|
|
|
|
$SIG{HUP}=sub{exit}; |
69
|
|
|
|
|
|
|
if ( my $fn=shift ) { |
70
|
|
|
|
|
|
|
sysopen(my $fh, qq{$fn}, O_WRONLY|O_CREAT|O_EXCL) or die $!; |
71
|
|
|
|
|
|
|
print {$fh} $$; |
72
|
|
|
|
|
|
|
close $fh; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
my $buf; while (sysread(STDIN, $buf, 2048)) { |
75
|
|
|
|
|
|
|
syswrite(STDOUT, $buf); syswrite(STDERR, $buf); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
HERE |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
80
|
|
|
|
|
|
|
# filehandle manipulation |
81
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub _relayer { |
84
|
5502797
|
|
|
5502797
|
|
17154203
|
my ($fh, $apply_layers) = @_; |
85
|
|
|
|
|
|
|
# _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n"); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# eliminate pseudo-layers |
88
|
5502797
|
|
|
|
|
34894392
|
binmode( $fh, ":raw" ); |
89
|
|
|
|
|
|
|
# strip off real layers until only :unix is left |
90
|
5502797
|
|
|
|
|
49614400
|
while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { |
91
|
5502797
|
|
|
|
|
47881799
|
binmode( $fh, ":pop" ); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
# apply other layers |
94
|
5502797
|
|
|
|
|
26382755
|
my @to_apply = @$apply_layers; |
95
|
5502797
|
|
|
|
|
14626415
|
shift @to_apply; # eliminate initial :unix |
96
|
|
|
|
|
|
|
# _debug("# applying layers (unix @to_apply) to @{[fileno $fh]}\n"); |
97
|
5502797
|
|
|
|
|
46353157
|
binmode($fh, ":" . join(":",@to_apply)); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _name { |
101
|
0
|
|
|
0
|
|
0
|
my $glob = shift; |
102
|
3306
|
|
|
3306
|
|
27745
|
no strict 'refs'; ## no critic |
|
3306
|
|
|
|
|
8225
|
|
|
3306
|
|
|
|
|
9908744
|
|
103
|
0
|
|
|
|
|
0
|
return *{$glob}{NAME}; |
|
0
|
|
|
|
|
0
|
|
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub _open { |
107
|
7485730
|
50
|
|
7485730
|
|
231551192
|
open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!"; |
108
|
|
|
|
|
|
|
# _debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" ); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _close { |
112
|
|
|
|
|
|
|
# _debug( "# closing " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . " on " . fileno( $_[0] ) . "\n" ); |
113
|
3086260
|
50
|
|
3086260
|
|
59722635
|
close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!"; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my %dup; # cache this so STDIN stays fd0 |
117
|
|
|
|
|
|
|
my %proxy_count; |
118
|
|
|
|
|
|
|
sub _proxy_std { |
119
|
1044168
|
|
|
1044168
|
|
2240981
|
my %proxies; |
120
|
1044168
|
100
|
|
|
|
4521625
|
if ( ! defined fileno STDIN ) { |
121
|
138870
|
|
|
|
|
478335
|
$proxy_count{stdin}++; |
122
|
138870
|
100
|
|
|
|
564086
|
if (defined $dup{stdin}) { |
123
|
31830
|
|
|
|
|
220760
|
_open \*STDIN, "<&=" . fileno($dup{stdin}); |
124
|
|
|
|
|
|
|
# _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
else { |
127
|
107040
|
|
|
|
|
2016201
|
_open \*STDIN, "<" . File::Spec->devnull; |
128
|
|
|
|
|
|
|
# _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
129
|
107040
|
|
|
|
|
1674618
|
_open $dup{stdin} = IO::Handle->new, "<&=STDIN"; |
130
|
|
|
|
|
|
|
} |
131
|
138870
|
|
|
|
|
685024
|
$proxies{stdin} = \*STDIN; |
132
|
138870
|
50
|
|
|
|
1144527
|
binmode(STDIN, ':utf8') if $] >= 5.008; ## no critic |
133
|
|
|
|
|
|
|
} |
134
|
1044168
|
100
|
|
|
|
3705758
|
if ( ! defined fileno STDOUT ) { |
135
|
107040
|
|
|
|
|
343305
|
$proxy_count{stdout}++; |
136
|
107040
|
50
|
|
|
|
457268
|
if (defined $dup{stdout}) { |
137
|
0
|
|
|
|
|
0
|
_open \*STDOUT, ">&=" . fileno($dup{stdout}); |
138
|
|
|
|
|
|
|
# _debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
else { |
141
|
107040
|
|
|
|
|
2113197
|
_open \*STDOUT, ">" . File::Spec->devnull; |
142
|
|
|
|
|
|
|
# _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
143
|
107040
|
|
|
|
|
1703646
|
_open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; |
144
|
|
|
|
|
|
|
} |
145
|
107040
|
|
|
|
|
534787
|
$proxies{stdout} = \*STDOUT; |
146
|
107040
|
50
|
|
|
|
942756
|
binmode(STDOUT, ':utf8') if $] >= 5.008; ## no critic |
147
|
|
|
|
|
|
|
} |
148
|
1044168
|
100
|
|
|
|
3812846
|
if ( ! defined fileno STDERR ) { |
149
|
107040
|
|
|
|
|
386969
|
$proxy_count{stderr}++; |
150
|
107040
|
50
|
|
|
|
430941
|
if (defined $dup{stderr}) { |
151
|
0
|
|
|
|
|
0
|
_open \*STDERR, ">&=" . fileno($dup{stderr}); |
152
|
|
|
|
|
|
|
# _debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
else { |
155
|
107040
|
|
|
|
|
2560849
|
_open \*STDERR, ">" . File::Spec->devnull; |
156
|
|
|
|
|
|
|
# _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
157
|
107040
|
|
|
|
|
1713827
|
_open $dup{stderr} = IO::Handle->new, ">&=STDERR"; |
158
|
|
|
|
|
|
|
} |
159
|
107040
|
|
|
|
|
532048
|
$proxies{stderr} = \*STDERR; |
160
|
107040
|
50
|
|
|
|
1081086
|
binmode(STDERR, ':utf8') if $] >= 5.008; ## no critic |
161
|
|
|
|
|
|
|
} |
162
|
1044168
|
|
|
|
|
4776667
|
return %proxies; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _unproxy { |
166
|
1037603
|
|
|
1037603
|
|
3902515
|
my (%proxies) = @_; |
167
|
|
|
|
|
|
|
# _debug( "# unproxying: " . join(" ", keys %proxies) . "\n" ); |
168
|
1037603
|
|
|
|
|
5931309
|
for my $p ( keys %proxies ) { |
169
|
351270
|
|
|
|
|
1852686
|
$proxy_count{$p}--; |
170
|
|
|
|
|
|
|
# _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); |
171
|
351270
|
100
|
|
|
|
1636674
|
if ( ! $proxy_count{$p} ) { |
172
|
319680
|
|
|
|
|
1451519
|
_close $proxies{$p}; |
173
|
319680
|
50
|
|
|
|
2233592
|
_close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup |
174
|
319680
|
|
|
|
|
4479000
|
delete $dup{$p}; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _copy_std { |
180
|
1044168
|
|
|
1044168
|
|
2366396
|
my %handles; |
181
|
1044168
|
|
|
|
|
4472530
|
for my $h ( qw/stdout stderr stdin/ ) { |
182
|
3132504
|
100
|
66
|
|
|
16977239
|
next if $h eq 'stdin' && ! $IS_WIN32; # WIN32 hangs on tee without STDIN copied |
183
|
2088336
|
50
|
|
|
|
6931051
|
my $redir = $h eq 'stdin' ? "<&" : ">&"; |
184
|
2088336
|
|
|
|
|
14735693
|
_open $handles{$h} = IO::Handle->new(), $redir . uc($h); # ">&STDOUT" or "<&STDIN" |
185
|
|
|
|
|
|
|
} |
186
|
1044168
|
|
|
|
|
5232665
|
return \%handles; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# In some cases we open all (prior to forking) and in others we only open |
190
|
|
|
|
|
|
|
# the output handles (setting up redirection) |
191
|
|
|
|
|
|
|
sub _open_std { |
192
|
2081771
|
|
|
2081771
|
|
8413959
|
my ($handles) = @_; |
193
|
2081771
|
100
|
|
|
|
9587381
|
_open \*STDIN, "<&" . fileno $handles->{stdin} if defined $handles->{stdin}; |
194
|
2081771
|
50
|
|
|
|
25083586
|
_open \*STDOUT, ">&" . fileno $handles->{stdout} if defined $handles->{stdout}; |
195
|
2081771
|
50
|
|
|
|
20476229
|
_open \*STDERR, ">&" . fileno $handles->{stderr} if defined $handles->{stderr}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
199
|
|
|
|
|
|
|
# private subs |
200
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _start_tee { |
203
|
372925
|
|
|
372925
|
|
1619138
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
204
|
|
|
|
|
|
|
# setup pipes |
205
|
372925
|
|
|
|
|
5971037
|
$stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; |
206
|
372925
|
|
|
|
|
43272978
|
pipe $stash->{reader}{$which}, $stash->{tee}{$which}; |
207
|
|
|
|
|
|
|
# _debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " " . fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which}) . " " . fileno( $stash->{reader}{$which}) . "\n" ); |
208
|
372925
|
|
|
|
|
7108776
|
select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush |
209
|
|
|
|
|
|
|
# setup desired redirection for parent and child |
210
|
372925
|
|
|
|
|
1755116
|
$stash->{new}{$which} = $stash->{tee}{$which}; |
211
|
|
|
|
|
|
|
$stash->{child}{$which} = { |
212
|
|
|
|
|
|
|
stdin => $stash->{reader}{$which}, |
213
|
|
|
|
|
|
|
stdout => $stash->{old}{$which}, |
214
|
372925
|
|
|
|
|
3771440
|
stderr => $stash->{capture}{$which}, |
215
|
|
|
|
|
|
|
}; |
216
|
|
|
|
|
|
|
# flag file is used to signal the child is ready |
217
|
372925
|
|
|
|
|
3771352
|
$stash->{flag_files}{$which} = scalar( tmpnam() ) . $$; |
218
|
|
|
|
|
|
|
# execute @cmd as a separate process |
219
|
372925
|
50
|
|
|
|
121838910
|
if ( $IS_WIN32 ) { |
220
|
0
|
|
|
|
|
0
|
my $old_eval_err=$@; |
221
|
0
|
|
|
|
|
0
|
undef $@; |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
0
|
eval "use Win32API::File qw/GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ "; |
224
|
|
|
|
|
|
|
# _debug( "# Win32API::File loaded\n") unless $@; |
225
|
0
|
|
|
|
|
0
|
my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} ); |
226
|
|
|
|
|
|
|
# _debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE(); |
227
|
0
|
|
|
|
|
0
|
my $result = SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0); |
228
|
|
|
|
|
|
|
# _debug( $result ? "# set no-inherit flag on $which tee\n" : ("# can't disable tee handle flag inherit: " . fileLastError() . "\n")); |
229
|
0
|
|
|
|
|
0
|
_open_std( $stash->{child}{$which} ); |
230
|
0
|
|
|
|
|
0
|
$stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which}); |
231
|
|
|
|
|
|
|
# not restoring std here as it all gets redirected again shortly anyway |
232
|
0
|
|
|
|
|
0
|
$@=$old_eval_err; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
else { # use fork |
235
|
372925
|
|
|
|
|
2740031
|
_fork_exec( $which, $stash ); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _fork_exec { |
240
|
372925
|
|
|
372925
|
|
1380009
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
241
|
372925
|
|
|
|
|
595615088
|
my $pid = fork; |
242
|
372925
|
50
|
|
|
|
8582479
|
if ( not defined $pid ) { |
|
|
100
|
|
|
|
|
|
243
|
0
|
|
|
|
|
0
|
Carp::confess "Couldn't fork(): $!"; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
elsif ($pid == 0) { # child |
246
|
|
|
|
|
|
|
# _debug( "# in child process ...\n" ); |
247
|
3282
|
|
|
|
|
230745
|
untie *STDIN; untie *STDOUT; untie *STDERR; |
|
3282
|
|
|
|
|
82809
|
|
|
3282
|
|
|
|
|
57791
|
|
248
|
3282
|
|
|
|
|
208039
|
_close $stash->{tee}{$which}; |
249
|
|
|
|
|
|
|
# _debug( "# redirecting handles in child ...\n" ); |
250
|
3282
|
|
|
|
|
94049
|
_open_std( $stash->{child}{$which} ); |
251
|
|
|
|
|
|
|
# _debug( "# calling exec on command ...\n" ); |
252
|
3282
|
|
|
|
|
0
|
exec @cmd, $stash->{flag_files}{$which}; |
253
|
|
|
|
|
|
|
} |
254
|
369643
|
|
|
|
|
33314641
|
$stash->{pid}{$which} = $pid |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
3306
|
|
|
3306
|
|
2242771
|
my $have_usleep = eval "use Time::HiRes 'usleep'; 1"; |
|
3306
|
|
|
|
|
5003960
|
|
|
3306
|
|
|
|
|
18382
|
|
258
|
|
|
|
|
|
|
sub _files_exist { |
259
|
72516388
|
100
|
|
72516388
|
|
338408275
|
return 1 if @_ == grep { -f } @_; |
|
122178968
|
|
|
|
|
2290197061
|
|
260
|
72078566
|
50
|
|
|
|
94669408774
|
Time::HiRes::usleep(1000) if $have_usleep; |
261
|
72078566
|
|
|
|
|
1059899383
|
return 0; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub _wait_for_tees { |
265
|
218911
|
|
|
218911
|
|
1158174
|
my ($stash) = @_; |
266
|
218911
|
|
|
|
|
974221
|
my $start = time; |
267
|
218911
|
|
|
|
|
738569
|
my @files = values %{$stash->{flag_files}}; |
|
218911
|
|
|
|
|
8058769
|
|
268
|
|
|
|
|
|
|
my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} |
269
|
218911
|
50
|
|
|
|
4075328
|
? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; |
270
|
218911
|
|
33
|
|
|
1503445
|
1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); |
|
|
|
66
|
|
|
|
|
271
|
218911
|
50
|
|
|
|
1242719
|
Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); |
272
|
218911
|
|
|
|
|
62427586
|
unlink $_ for @files; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub _kill_tees { |
276
|
218911
|
|
|
218911
|
|
1207398
|
my ($stash) = @_; |
277
|
218911
|
50
|
|
|
|
1251835
|
if ( $IS_WIN32 ) { |
278
|
|
|
|
|
|
|
# _debug( "# closing handles\n"); |
279
|
0
|
|
|
|
|
0
|
close($_) for values %{ $stash->{tee} }; |
|
0
|
|
|
|
|
0
|
|
280
|
|
|
|
|
|
|
# _debug( "# waiting for subprocesses to finish\n"); |
281
|
0
|
|
|
|
|
0
|
my $start = time; |
282
|
0
|
|
0
|
|
|
0
|
1 until wait == -1 || (time - $start > 30); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
else { |
285
|
218911
|
|
|
|
|
655536
|
_close $_ for values %{ $stash->{tee} }; |
|
218911
|
|
|
|
|
2309806
|
|
286
|
218911
|
|
|
|
|
951113
|
waitpid $_, 0 for values %{ $stash->{pid} }; |
|
218911
|
|
|
|
|
243112157
|
|
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub _slurp { |
291
|
1832073
|
|
|
1832073
|
|
5819843
|
my ($name, $stash) = @_; |
292
|
1832073
|
|
|
|
|
5920007
|
my ($fh, $pos) = map { $stash->{$_}{$name} } qw/capture pos/; |
|
3664146
|
|
|
|
|
16077629
|
|
293
|
|
|
|
|
|
|
# _debug( "# slurping captured $name from " . fileno($fh) . " at pos $pos with layers: @{[PerlIO::get_layers($fh)]}\n"); |
294
|
1832073
|
50
|
|
|
|
10385174
|
seek( $fh, $pos, 0 ) or die "Couldn't seek on capture handle for $name\n"; |
295
|
1832073
|
|
|
|
|
5124256
|
my $text = do { local $/; scalar readline $fh }; |
|
1832073
|
|
|
|
|
11206759
|
|
|
1832073
|
|
|
|
|
55749698
|
|
296
|
1832073
|
100
|
|
|
|
19896865
|
return defined($text) ? $text : ""; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
300
|
|
|
|
|
|
|
# _capture_tee() -- generic main sub for capturing or teeing |
301
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub _capture_tee { |
304
|
|
|
|
|
|
|
# _debug( "# starting _capture_tee with (@_)...\n" ); |
305
|
1044168
|
|
|
1044168
|
|
5072494
|
my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_; |
306
|
1044168
|
100
|
|
|
|
9540637
|
my %do = ($do_stdout ? (stdout => 1) : (), $do_stderr ? (stderr => 1) : ()); |
|
|
100
|
|
|
|
|
|
307
|
1044168
|
50
|
|
|
|
5445495
|
Carp::confess("Custom capture options must be given as key/value pairs\n") |
308
|
|
|
|
|
|
|
unless @opts % 2 == 0; |
309
|
1044168
|
|
|
|
|
4901350
|
my $stash = { capture => { @opts } }; |
310
|
1044168
|
|
|
|
|
3081373
|
for ( keys %{$stash->{capture}} ) { |
|
1044168
|
|
|
|
|
7780720
|
|
311
|
10
|
|
|
|
|
35
|
my $fh = $stash->{capture}{$_}; |
312
|
10
|
50
|
33
|
|
|
166
|
Carp::confess "Custom handle for $_ must be seekable\n" |
|
|
|
33
|
|
|
|
|
313
|
|
|
|
|
|
|
unless ref($fh) eq 'GLOB' || (blessed($fh) && $fh->isa("IO::Seekable")); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
# save existing filehandles and setup captures |
316
|
1044168
|
|
|
|
|
8662322
|
local *CT_ORIG_STDIN = *STDIN ; |
317
|
1044168
|
|
|
|
|
5031334
|
local *CT_ORIG_STDOUT = *STDOUT; |
318
|
1044168
|
|
|
|
|
3310492
|
local *CT_ORIG_STDERR = *STDERR; |
319
|
|
|
|
|
|
|
# find initial layers |
320
|
1044168
|
|
|
|
|
19998901
|
my %layers = ( |
321
|
|
|
|
|
|
|
stdin => [PerlIO::get_layers(\*STDIN) ], |
322
|
|
|
|
|
|
|
stdout => [PerlIO::get_layers(\*STDOUT, output => 1)], |
323
|
|
|
|
|
|
|
stderr => [PerlIO::get_layers(\*STDERR, output => 1)], |
324
|
|
|
|
|
|
|
); |
325
|
|
|
|
|
|
|
# _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
326
|
|
|
|
|
|
|
# get layers from underlying glob of tied filehandles if we can |
327
|
|
|
|
|
|
|
# (this only works for things that work like Tie::StdHandle) |
328
|
1044168
|
100
|
100
|
|
|
10478904
|
$layers{stdout} = [PerlIO::get_layers(tied *STDOUT)] |
329
|
|
|
|
|
|
|
if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB'); |
330
|
1044168
|
100
|
100
|
|
|
7330478
|
$layers{stderr} = [PerlIO::get_layers(tied *STDERR)] |
331
|
|
|
|
|
|
|
if tied(*STDERR) && (reftype tied *STDERR eq 'GLOB'); |
332
|
|
|
|
|
|
|
# _debug( "# tied object corrected layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
333
|
|
|
|
|
|
|
# bypass scalar filehandles and tied handles |
334
|
|
|
|
|
|
|
# localize scalar STDIN to get a proxy to pick up FD0, then restore later to CT_ORIG_STDIN |
335
|
1044168
|
|
|
|
|
2625578
|
my %localize; |
336
|
|
|
|
|
|
|
$localize{stdin}++, local(*STDIN) |
337
|
1044168
|
100
|
|
|
|
2252503
|
if grep { $_ eq 'scalar' } @{$layers{stdin}}; |
|
1927776
|
|
|
|
|
8622183
|
|
|
1044168
|
|
|
|
|
3920382
|
|
338
|
|
|
|
|
|
|
$localize{stdout}++, local(*STDOUT) |
339
|
1044168
|
100
|
100
|
|
|
5821748
|
if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}}; |
|
1968556
|
|
|
|
|
9284446
|
|
|
926297
|
|
|
|
|
3652396
|
|
340
|
|
|
|
|
|
|
$localize{stderr}++, local(*STDERR) |
341
|
1044168
|
100
|
66
|
|
|
7166422
|
if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}}; |
|
1947426
|
|
100
|
|
|
10175647
|
|
|
918092
|
|
|
|
|
3016595
|
|
342
|
1044168
|
100
|
66
|
|
|
6531088
|
$localize{stdin}++, local(*STDIN), _open( \*STDIN, "<&=0") |
343
|
|
|
|
|
|
|
if tied *STDIN && $] >= 5.008; |
344
|
1044168
|
100
|
100
|
|
|
9121346
|
$localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") |
|
|
|
66
|
|
|
|
|
345
|
|
|
|
|
|
|
if $do_stdout && tied *STDOUT && $] >= 5.008; |
346
|
1044168
|
100
|
66
|
|
|
8531705
|
$localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
347
|
|
|
|
|
|
|
if ($do_stderr || $do_merge) && tied *STDERR && $] >= 5.008; |
348
|
|
|
|
|
|
|
# _debug( "# localized $_\n" ) for keys %localize; |
349
|
|
|
|
|
|
|
# proxy any closed/localized handles so we don't use fds 0, 1 or 2 |
350
|
1044168
|
|
|
|
|
4149126
|
my %proxy_std = _proxy_std(); |
351
|
|
|
|
|
|
|
# _debug( "# proxy std: @{ [%proxy_std] }\n" ); |
352
|
|
|
|
|
|
|
# update layers after any proxying |
353
|
1044168
|
100
|
|
|
|
5459906
|
$layers{stdout} = [PerlIO::get_layers(\*STDOUT, output => 1)] if $proxy_std{stdout}; |
354
|
1044168
|
100
|
|
|
|
4301861
|
$layers{stderr} = [PerlIO::get_layers(\*STDERR, output => 1)] if $proxy_std{stderr}; |
355
|
|
|
|
|
|
|
# _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
356
|
|
|
|
|
|
|
# store old handles and setup handles for capture |
357
|
1044168
|
|
|
|
|
3768090
|
$stash->{old} = _copy_std(); |
358
|
1044168
|
|
|
|
|
2501936
|
$stash->{new} = { %{$stash->{old}} }; # default to originals |
|
1044168
|
|
|
|
|
7039223
|
|
359
|
1044168
|
|
|
|
|
4712349
|
for ( keys %do ) { |
360
|
1843158
|
|
66
|
|
|
29339726
|
$stash->{new}{$_} = ($stash->{capture}{$_} ||= File::Temp->new); |
361
|
1843158
|
50
|
|
|
|
1171878234
|
seek( $stash->{capture}{$_}, 0, 2 ) or die "Could not seek on capture handle for $_\n"; |
362
|
1843158
|
|
|
|
|
10511765
|
$stash->{pos}{$_} = tell $stash->{capture}{$_}; |
363
|
|
|
|
|
|
|
# _debug("# will capture $_ on " . fileno($stash->{capture}{$_})."\n" ); |
364
|
1843158
|
100
|
|
|
|
9718067
|
_start_tee( $_ => $stash ) if $do_tee; # tees may change $stash->{new} |
365
|
|
|
|
|
|
|
} |
366
|
1040886
|
100
|
|
|
|
12129959
|
_wait_for_tees( $stash ) if $do_tee; |
367
|
|
|
|
|
|
|
# finalize redirection |
368
|
1040886
|
100
|
|
|
|
4787803
|
$stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge; |
369
|
|
|
|
|
|
|
# _debug( "# redirecting in parent ...\n" ); |
370
|
1040886
|
|
|
|
|
7671508
|
_open_std( $stash->{new} ); |
371
|
|
|
|
|
|
|
# execute user provided code |
372
|
1040886
|
|
|
|
|
6428836
|
my ($exit_code, $inner_error, $outer_error, $orig_pid, @result); |
373
|
|
|
|
|
|
|
{ |
374
|
1040886
|
|
|
|
|
2588553
|
$orig_pid = $$; |
|
1040886
|
|
|
|
|
5589373
|
|
375
|
1040886
|
100
|
|
|
|
6169459
|
local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN |
376
|
|
|
|
|
|
|
# _debug( "# finalizing layers ...\n" ); |
377
|
1040886
|
100
|
|
|
|
8498844
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
378
|
1040886
|
100
|
|
|
|
7497895
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
379
|
|
|
|
|
|
|
# _debug( "# running code $code ...\n" ); |
380
|
1040886
|
|
|
|
|
4145142
|
my $old_eval_err=$@; |
381
|
1040886
|
|
|
|
|
3756521
|
undef $@; |
382
|
1040886
|
|
|
|
|
3128633
|
eval { @result = $code->(); $inner_error = $@ }; |
|
1040886
|
|
|
|
|
11063680
|
|
|
1037602
|
|
|
|
|
2556130071
|
|
383
|
1037603
|
|
|
|
|
7576491
|
$exit_code = $?; # save this for later |
384
|
1037603
|
|
|
|
|
3847483
|
$outer_error = $@; # save this for later |
385
|
1037603
|
100
|
|
|
|
18204867
|
STDOUT->flush if $do_stdout; |
386
|
1037603
|
100
|
|
|
|
11820665
|
STDERR->flush if $do_stderr; |
387
|
1037603
|
|
|
|
|
4906699
|
$@ = $old_eval_err; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
# restore prior filehandles and shut down tees |
390
|
|
|
|
|
|
|
# _debug( "# restoring filehandles ...\n" ); |
391
|
1037603
|
|
|
|
|
8468303
|
_open_std( $stash->{old} ); |
392
|
1037603
|
|
|
|
|
3824389
|
_close( $_ ) for values %{$stash->{old}}; # don't leak fds |
|
1037603
|
|
|
|
|
8542420
|
|
393
|
|
|
|
|
|
|
# shouldn't need relayering originals, but see rt.perl.org #114404 |
394
|
1037603
|
100
|
|
|
|
8121681
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
395
|
1037603
|
100
|
|
|
|
8375758
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
396
|
1037603
|
|
|
|
|
7482919
|
_unproxy( %proxy_std ); |
397
|
|
|
|
|
|
|
# _debug( "# killing tee subprocesses ...\n" ) if $do_tee; |
398
|
1037603
|
100
|
|
|
|
5514949
|
_kill_tees( $stash ) if $do_tee; |
399
|
|
|
|
|
|
|
# return captured output, but shortcut in void context |
400
|
|
|
|
|
|
|
# unless we have to echo output to tied/scalar handles; |
401
|
1037603
|
|
|
|
|
3713436
|
my %got; |
402
|
1037603
|
100
|
66
|
|
|
12784795
|
if ( $orig_pid == $$ and ( defined wantarray or ($do_tee && keys %localize) ) ) { |
|
|
|
33
|
|
|
|
|
403
|
1037600
|
|
|
|
|
6175633
|
for ( keys %do ) { |
404
|
1832073
|
|
|
|
|
11824887
|
_relayer($stash->{capture}{$_}, $layers{$_}); |
405
|
1832073
|
|
|
|
|
8598759
|
$got{$_} = _slurp($_, $stash); |
406
|
|
|
|
|
|
|
# _debug("# slurped " . length($got{$_}) . " bytes from $_\n"); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
print CT_ORIG_STDOUT $got{stdout} |
409
|
1037600
|
50
|
100
|
|
|
9630584
|
if $do_stdout && $do_tee && $localize{stdout}; |
|
|
|
66
|
|
|
|
|
410
|
|
|
|
|
|
|
print CT_ORIG_STDERR $got{stderr} |
411
|
1037600
|
50
|
100
|
|
|
8091207
|
if $do_stderr && $do_tee && $localize{stderr}; |
|
|
|
66
|
|
|
|
|
412
|
|
|
|
|
|
|
} |
413
|
1037603
|
|
|
|
|
3744077
|
$? = $exit_code; |
414
|
1037603
|
100
|
|
|
|
3520141
|
$@ = $inner_error if $inner_error; |
415
|
1037603
|
100
|
|
|
|
3295183
|
die $outer_error if $outer_error; |
416
|
|
|
|
|
|
|
# _debug( "# ending _capture_tee with (@_)...\n" ); |
417
|
1037602
|
100
|
|
|
|
4035847
|
return unless defined wantarray; |
418
|
1037599
|
|
|
|
|
2392645
|
my @return; |
419
|
1037599
|
100
|
|
|
|
5347269
|
push @return, $got{stdout} if $do_stdout; |
420
|
1037599
|
100
|
100
|
|
|
7563232
|
push @return, $got{stderr} if $do_stderr && ! $do_merge; |
421
|
1037599
|
|
|
|
|
3157578
|
push @return, @result; |
422
|
1037599
|
100
|
|
|
|
39753031
|
return wantarray ? @return : $return[0]; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
1; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
__END__ |