line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3306
|
|
|
3306
|
|
54272476
|
use 5.006; |
|
3306
|
|
|
|
|
7987
|
|
2
|
3306
|
|
|
3306
|
|
10091
|
use strict; |
|
3306
|
|
|
|
|
3628
|
|
|
3306
|
|
|
|
|
61837
|
|
3
|
3306
|
|
|
3306
|
|
9758
|
use warnings; |
|
3306
|
|
|
|
|
3146
|
|
|
3306
|
|
|
|
|
123020
|
|
4
|
|
|
|
|
|
|
package Capture::Tiny; |
5
|
|
|
|
|
|
|
# ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs |
6
|
|
|
|
|
|
|
our $VERSION = '0.45'; # TRIAL |
7
|
3306
|
|
|
3306
|
|
10910
|
use Carp (); |
|
3306
|
|
|
|
|
3544
|
|
|
3306
|
|
|
|
|
38173
|
|
8
|
3306
|
|
|
3306
|
|
8550
|
use Exporter (); |
|
3306
|
|
|
|
|
4513
|
|
|
3306
|
|
|
|
|
36882
|
|
9
|
3306
|
|
|
3306
|
|
1559881
|
use IO::Handle (); |
|
3306
|
|
|
|
|
16642450
|
|
|
3306
|
|
|
|
|
60691
|
|
10
|
3306
|
|
|
3306
|
|
15795
|
use File::Spec (); |
|
3306
|
|
|
|
|
4028
|
|
|
3306
|
|
|
|
|
52976
|
|
11
|
3306
|
|
|
3306
|
|
2171464
|
use File::Temp qw/tempfile tmpnam/; |
|
3306
|
|
|
|
|
37932791
|
|
|
3306
|
|
|
|
|
192048
|
|
12
|
3306
|
|
|
3306
|
|
16928
|
use Scalar::Util qw/reftype blessed/; |
|
3306
|
|
|
|
|
4031
|
|
|
3306
|
|
|
|
|
226010
|
|
13
|
|
|
|
|
|
|
# Get PerlIO or fake it |
14
|
|
|
|
|
|
|
BEGIN { |
15
|
3306
|
|
|
3306
|
|
24601
|
local $@; |
16
|
3306
|
|
|
|
|
12018
|
eval { require PerlIO; PerlIO->can('get_layers') } |
|
3306
|
|
|
|
|
800083
|
|
17
|
3306
|
50
|
|
|
|
4596
|
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
|
911894525
|
eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic |
|
563047
|
|
|
85211
|
1
|
2384039
|
|
|
85211
|
|
|
88506
|
1
|
94427903
|
|
|
85211
|
|
|
85211
|
1
|
279972
|
|
|
88506
|
|
|
139643
|
1
|
9706089
|
|
|
88506
|
|
|
12320
|
1
|
289713
|
|
|
85211
|
|
|
29365
|
1
|
2502501
|
|
|
85211
|
|
|
40865
|
1
|
281374
|
|
|
139643
|
|
|
|
|
4534950
|
|
|
139643
|
|
|
|
|
618780
|
|
|
12320
|
|
|
|
|
411046
|
|
|
12320
|
|
|
|
|
59489
|
|
|
29365
|
|
|
|
|
942103
|
|
|
29365
|
|
|
|
|
129309
|
|
|
40865
|
|
|
|
|
1323357
|
|
|
40865
|
|
|
|
|
187924
|
|
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
|
|
5638593
|
my ($fh, $apply_layers) = @_; |
85
|
|
|
|
|
|
|
# _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n"); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# eliminate pseudo-layers |
88
|
5502797
|
|
|
|
|
15054380
|
binmode( $fh, ":raw" ); |
89
|
|
|
|
|
|
|
# strip off real layers until only :unix is left |
90
|
5502797
|
|
|
|
|
24127961
|
while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { |
91
|
5502797
|
|
|
|
|
22122139
|
binmode( $fh, ":pop" ); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
# apply other layers |
94
|
5502797
|
|
|
|
|
10498016
|
my @to_apply = @$apply_layers; |
95
|
5502797
|
|
|
|
|
4431066
|
shift @to_apply; # eliminate initial :unix |
96
|
|
|
|
|
|
|
# _debug("# applying layers (unix @to_apply) to @{[fileno $fh]}\n"); |
97
|
5502797
|
|
|
|
|
20109078
|
binmode($fh, ":" . join(":",@to_apply)); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _name { |
101
|
0
|
|
|
0
|
|
0
|
my $glob = shift; |
102
|
3306
|
|
|
3306
|
|
12659
|
no strict 'refs'; ## no critic |
|
3306
|
|
|
|
|
3306
|
|
|
3306
|
|
|
|
|
6494806
|
|
103
|
0
|
|
|
|
|
0
|
return *{$glob}{NAME}; |
|
0
|
|
|
|
|
0
|
|
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub _open { |
107
|
7485730
|
50
|
|
7485730
|
|
114044573
|
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
|
|
23214867
|
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
|
|
832841
|
my %proxies; |
120
|
1044168
|
100
|
|
|
|
1992308
|
if ( ! defined fileno STDIN ) { |
121
|
138870
|
|
|
|
|
249470
|
$proxy_count{stdin}++; |
122
|
138870
|
100
|
|
|
|
282326
|
if (defined $dup{stdin}) { |
123
|
31830
|
|
|
|
|
117233
|
_open \*STDIN, "<&=" . fileno($dup{stdin}); |
124
|
|
|
|
|
|
|
# _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
else { |
127
|
107040
|
|
|
|
|
1428023
|
_open \*STDIN, "<" . File::Spec->devnull; |
128
|
|
|
|
|
|
|
# _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
129
|
107040
|
|
|
|
|
1068811
|
_open $dup{stdin} = IO::Handle->new, "<&=STDIN"; |
130
|
|
|
|
|
|
|
} |
131
|
138870
|
|
|
|
|
301519
|
$proxies{stdin} = \*STDIN; |
132
|
138870
|
50
|
|
|
|
700634
|
binmode(STDIN, ':utf8') if $] >= 5.008; ## no critic |
133
|
|
|
|
|
|
|
} |
134
|
1044168
|
100
|
|
|
|
1846758
|
if ( ! defined fileno STDOUT ) { |
135
|
107040
|
|
|
|
|
146996
|
$proxy_count{stdout}++; |
136
|
107040
|
50
|
|
|
|
190841
|
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
|
|
|
|
|
1045432
|
_open \*STDOUT, ">" . File::Spec->devnull; |
142
|
|
|
|
|
|
|
# _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
143
|
107040
|
|
|
|
|
815189
|
_open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; |
144
|
|
|
|
|
|
|
} |
145
|
107040
|
|
|
|
|
188460
|
$proxies{stdout} = \*STDOUT; |
146
|
107040
|
50
|
|
|
|
491255
|
binmode(STDOUT, ':utf8') if $] >= 5.008; ## no critic |
147
|
|
|
|
|
|
|
} |
148
|
1044168
|
100
|
|
|
|
1683535
|
if ( ! defined fileno STDERR ) { |
149
|
107040
|
|
|
|
|
139889
|
$proxy_count{stderr}++; |
150
|
107040
|
50
|
|
|
|
190585
|
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
|
|
|
|
|
1020393
|
_open \*STDERR, ">" . File::Spec->devnull; |
156
|
|
|
|
|
|
|
# _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
157
|
107040
|
|
|
|
|
836409
|
_open $dup{stderr} = IO::Handle->new, ">&=STDERR"; |
158
|
|
|
|
|
|
|
} |
159
|
107040
|
|
|
|
|
207632
|
$proxies{stderr} = \*STDERR; |
160
|
107040
|
50
|
|
|
|
538816
|
binmode(STDERR, ':utf8') if $] >= 5.008; ## no critic |
161
|
|
|
|
|
|
|
} |
162
|
1044168
|
|
|
|
|
2443919
|
return %proxies; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _unproxy { |
166
|
1037603
|
|
|
1037603
|
|
2091766
|
my (%proxies) = @_; |
167
|
|
|
|
|
|
|
# _debug( "# unproxying: " . join(" ", keys %proxies) . "\n" ); |
168
|
1037603
|
|
|
|
|
2358061
|
for my $p ( keys %proxies ) { |
169
|
351270
|
|
|
|
|
661615
|
$proxy_count{$p}--; |
170
|
|
|
|
|
|
|
# _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); |
171
|
351270
|
100
|
|
|
|
797331
|
if ( ! $proxy_count{$p} ) { |
172
|
319680
|
|
|
|
|
620404
|
_close $proxies{$p}; |
173
|
319680
|
50
|
|
|
|
957948
|
_close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup |
174
|
319680
|
|
|
|
|
1817357
|
delete $dup{$p}; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _copy_std { |
180
|
1044168
|
|
|
1044168
|
|
824333
|
my %handles; |
181
|
1044168
|
|
|
|
|
1913022
|
for my $h ( qw/stdout stderr stdin/ ) { |
182
|
3132504
|
100
|
66
|
|
|
8725937
|
next if $h eq 'stdin' && ! $IS_WIN32; # WIN32 hangs on tee without STDIN copied |
183
|
2088336
|
50
|
|
|
|
3096771
|
my $redir = $h eq 'stdin' ? "<&" : ">&"; |
184
|
2088336
|
|
|
|
|
7653683
|
_open $handles{$h} = IO::Handle->new(), $redir . uc($h); # ">&STDOUT" or "<&STDIN" |
185
|
|
|
|
|
|
|
} |
186
|
1044168
|
|
|
|
|
2219989
|
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
|
|
2628538
|
my ($handles) = @_; |
193
|
2081771
|
100
|
|
|
|
4159990
|
_open \*STDIN, "<&" . fileno $handles->{stdin} if defined $handles->{stdin}; |
194
|
2081771
|
50
|
|
|
|
12037608
|
_open \*STDOUT, ">&" . fileno $handles->{stdout} if defined $handles->{stdout}; |
195
|
2081771
|
50
|
|
|
|
8359499
|
_open \*STDERR, ">&" . fileno $handles->{stderr} if defined $handles->{stderr}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
199
|
|
|
|
|
|
|
# private subs |
200
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _start_tee { |
203
|
372925
|
|
|
372925
|
|
647240
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
204
|
|
|
|
|
|
|
# setup pipes |
205
|
372925
|
|
|
|
|
3944125
|
$stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; |
206
|
372925
|
|
|
|
|
20717011
|
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
|
|
|
|
|
3337588
|
select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush |
209
|
|
|
|
|
|
|
# setup desired redirection for parent and child |
210
|
372925
|
|
|
|
|
724322
|
$stash->{new}{$which} = $stash->{tee}{$which}; |
211
|
|
|
|
|
|
|
$stash->{child}{$which} = { |
212
|
|
|
|
|
|
|
stdin => $stash->{reader}{$which}, |
213
|
|
|
|
|
|
|
stdout => $stash->{old}{$which}, |
214
|
372925
|
|
|
|
|
1533949
|
stderr => $stash->{capture}{$which}, |
215
|
|
|
|
|
|
|
}; |
216
|
|
|
|
|
|
|
# flag file is used to signal the child is ready |
217
|
372925
|
|
|
|
|
1636792
|
$stash->{flag_files}{$which} = scalar tmpnam(); |
218
|
|
|
|
|
|
|
# execute @cmd as a separate process |
219
|
372925
|
50
|
|
|
|
62143488
|
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
|
|
|
|
|
1211996
|
_fork_exec( $which, $stash ); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _fork_exec { |
240
|
372925
|
|
|
372925
|
|
505085
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
241
|
372925
|
|
|
|
|
179820061
|
my $pid = fork; |
242
|
372925
|
50
|
|
|
|
4158925
|
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
|
|
|
|
|
113063
|
untie *STDIN; untie *STDOUT; untie *STDERR; |
|
3282
|
|
|
|
|
28588
|
|
|
3282
|
|
|
|
|
25385
|
|
248
|
3282
|
|
|
|
|
107559
|
_close $stash->{tee}{$which}; |
249
|
|
|
|
|
|
|
# _debug( "# redirecting handles in child ...\n" ); |
250
|
3282
|
|
|
|
|
50056
|
_open_std( $stash->{child}{$which} ); |
251
|
|
|
|
|
|
|
# _debug( "# calling exec on command ...\n" ); |
252
|
3282
|
|
|
|
|
0
|
exec @cmd, $stash->{flag_files}{$which}; |
253
|
|
|
|
|
|
|
} |
254
|
369643
|
|
|
|
|
18162419
|
$stash->{pid}{$which} = $pid |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
3306
|
|
|
3306
|
|
1576492
|
my $have_usleep = eval "use Time::HiRes 'usleep'; 1"; |
|
3306
|
|
|
|
|
3481086
|
|
|
3306
|
|
|
|
|
10809
|
|
258
|
|
|
|
|
|
|
sub _files_exist { |
259
|
51944300
|
100
|
|
51944300
|
|
80110261
|
return 1 if @_ == grep { -f } @_; |
|
87524340
|
|
|
|
|
745188210
|
|
260
|
51506478
|
50
|
|
|
|
55961367993
|
Time::HiRes::usleep(1000) if $have_usleep; |
261
|
51506478
|
|
|
|
|
310854165
|
return 0; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub _wait_for_tees { |
265
|
218911
|
|
|
218911
|
|
671054
|
my ($stash) = @_; |
266
|
218911
|
|
|
|
|
392057
|
my $start = time; |
267
|
218911
|
|
|
|
|
343059
|
my @files = values %{$stash->{flag_files}}; |
|
218911
|
|
|
|
|
4450471
|
|
268
|
|
|
|
|
|
|
my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} |
269
|
218911
|
50
|
|
|
|
1549052
|
? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; |
270
|
218911
|
|
33
|
|
|
791448
|
1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); |
|
|
|
66
|
|
|
|
|
271
|
218911
|
50
|
|
|
|
625375
|
Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); |
272
|
218911
|
|
|
|
|
65726697
|
unlink $_ for @files; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub _kill_tees { |
276
|
218911
|
|
|
218911
|
|
419933
|
my ($stash) = @_; |
277
|
218911
|
50
|
|
|
|
671107
|
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
|
|
|
|
|
273717
|
_close $_ for values %{ $stash->{tee} }; |
|
218911
|
|
|
|
|
1256758
|
|
286
|
218911
|
|
|
|
|
393742
|
waitpid $_, 0 for values %{ $stash->{pid} }; |
|
218911
|
|
|
|
|
99294159
|
|
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub _slurp { |
291
|
1832073
|
|
|
1832073
|
|
1930768
|
my ($name, $stash) = @_; |
292
|
1832073
|
|
|
|
|
2306522
|
my ($fh, $pos) = map { $stash->{$_}{$name} } qw/capture pos/; |
|
3664146
|
|
|
|
|
6122535
|
|
293
|
|
|
|
|
|
|
# _debug( "# slurping captured $name from " . fileno($fh) . " at pos $pos with layers: @{[PerlIO::get_layers($fh)]}\n"); |
294
|
1832073
|
50
|
|
|
|
4808871
|
seek( $fh, $pos, 0 ) or die "Couldn't seek on capture handle for $name\n"; |
295
|
1832073
|
|
|
|
|
1365345
|
my $text = do { local $/; scalar readline $fh }; |
|
1832073
|
|
|
|
|
4739829
|
|
|
1832073
|
|
|
|
|
31778234
|
|
296
|
1832073
|
100
|
|
|
|
7784287
|
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
|
|
1948605
|
my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_; |
306
|
1044168
|
100
|
|
|
|
3955905
|
my %do = ($do_stdout ? (stdout => 1) : (), $do_stderr ? (stderr => 1) : ()); |
|
|
100
|
|
|
|
|
|
307
|
1044168
|
50
|
|
|
|
2783099
|
Carp::confess("Custom capture options must be given as key/value pairs\n") |
308
|
|
|
|
|
|
|
unless @opts % 2 == 0; |
309
|
1044168
|
|
|
|
|
2385793
|
my $stash = { capture => { @opts } }; |
310
|
1044168
|
|
|
|
|
1218182
|
for ( keys %{$stash->{capture}} ) { |
|
1044168
|
|
|
|
|
3168937
|
|
311
|
10
|
|
|
|
|
13
|
my $fh = $stash->{capture}{$_}; |
312
|
10
|
50
|
33
|
|
|
88
|
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
|
|
|
|
|
3330342
|
local *CT_ORIG_STDIN = *STDIN ; |
317
|
1044168
|
|
|
|
|
1637649
|
local *CT_ORIG_STDOUT = *STDOUT; |
318
|
1044168
|
|
|
|
|
1478220
|
local *CT_ORIG_STDERR = *STDERR; |
319
|
|
|
|
|
|
|
# find initial layers |
320
|
1044168
|
|
|
|
|
10153207
|
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
|
|
|
4376273
|
$layers{stdout} = [PerlIO::get_layers(tied *STDOUT)] |
329
|
|
|
|
|
|
|
if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB'); |
330
|
1044168
|
100
|
100
|
|
|
3166165
|
$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
|
|
|
|
|
968846
|
my %localize; |
336
|
|
|
|
|
|
|
$localize{stdin}++, local(*STDIN) |
337
|
1044168
|
100
|
|
|
|
894120
|
if grep { $_ eq 'scalar' } @{$layers{stdin}}; |
|
1927776
|
|
|
|
|
3796233
|
|
|
1044168
|
|
|
|
|
1610955
|
|
338
|
|
|
|
|
|
|
$localize{stdout}++, local(*STDOUT) |
339
|
1044168
|
100
|
100
|
|
|
2600282
|
if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}}; |
|
1968277
|
|
|
|
|
4342874
|
|
|
926297
|
|
|
|
|
1433041
|
|
340
|
|
|
|
|
|
|
$localize{stderr}++, local(*STDERR) |
341
|
1044168
|
100
|
66
|
|
|
3237552
|
if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}}; |
|
1947196
|
|
100
|
|
|
3807752
|
|
|
918092
|
|
|
|
|
1191914
|
|
342
|
1044168
|
100
|
66
|
|
|
3339629
|
$localize{stdin}++, local(*STDIN), _open( \*STDIN, "<&=0") |
343
|
|
|
|
|
|
|
if tied *STDIN && $] >= 5.008; |
344
|
1044168
|
100
|
100
|
|
|
4841001
|
$localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") |
|
|
|
66
|
|
|
|
|
345
|
|
|
|
|
|
|
if $do_stdout && tied *STDOUT && $] >= 5.008; |
346
|
1044168
|
100
|
66
|
|
|
5541793
|
$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
|
|
|
|
|
1900027
|
my %proxy_std = _proxy_std(); |
351
|
|
|
|
|
|
|
# _debug( "# proxy std: @{ [%proxy_std] }\n" ); |
352
|
|
|
|
|
|
|
# update layers after any proxying |
353
|
1044168
|
100
|
|
|
|
2395984
|
$layers{stdout} = [PerlIO::get_layers(\*STDOUT, output => 1)] if $proxy_std{stdout}; |
354
|
1044168
|
100
|
|
|
|
2063956
|
$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
|
|
|
|
|
1543506
|
$stash->{old} = _copy_std(); |
358
|
1044168
|
|
|
|
|
974269
|
$stash->{new} = { %{$stash->{old}} }; # default to originals |
|
1044168
|
|
|
|
|
3376920
|
|
359
|
1044168
|
|
|
|
|
2124106
|
for ( keys %do ) { |
360
|
1843158
|
|
66
|
|
|
16829998
|
$stash->{new}{$_} = ($stash->{capture}{$_} ||= File::Temp->new); |
361
|
1843158
|
50
|
|
|
|
691757688
|
seek( $stash->{capture}{$_}, 0, 2 ) or die "Could not seek on capture handle for $_\n"; |
362
|
1843158
|
|
|
|
|
4494259
|
$stash->{pos}{$_} = tell $stash->{capture}{$_}; |
363
|
|
|
|
|
|
|
# _debug("# will capture $_ on " . fileno($stash->{capture}{$_})."\n" ); |
364
|
1843158
|
100
|
|
|
|
5025993
|
_start_tee( $_ => $stash ) if $do_tee; # tees may change $stash->{new} |
365
|
|
|
|
|
|
|
} |
366
|
1040886
|
100
|
|
|
|
6744795
|
_wait_for_tees( $stash ) if $do_tee; |
367
|
|
|
|
|
|
|
# finalize redirection |
368
|
1040886
|
100
|
|
|
|
2088457
|
$stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge; |
369
|
|
|
|
|
|
|
# _debug( "# redirecting in parent ...\n" ); |
370
|
1040886
|
|
|
|
|
3205412
|
_open_std( $stash->{new} ); |
371
|
|
|
|
|
|
|
# execute user provided code |
372
|
1040886
|
|
|
|
|
1449757
|
my ($exit_code, $inner_error, $outer_error, $orig_pid, @result); |
373
|
|
|
|
|
|
|
{ |
374
|
1040886
|
|
|
|
|
1044104
|
$orig_pid = $$; |
|
1040886
|
|
|
|
|
2503593
|
|
375
|
1040886
|
100
|
|
|
|
3033462
|
local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN |
376
|
|
|
|
|
|
|
# _debug( "# finalizing layers ...\n" ); |
377
|
1040886
|
100
|
|
|
|
3645241
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
378
|
1040886
|
100
|
|
|
|
2964443
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
379
|
|
|
|
|
|
|
# _debug( "# running code $code ...\n" ); |
380
|
1040886
|
|
|
|
|
1512090
|
my $old_eval_err=$@; |
381
|
1040886
|
|
|
|
|
1304384
|
undef $@; |
382
|
1040886
|
|
|
|
|
1340854
|
eval { @result = $code->(); $inner_error = $@ }; |
|
1040886
|
|
|
|
|
3820393
|
|
|
1037602
|
|
|
|
|
1141617491
|
|
383
|
1037603
|
|
|
|
|
3083756
|
$exit_code = $?; # save this for later |
384
|
1037603
|
|
|
|
|
1357206
|
$outer_error = $@; # save this for later |
385
|
1037603
|
100
|
|
|
|
8300696
|
STDOUT->flush if $do_stdout; |
386
|
1037603
|
100
|
|
|
|
5087635
|
STDERR->flush if $do_stderr; |
387
|
1037603
|
|
|
|
|
1672406
|
$@ = $old_eval_err; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
# restore prior filehandles and shut down tees |
390
|
|
|
|
|
|
|
# _debug( "# restoring filehandles ...\n" ); |
391
|
1037603
|
|
|
|
|
3384538
|
_open_std( $stash->{old} ); |
392
|
1037603
|
|
|
|
|
1193224
|
_close( $_ ) for values %{$stash->{old}}; # don't leak fds |
|
1037603
|
|
|
|
|
5174920
|
|
393
|
|
|
|
|
|
|
# shouldn't need relayering originals, but see rt.perl.org #114404 |
394
|
1037603
|
100
|
|
|
|
3550997
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
395
|
1037603
|
100
|
|
|
|
2743170
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
396
|
1037603
|
|
|
|
|
3017172
|
_unproxy( %proxy_std ); |
397
|
|
|
|
|
|
|
# _debug( "# killing tee subprocesses ...\n" ) if $do_tee; |
398
|
1037603
|
100
|
|
|
|
2504440
|
_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
|
|
|
|
|
1200309
|
my %got; |
402
|
1037603
|
100
|
66
|
|
|
6498654
|
if ( $orig_pid == $$ and ( defined wantarray or ($do_tee && keys %localize) ) ) { |
|
|
|
33
|
|
|
|
|
403
|
1037600
|
|
|
|
|
2960698
|
for ( keys %do ) { |
404
|
1832073
|
|
|
|
|
3875666
|
_relayer($stash->{capture}{$_}, $layers{$_}); |
405
|
1832073
|
|
|
|
|
3143527
|
$got{$_} = _slurp($_, $stash); |
406
|
|
|
|
|
|
|
# _debug("# slurped " . length($got{$_}) . " bytes from $_\n"); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
print CT_ORIG_STDOUT $got{stdout} |
409
|
1037600
|
50
|
100
|
|
|
5525274
|
if $do_stdout && $do_tee && $localize{stdout}; |
|
|
|
66
|
|
|
|
|
410
|
|
|
|
|
|
|
print CT_ORIG_STDERR $got{stderr} |
411
|
1037600
|
50
|
100
|
|
|
4305332
|
if $do_stderr && $do_tee && $localize{stderr}; |
|
|
|
66
|
|
|
|
|
412
|
|
|
|
|
|
|
} |
413
|
1037603
|
|
|
|
|
1331544
|
$? = $exit_code; |
414
|
1037603
|
100
|
|
|
|
1494447
|
$@ = $inner_error if $inner_error; |
415
|
1037603
|
100
|
|
|
|
1435864
|
die $outer_error if $outer_error; |
416
|
|
|
|
|
|
|
# _debug( "# ending _capture_tee with (@_)...\n" ); |
417
|
1037602
|
100
|
|
|
|
1581393
|
return unless defined wantarray; |
418
|
1037599
|
|
|
|
|
1080355
|
my @return; |
419
|
1037599
|
100
|
|
|
|
2266099
|
push @return, $got{stdout} if $do_stdout; |
420
|
1037599
|
100
|
100
|
|
|
4008198
|
push @return, $got{stderr} if $do_stderr && ! $do_merge; |
421
|
1037599
|
|
|
|
|
1123032
|
push @return, @result; |
422
|
1037599
|
100
|
|
|
|
22053819
|
return wantarray ? @return : $return[0]; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
1; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
__END__ |