line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3306
|
|
|
3306
|
|
52302585
|
use 5.006; |
|
3306
|
|
|
|
|
8390
|
|
2
|
3306
|
|
|
3306
|
|
10488
|
use strict; |
|
3306
|
|
|
|
|
4189
|
|
|
3306
|
|
|
|
|
50722
|
|
3
|
3306
|
|
|
3306
|
|
9351
|
use warnings; |
|
3306
|
|
|
|
|
3629
|
|
|
3306
|
|
|
|
|
138965
|
|
4
|
|
|
|
|
|
|
package Capture::Tiny; |
5
|
|
|
|
|
|
|
# ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs |
6
|
|
|
|
|
|
|
our $VERSION = '0.46'; |
7
|
3306
|
|
|
3306
|
|
13456
|
use Carp (); |
|
3306
|
|
|
|
|
3629
|
|
|
3306
|
|
|
|
|
42558
|
|
8
|
3306
|
|
|
3306
|
|
8948
|
use Exporter (); |
|
3306
|
|
|
|
|
3547
|
|
|
3306
|
|
|
|
|
39575
|
|
9
|
3306
|
|
|
3306
|
|
1584186
|
use IO::Handle (); |
|
3306
|
|
|
|
|
15681165
|
|
|
3306
|
|
|
|
|
64089
|
|
10
|
3306
|
|
|
3306
|
|
16124
|
use File::Spec (); |
|
3306
|
|
|
|
|
3710
|
|
|
3306
|
|
|
|
|
54360
|
|
11
|
3306
|
|
|
3306
|
|
2285017
|
use File::Temp qw/tempfile tmpnam/; |
|
3306
|
|
|
|
|
38169527
|
|
|
3306
|
|
|
|
|
197877
|
|
12
|
3306
|
|
|
3306
|
|
18227
|
use Scalar::Util qw/reftype blessed/; |
|
3306
|
|
|
|
|
3950
|
|
|
3306
|
|
|
|
|
227754
|
|
13
|
|
|
|
|
|
|
# Get PerlIO or fake it |
14
|
|
|
|
|
|
|
BEGIN { |
15
|
3306
|
|
|
3306
|
|
23787
|
local $@; |
16
|
3306
|
|
|
|
|
11132
|
eval { require PerlIO; PerlIO->can('get_layers') } |
|
3306
|
|
|
|
|
742893
|
|
17
|
3306
|
50
|
|
|
|
4674
|
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
|
982598941
|
eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic |
|
563047
|
|
|
85211
|
1
|
2302032
|
|
|
85211
|
|
|
88506
|
1
|
100203690
|
|
|
85211
|
|
|
85211
|
1
|
298528
|
|
|
88506
|
|
|
139643
|
1
|
9376903
|
|
|
88506
|
|
|
12320
|
1
|
265170
|
|
|
85211
|
|
|
29365
|
1
|
2360931
|
|
|
85211
|
|
|
40865
|
1
|
266494
|
|
|
139643
|
|
|
|
|
4433549
|
|
|
139643
|
|
|
|
|
666510
|
|
|
12320
|
|
|
|
|
396493
|
|
|
12320
|
|
|
|
|
59596
|
|
|
29365
|
|
|
|
|
960721
|
|
|
29365
|
|
|
|
|
143825
|
|
|
40865
|
|
|
|
|
1312308
|
|
|
40865
|
|
|
|
|
205227
|
|
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
|
|
5614637
|
my ($fh, $apply_layers) = @_; |
85
|
|
|
|
|
|
|
# _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n"); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# eliminate pseudo-layers |
88
|
5502797
|
|
|
|
|
14571951
|
binmode( $fh, ":raw" ); |
89
|
|
|
|
|
|
|
# strip off real layers until only :unix is left |
90
|
5502797
|
|
|
|
|
23513721
|
while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { |
91
|
5502797
|
|
|
|
|
21976173
|
binmode( $fh, ":pop" ); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
# apply other layers |
94
|
5502797
|
|
|
|
|
10208827
|
my @to_apply = @$apply_layers; |
95
|
5502797
|
|
|
|
|
4368337
|
shift @to_apply; # eliminate initial :unix |
96
|
|
|
|
|
|
|
# _debug("# applying layers (unix @to_apply) to @{[fileno $fh]}\n"); |
97
|
5502797
|
|
|
|
|
19630612
|
binmode($fh, ":" . join(":",@to_apply)); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _name { |
101
|
0
|
|
|
0
|
|
0
|
my $glob = shift; |
102
|
3306
|
|
|
3306
|
|
12663
|
no strict 'refs'; ## no critic |
|
3306
|
|
|
|
|
3307
|
|
|
3306
|
|
|
|
|
6194338
|
|
103
|
0
|
|
|
|
|
0
|
return *{$glob}{NAME}; |
|
0
|
|
|
|
|
0
|
|
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub _open { |
107
|
7485730
|
50
|
|
7485730
|
|
169619571
|
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
|
|
22343250
|
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
|
|
842857
|
my %proxies; |
120
|
1044168
|
100
|
|
|
|
2001850
|
if ( ! defined fileno STDIN ) { |
121
|
138870
|
|
|
|
|
182982
|
$proxy_count{stdin}++; |
122
|
138870
|
100
|
|
|
|
277699
|
if (defined $dup{stdin}) { |
123
|
31830
|
|
|
|
|
99241
|
_open \*STDIN, "<&=" . fileno($dup{stdin}); |
124
|
|
|
|
|
|
|
# _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
else { |
127
|
107040
|
|
|
|
|
1197170
|
_open \*STDIN, "<" . File::Spec->devnull; |
128
|
|
|
|
|
|
|
# _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
129
|
107040
|
|
|
|
|
863988
|
_open $dup{stdin} = IO::Handle->new, "<&=STDIN"; |
130
|
|
|
|
|
|
|
} |
131
|
138870
|
|
|
|
|
233679
|
$proxies{stdin} = \*STDIN; |
132
|
138870
|
50
|
|
|
|
681882
|
binmode(STDIN, ':utf8') if $] >= 5.008; ## no critic |
133
|
|
|
|
|
|
|
} |
134
|
1044168
|
100
|
|
|
|
1743876
|
if ( ! defined fileno STDOUT ) { |
135
|
107040
|
|
|
|
|
158933
|
$proxy_count{stdout}++; |
136
|
107040
|
50
|
|
|
|
211849
|
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
|
|
|
|
|
1071274
|
_open \*STDOUT, ">" . File::Spec->devnull; |
142
|
|
|
|
|
|
|
# _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
143
|
107040
|
|
|
|
|
969639
|
_open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; |
144
|
|
|
|
|
|
|
} |
145
|
107040
|
|
|
|
|
189500
|
$proxies{stdout} = \*STDOUT; |
146
|
107040
|
50
|
|
|
|
547089
|
binmode(STDOUT, ':utf8') if $] >= 5.008; ## no critic |
147
|
|
|
|
|
|
|
} |
148
|
1044168
|
100
|
|
|
|
1626262
|
if ( ! defined fileno STDERR ) { |
149
|
107040
|
|
|
|
|
167652
|
$proxy_count{stderr}++; |
150
|
107040
|
50
|
|
|
|
200759
|
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
|
|
|
|
|
1087297
|
_open \*STDERR, ">" . File::Spec->devnull; |
156
|
|
|
|
|
|
|
# _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
157
|
107040
|
|
|
|
|
871249
|
_open $dup{stderr} = IO::Handle->new, ">&=STDERR"; |
158
|
|
|
|
|
|
|
} |
159
|
107040
|
|
|
|
|
187511
|
$proxies{stderr} = \*STDERR; |
160
|
107040
|
50
|
|
|
|
540136
|
binmode(STDERR, ':utf8') if $] >= 5.008; ## no critic |
161
|
|
|
|
|
|
|
} |
162
|
1044168
|
|
|
|
|
2376227
|
return %proxies; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _unproxy { |
166
|
1037603
|
|
|
1037603
|
|
2028320
|
my (%proxies) = @_; |
167
|
|
|
|
|
|
|
# _debug( "# unproxying: " . join(" ", keys %proxies) . "\n" ); |
168
|
1037603
|
|
|
|
|
2287434
|
for my $p ( keys %proxies ) { |
169
|
351270
|
|
|
|
|
564138
|
$proxy_count{$p}--; |
170
|
|
|
|
|
|
|
# _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); |
171
|
351270
|
100
|
|
|
|
792016
|
if ( ! $proxy_count{$p} ) { |
172
|
319680
|
|
|
|
|
511833
|
_close $proxies{$p}; |
173
|
319680
|
50
|
|
|
|
903379
|
_close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup |
174
|
319680
|
|
|
|
|
1699705
|
delete $dup{$p}; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _copy_std { |
180
|
1044168
|
|
|
1044168
|
|
797989
|
my %handles; |
181
|
1044168
|
|
|
|
|
1934239
|
for my $h ( qw/stdout stderr stdin/ ) { |
182
|
3132504
|
100
|
66
|
|
|
8639279
|
next if $h eq 'stdin' && ! $IS_WIN32; # WIN32 hangs on tee without STDIN copied |
183
|
2088336
|
50
|
|
|
|
3079354
|
my $redir = $h eq 'stdin' ? "<&" : ">&"; |
184
|
2088336
|
|
|
|
|
7431324
|
_open $handles{$h} = IO::Handle->new(), $redir . uc($h); # ">&STDOUT" or "<&STDIN" |
185
|
|
|
|
|
|
|
} |
186
|
1044168
|
|
|
|
|
2133870
|
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
|
|
2683935
|
my ($handles) = @_; |
193
|
2081771
|
100
|
|
|
|
4142305
|
_open \*STDIN, "<&" . fileno $handles->{stdin} if defined $handles->{stdin}; |
194
|
2081771
|
50
|
|
|
|
11768577
|
_open \*STDOUT, ">&" . fileno $handles->{stdout} if defined $handles->{stdout}; |
195
|
2081771
|
50
|
|
|
|
8164298
|
_open \*STDERR, ">&" . fileno $handles->{stderr} if defined $handles->{stderr}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
199
|
|
|
|
|
|
|
# private subs |
200
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _start_tee { |
203
|
372925
|
|
|
372925
|
|
649927
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
204
|
|
|
|
|
|
|
# setup pipes |
205
|
372925
|
|
|
|
|
3767976
|
$stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; |
206
|
372925
|
|
|
|
|
20592601
|
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
|
|
|
|
|
3222656
|
select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush |
209
|
|
|
|
|
|
|
# setup desired redirection for parent and child |
210
|
372925
|
|
|
|
|
733506
|
$stash->{new}{$which} = $stash->{tee}{$which}; |
211
|
|
|
|
|
|
|
$stash->{child}{$which} = { |
212
|
|
|
|
|
|
|
stdin => $stash->{reader}{$which}, |
213
|
|
|
|
|
|
|
stdout => $stash->{old}{$which}, |
214
|
372925
|
|
|
|
|
1486658
|
stderr => $stash->{capture}{$which}, |
215
|
|
|
|
|
|
|
}; |
216
|
|
|
|
|
|
|
# flag file is used to signal the child is ready |
217
|
372925
|
|
|
|
|
1646853
|
$stash->{flag_files}{$which} = scalar tmpnam(); |
218
|
|
|
|
|
|
|
# execute @cmd as a separate process |
219
|
372925
|
50
|
|
|
|
61732149
|
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
|
|
|
|
|
1184621
|
_fork_exec( $which, $stash ); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _fork_exec { |
240
|
372925
|
|
|
372925
|
|
457220
|
my ($which, $stash) = @_; # $which is "stdout" or "stderr" |
241
|
372925
|
|
|
|
|
177332156
|
my $pid = fork; |
242
|
372925
|
50
|
|
|
|
4112484
|
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
|
|
|
|
|
118081
|
untie *STDIN; untie *STDOUT; untie *STDERR; |
|
3282
|
|
|
|
|
26822
|
|
|
3282
|
|
|
|
|
24730
|
|
248
|
3282
|
|
|
|
|
106830
|
_close $stash->{tee}{$which}; |
249
|
|
|
|
|
|
|
# _debug( "# redirecting handles in child ...\n" ); |
250
|
3282
|
|
|
|
|
48654
|
_open_std( $stash->{child}{$which} ); |
251
|
|
|
|
|
|
|
# _debug( "# calling exec on command ...\n" ); |
252
|
3282
|
|
|
|
|
0
|
exec @cmd, $stash->{flag_files}{$which}; |
253
|
|
|
|
|
|
|
} |
254
|
369643
|
|
|
|
|
17944278
|
$stash->{pid}{$which} = $pid |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
3306
|
|
|
3306
|
|
1516989
|
my $have_usleep = eval "use Time::HiRes 'usleep'; 1"; |
|
3306
|
|
|
|
|
3432197
|
|
|
3306
|
|
|
|
|
10162
|
|
258
|
|
|
|
|
|
|
sub _files_exist { |
259
|
54372557
|
100
|
|
54372557
|
|
83788795
|
return 1 if @_ == grep { -f } @_; |
|
91591282
|
|
|
|
|
787026990
|
|
260
|
53934735
|
50
|
|
|
|
58560263933
|
Time::HiRes::usleep(1000) if $have_usleep; |
261
|
53934735
|
|
|
|
|
320691319
|
return 0; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub _wait_for_tees { |
265
|
218911
|
|
|
218911
|
|
739630
|
my ($stash) = @_; |
266
|
218911
|
|
|
|
|
389611
|
my $start = time; |
267
|
218911
|
|
|
|
|
359792
|
my @files = values %{$stash->{flag_files}}; |
|
218911
|
|
|
|
|
4413869
|
|
268
|
|
|
|
|
|
|
my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} |
269
|
218911
|
50
|
|
|
|
1530609
|
? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; |
270
|
218911
|
|
33
|
|
|
815175
|
1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); |
|
|
|
66
|
|
|
|
|
271
|
218911
|
50
|
|
|
|
623239
|
Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); |
272
|
218911
|
|
|
|
|
132291265
|
unlink $_ for @files; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub _kill_tees { |
276
|
218911
|
|
|
218911
|
|
401393
|
my ($stash) = @_; |
277
|
218911
|
50
|
|
|
|
689441
|
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
|
|
|
|
|
315344
|
_close $_ for values %{ $stash->{tee} }; |
|
218911
|
|
|
|
|
1225281
|
|
286
|
218911
|
|
|
|
|
385746
|
waitpid $_, 0 for values %{ $stash->{pid} }; |
|
218911
|
|
|
|
|
95861304
|
|
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub _slurp { |
291
|
1832073
|
|
|
1832073
|
|
1808948
|
my ($name, $stash) = @_; |
292
|
1832073
|
|
|
|
|
2235425
|
my ($fh, $pos) = map { $stash->{$_}{$name} } qw/capture pos/; |
|
3664146
|
|
|
|
|
5850817
|
|
293
|
|
|
|
|
|
|
# _debug( "# slurping captured $name from " . fileno($fh) . " at pos $pos with layers: @{[PerlIO::get_layers($fh)]}\n"); |
294
|
1832073
|
50
|
|
|
|
4679522
|
seek( $fh, $pos, 0 ) or die "Couldn't seek on capture handle for $name\n"; |
295
|
1832073
|
|
|
|
|
1306029
|
my $text = do { local $/; scalar readline $fh }; |
|
1832073
|
|
|
|
|
4725856
|
|
|
1832073
|
|
|
|
|
30524546
|
|
296
|
1832073
|
100
|
|
|
|
7690480
|
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
|
|
1948642
|
my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_; |
306
|
1044168
|
100
|
|
|
|
3878453
|
my %do = ($do_stdout ? (stdout => 1) : (), $do_stderr ? (stderr => 1) : ()); |
|
|
100
|
|
|
|
|
|
307
|
1044168
|
50
|
|
|
|
2730202
|
Carp::confess("Custom capture options must be given as key/value pairs\n") |
308
|
|
|
|
|
|
|
unless @opts % 2 == 0; |
309
|
1044168
|
|
|
|
|
2427014
|
my $stash = { capture => { @opts } }; |
310
|
1044168
|
|
|
|
|
1146925
|
for ( keys %{$stash->{capture}} ) { |
|
1044168
|
|
|
|
|
3131612
|
|
311
|
10
|
|
|
|
|
11
|
my $fh = $stash->{capture}{$_}; |
312
|
10
|
50
|
33
|
|
|
87
|
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
|
|
|
|
|
3283013
|
local *CT_ORIG_STDIN = *STDIN ; |
317
|
1044168
|
|
|
|
|
1613314
|
local *CT_ORIG_STDOUT = *STDOUT; |
318
|
1044168
|
|
|
|
|
1412682
|
local *CT_ORIG_STDERR = *STDERR; |
319
|
|
|
|
|
|
|
# find initial layers |
320
|
1044168
|
|
|
|
|
9955895
|
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
|
|
|
4250105
|
$layers{stdout} = [PerlIO::get_layers(tied *STDOUT)] |
329
|
|
|
|
|
|
|
if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB'); |
330
|
1044168
|
100
|
100
|
|
|
3182581
|
$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
|
|
|
|
|
938945
|
my %localize; |
336
|
|
|
|
|
|
|
$localize{stdin}++, local(*STDIN) |
337
|
1044168
|
100
|
|
|
|
820621
|
if grep { $_ eq 'scalar' } @{$layers{stdin}}; |
|
1927776
|
|
|
|
|
3787436
|
|
|
1044168
|
|
|
|
|
1569396
|
|
338
|
|
|
|
|
|
|
$localize{stdout}++, local(*STDOUT) |
339
|
1044168
|
100
|
100
|
|
|
2548396
|
if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}}; |
|
1967715
|
|
|
|
|
4166699
|
|
|
926297
|
|
|
|
|
1476954
|
|
340
|
|
|
|
|
|
|
$localize{stderr}++, local(*STDERR) |
341
|
1044168
|
100
|
66
|
|
|
3089438
|
if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}}; |
|
1947046
|
|
100
|
|
|
3790672
|
|
|
918092
|
|
|
|
|
1216804
|
|
342
|
1044168
|
100
|
66
|
|
|
3253597
|
$localize{stdin}++, local(*STDIN), _open( \*STDIN, "<&=0") |
343
|
|
|
|
|
|
|
if tied *STDIN && $] >= 5.008; |
344
|
1044168
|
100
|
100
|
|
|
4773722
|
$localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") |
|
|
|
66
|
|
|
|
|
345
|
|
|
|
|
|
|
if $do_stdout && tied *STDOUT && $] >= 5.008; |
346
|
1044168
|
100
|
66
|
|
|
5427038
|
$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
|
|
|
|
|
1855564
|
my %proxy_std = _proxy_std(); |
351
|
|
|
|
|
|
|
# _debug( "# proxy std: @{ [%proxy_std] }\n" ); |
352
|
|
|
|
|
|
|
# update layers after any proxying |
353
|
1044168
|
100
|
|
|
|
2346805
|
$layers{stdout} = [PerlIO::get_layers(\*STDOUT, output => 1)] if $proxy_std{stdout}; |
354
|
1044168
|
100
|
|
|
|
2099585
|
$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
|
|
|
|
|
1531753
|
$stash->{old} = _copy_std(); |
358
|
1044168
|
|
|
|
|
935331
|
$stash->{new} = { %{$stash->{old}} }; # default to originals |
|
1044168
|
|
|
|
|
3338995
|
|
359
|
1044168
|
|
|
|
|
2076644
|
for ( keys %do ) { |
360
|
1843158
|
|
66
|
|
|
16241185
|
$stash->{new}{$_} = ($stash->{capture}{$_} ||= File::Temp->new); |
361
|
1843158
|
50
|
|
|
|
626508832
|
seek( $stash->{capture}{$_}, 0, 2 ) or die "Could not seek on capture handle for $_\n"; |
362
|
1843158
|
|
|
|
|
4720152
|
$stash->{pos}{$_} = tell $stash->{capture}{$_}; |
363
|
|
|
|
|
|
|
# _debug("# will capture $_ on " . fileno($stash->{capture}{$_})."\n" ); |
364
|
1843158
|
100
|
|
|
|
5278663
|
_start_tee( $_ => $stash ) if $do_tee; # tees may change $stash->{new} |
365
|
|
|
|
|
|
|
} |
366
|
1040886
|
100
|
|
|
|
6695845
|
_wait_for_tees( $stash ) if $do_tee; |
367
|
|
|
|
|
|
|
# finalize redirection |
368
|
1040886
|
100
|
|
|
|
2101173
|
$stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge; |
369
|
|
|
|
|
|
|
# _debug( "# redirecting in parent ...\n" ); |
370
|
1040886
|
|
|
|
|
3137789
|
_open_std( $stash->{new} ); |
371
|
|
|
|
|
|
|
# execute user provided code |
372
|
1040886
|
|
|
|
|
1495565
|
my ($exit_code, $inner_error, $outer_error, $orig_pid, @result); |
373
|
|
|
|
|
|
|
{ |
374
|
1040886
|
|
|
|
|
1010758
|
$orig_pid = $$; |
|
1040886
|
|
|
|
|
2334620
|
|
375
|
1040886
|
100
|
|
|
|
2941588
|
local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN |
376
|
|
|
|
|
|
|
# _debug( "# finalizing layers ...\n" ); |
377
|
1040886
|
100
|
|
|
|
3644433
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
378
|
1040886
|
100
|
|
|
|
2793856
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
379
|
|
|
|
|
|
|
# _debug( "# running code $code ...\n" ); |
380
|
1040886
|
|
|
|
|
1456112
|
my $old_eval_err=$@; |
381
|
1040886
|
|
|
|
|
1258069
|
undef $@; |
382
|
1040886
|
|
|
|
|
1355844
|
eval { @result = $code->(); $inner_error = $@ }; |
|
1040886
|
|
|
|
|
3690926
|
|
|
1037602
|
|
|
|
|
1020341536
|
|
383
|
1037603
|
|
|
|
|
2937570
|
$exit_code = $?; # save this for later |
384
|
1037603
|
|
|
|
|
1390879
|
$outer_error = $@; # save this for later |
385
|
1037603
|
100
|
|
|
|
7764342
|
STDOUT->flush if $do_stdout; |
386
|
1037603
|
100
|
|
|
|
4835202
|
STDERR->flush if $do_stderr; |
387
|
1037603
|
|
|
|
|
1747373
|
$@ = $old_eval_err; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
# restore prior filehandles and shut down tees |
390
|
|
|
|
|
|
|
# _debug( "# restoring filehandles ...\n" ); |
391
|
1037603
|
|
|
|
|
3228424
|
_open_std( $stash->{old} ); |
392
|
1037603
|
|
|
|
|
1248403
|
_close( $_ ) for values %{$stash->{old}}; # don't leak fds |
|
1037603
|
|
|
|
|
4769666
|
|
393
|
|
|
|
|
|
|
# shouldn't need relayering originals, but see rt.perl.org #114404 |
394
|
1037603
|
100
|
|
|
|
3484743
|
_relayer(\*STDOUT, $layers{stdout}) if $do_stdout; |
395
|
1037603
|
100
|
|
|
|
2732597
|
_relayer(\*STDERR, $layers{stderr}) if $do_stderr; |
396
|
1037603
|
|
|
|
|
3032800
|
_unproxy( %proxy_std ); |
397
|
|
|
|
|
|
|
# _debug( "# killing tee subprocesses ...\n" ) if $do_tee; |
398
|
1037603
|
100
|
|
|
|
2515563
|
_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
|
|
|
|
|
1222831
|
my %got; |
402
|
1037603
|
100
|
66
|
|
|
6302042
|
if ( $orig_pid == $$ and ( defined wantarray or ($do_tee && keys %localize) ) ) { |
|
|
|
33
|
|
|
|
|
403
|
1037600
|
|
|
|
|
2913411
|
for ( keys %do ) { |
404
|
1832073
|
|
|
|
|
3728111
|
_relayer($stash->{capture}{$_}, $layers{$_}); |
405
|
1832073
|
|
|
|
|
3118676
|
$got{$_} = _slurp($_, $stash); |
406
|
|
|
|
|
|
|
# _debug("# slurped " . length($got{$_}) . " bytes from $_\n"); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
print CT_ORIG_STDOUT $got{stdout} |
409
|
1037600
|
50
|
100
|
|
|
5530351
|
if $do_stdout && $do_tee && $localize{stdout}; |
|
|
|
66
|
|
|
|
|
410
|
|
|
|
|
|
|
print CT_ORIG_STDERR $got{stderr} |
411
|
1037600
|
50
|
100
|
|
|
4246976
|
if $do_stderr && $do_tee && $localize{stderr}; |
|
|
|
66
|
|
|
|
|
412
|
|
|
|
|
|
|
} |
413
|
1037603
|
|
|
|
|
1300319
|
$? = $exit_code; |
414
|
1037603
|
100
|
|
|
|
1554677
|
$@ = $inner_error if $inner_error; |
415
|
1037603
|
100
|
|
|
|
1405236
|
die $outer_error if $outer_error; |
416
|
|
|
|
|
|
|
# _debug( "# ending _capture_tee with (@_)...\n" ); |
417
|
1037602
|
100
|
|
|
|
1593084
|
return unless defined wantarray; |
418
|
1037599
|
|
|
|
|
859342
|
my @return; |
419
|
1037599
|
100
|
|
|
|
2208979
|
push @return, $got{stdout} if $do_stdout; |
420
|
1037599
|
100
|
100
|
|
|
3836561
|
push @return, $got{stderr} if $do_stderr && ! $do_merge; |
421
|
1037599
|
|
|
|
|
1052393
|
push @return, @result; |
422
|
1037599
|
100
|
|
|
|
20775831
|
return wantarray ? @return : $return[0]; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
1; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
__END__ |