File Coverage

blib/lib/IPC/Run/Win32Helper.pm
Criterion Covered Total %
statement 27 137 19.7
branch 0 114 0.0
condition 0 66 0.0
subroutine 9 16 56.2
pod 3 3 100.0
total 39 336 11.6


line stmt bran cond sub pod time code
1             package IPC::Run::Win32Helper;
2              
3             =pod
4              
5             =head1 NAME
6              
7             IPC::Run::Win32Helper - helper routines for IPC::Run on Win32 platforms.
8              
9             =head1 SYNOPSIS
10              
11             use IPC::Run::Win32Helper; # Exports all by default
12              
13             =head1 DESCRIPTION
14              
15             IPC::Run needs to use sockets to redirect subprocess I/O so that the select() loop
16             will work on Win32. This seems to only work on WinNT and Win2K at this time, not
17             sure if it will ever work on Win95 or Win98. If you have experience in this area, please
18             contact me at barries@slaysys.com, thanks!.
19              
20             =cut
21              
22 1     1   71116 use strict;
  1         10  
  1         49  
23 1     1   7 use Carp;
  1         2  
  1         85  
24 1     1   608 use IO::Handle;
  1         6497  
  1         47  
25 1     1   7 use vars qw{ $VERSION @ISA @EXPORT };
  1         2  
  1         73  
26              
27             BEGIN {
28 1     1   3 $VERSION = '20200505.0';
29 1         16 @ISA = qw( Exporter );
30 1         33 @EXPORT = qw(
31             win32_spawn
32             win32_parse_cmd_line
33             _dont_inherit
34             _inherit
35             );
36             }
37              
38             require POSIX;
39              
40 1     1   532 use Text::ParseWords;
  1         1400  
  1         63  
41 1     1   8 use Win32::Process;
  1         2  
  1         41  
42 1     1   450 use IPC::Run::Debug;
  1         4  
  1         88  
43 1         1851 use Win32API::File qw(
44             FdGetOsFHandle
45             SetHandleInformation
46             HANDLE_FLAG_INHERIT
47             INVALID_HANDLE_VALUE
48 1     1   7 );
  1         2  
49              
50             ## Takes an fd or a GLOB ref, never never never a Win32 handle.
51             sub _dont_inherit {
52 0     0     for (@_) {
53 0 0         next unless defined $_;
54 0           my $fd = $_;
55 0 0         $fd = fileno $fd if ref $fd;
56 0 0         _debug "disabling inheritance of ", $fd if _debugging_details;
57 0           my $osfh = FdGetOsFHandle $fd;
58 0 0 0       croak $^E if !defined $osfh || $osfh == INVALID_HANDLE_VALUE;
59              
60 0           SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 0 );
61             }
62             }
63              
64             sub _inherit { #### REMOVE
65 0     0     for (@_) { #### REMOVE
66 0 0         next unless defined $_; #### REMOVE
67 0           my $fd = $_; #### REMOVE
68 0 0         $fd = fileno $fd if ref $fd; #### REMOVE
69 0 0         _debug "enabling inheritance of ", $fd if _debugging_details; #### REMOVE
70 0           my $osfh = FdGetOsFHandle $fd; #### REMOVE
71 0 0 0       croak $^E if !defined $osfh || $osfh == INVALID_HANDLE_VALUE; #### REMOVE
72             #### REMOVE
73 0           SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 1 ); #### REMOVE
74             } #### REMOVE
75             } #### REMOVE
76             #### REMOVE
77             #sub _inherit {
78             # for ( @_ ) {
79             # next unless defined $_;
80             # my $osfh = GetOsFHandle $_;
81             # croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE;
82             # SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
83             # }
84             #}
85              
86             =pod
87              
88             =head1 FUNCTIONS
89              
90             =over
91              
92             =item optimize()
93              
94             Most common incantations of C (I C, C,
95             or C) now use temporary files to redirect input and output
96             instead of pumper processes.
97              
98             Temporary files are used when sending to child processes if input is
99             taken from a scalar with no filter subroutines. This is the only time
100             we can assume that the parent is not interacting with the child's
101             redirected input as it runs.
102              
103             Temporary files are used when receiving from children when output is
104             to a scalar or subroutine with or without filters, but only if
105             the child in question closes its inputs or takes input from
106             unfiltered SCALARs or named files. Normally, a child inherits its STDIN
107             from its parent; to close it, use "0<&-" or the C<< noinherit => 1 >> option.
108             If data is sent to the child from CODE refs, filehandles or from
109             scalars through filters than the child's outputs will not be optimized
110             because C assumes the parent is interacting with the child.
111             It is ok if the output is filtered or handled by a subroutine, however.
112              
113             This assumes that all named files are real files (as opposed to named
114             pipes) and won't change; and that a process is not communicating with
115             the child indirectly (through means not visible to IPC::Run).
116             These can be an invalid assumptions, but are the 99% case.
117             Write me if you need an option to enable or disable optimizations; I
118             suspect it will work like the C modifier.
119              
120             To detect cases that you might want to optimize by closing inputs, try
121             setting the C environment variable to the special C
122             value:
123              
124             C:> set IPCRUNDEBUG=notopt
125             C:> my_app_that_uses_IPC_Run.pl
126              
127             =item optimizer() rationalizations
128              
129             Only for that limited case can we be sure that it's ok to batch all the
130             input in to a temporary file. If STDIN is from a SCALAR or from a named
131             file or filehandle (again, only in C), then outputs to CODE refs
132             are also assumed to be safe enough to batch through a temp file,
133             otherwise only outputs to SCALAR refs are batched. This can cause a bit
134             of grief if the parent process benefits from or relies on a bit of
135             "early returns" coming in before the child program exits. As long as
136             the output is redirected to a SCALAR ref, this will not be visible.
137             When output is redirected to a subroutine or (deprecated) filters, the
138             subroutine will not get any data until after the child process exits,
139             and it is likely to get bigger chunks of data at once.
140              
141             The reason for the optimization is that, without it, "pumper" processes
142             are used to overcome the inconsistencies of the Win32 API. We need to
143             use anonymous pipes to connect to the child processes' stdin, stdout,
144             and stderr, yet select() does not work on these. select() only works on
145             sockets on Win32. So for each redirected child handle, there is
146             normally a "pumper" process that connects to the parent using a
147             socket--so the parent can select() on that fd--and to the child on an
148             anonymous pipe--so the child can read/write a pipe.
149              
150             Using a socket to connect directly to the child (as at least one MSDN
151             article suggests) seems to cause the trailing output from most children
152             to be lost. I think this is because child processes rarely close their
153             stdout and stderr explicitly, and the winsock dll does not seem to flush
154             output when a process that uses it exits without explicitly closing
155             them.
156              
157             Because of these pumpers and the inherent slowness of Win32
158             CreateProcess(), child processes with redirects are quite slow to
159             launch; so this routine looks for the very common case of
160             reading/writing to/from scalar references in a run() routine and
161             converts such reads and writes in to temporary file reads and writes.
162              
163             Such files are marked as FILE_ATTRIBUTE_TEMPORARY to increase speed and
164             as FILE_FLAG_DELETE_ON_CLOSE so it will be cleaned up when the child
165             process exits (for input files). The user's default permissions are
166             used for both the temporary files and the directory that contains them,
167             hope your Win32 permissions are secure enough for you. Files are
168             created with the Win32API::File defaults of
169             FILE_SHARE_READ|FILE_SHARE_WRITE.
170              
171             Setting the debug level to "details" or "gory" will give detailed
172             information about the optimization process; setting it to "basic" or
173             higher will tell whether or not a given call is optimized. Setting
174             it to "notopt" will highlight those calls that aren't optimized.
175              
176             =cut
177              
178             sub optimize {
179 0     0 1   my ($h) = @_;
180              
181 0           my @kids = @{ $h->{KIDS} };
  0            
182              
183 0           my $saw_pipe;
184              
185 0           my ( $ok_to_optimize_outputs, $veto_output_optimization );
186              
187 0           for my $kid (@kids) {
188 0 0         ( $ok_to_optimize_outputs, $veto_output_optimization ) = ()
189             unless $saw_pipe;
190              
191 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over ok of non-SCALAR output optimization"
192             if _debugging_details && $ok_to_optimize_outputs;
193 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over veto of non-SCALAR output optimization"
194             if _debugging_details && $veto_output_optimization;
195              
196 0 0 0       if ( $h->{noinherit} && !$ok_to_optimize_outputs ) {
197 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) STDIN not inherited from parent oking non-SCALAR output optimization"
198             if _debugging_details && $ok_to_optimize_outputs;
199 0           $ok_to_optimize_outputs = 1;
200             }
201              
202 0           for ( @{ $kid->{OPS} } ) {
  0            
203 0 0 0       if ( substr( $_->{TYPE}, 0, 1 ) eq "<" ) {
    0 0        
    0          
    0          
204 0 0         if ( $_->{TYPE} eq "<" ) {
205 0 0 0       if ( @{ $_->{FILTERS} } > 1 ) {
  0 0 0        
    0 0        
206             ## Can't assume that the filters are idempotent.
207             }
208             elsif (ref $_->{SOURCE} eq "SCALAR"
209             || ref $_->{SOURCE} eq "GLOB"
210             || UNIVERSAL::isa( $_, "IO::Handle" ) ) {
211 0 0         if ( $_->{KFD} == 0 ) {
212             _debug
213             "Win32 optimizer: (kid $kid->{NUM}) 0$_->{TYPE}",
214             ref $_->{SOURCE},
215 0 0         ", ok to optimize outputs"
216             if _debugging_details;
217 0           $ok_to_optimize_outputs = 1;
218             }
219 0           $_->{SEND_THROUGH_TEMP_FILE} = 1;
220 0           next;
221             }
222             elsif ( !ref $_->{SOURCE} && defined $_->{SOURCE} ) {
223 0 0         if ( $_->{KFD} == 0 ) {
224 0 0         _debug
225             "Win32 optimizer: (kid $kid->{NUM}) 0<$_->{SOURCE}, ok to optimize outputs",
226             if _debugging_details;
227 0           $ok_to_optimize_outputs = 1;
228             }
229 0           next;
230             }
231             }
232             _debug
233             "Win32 optimizer: (kid $kid->{NUM}) ",
234             $_->{KFD},
235             $_->{TYPE},
236             defined $_->{SOURCE}
237             ? ref $_->{SOURCE}
238             ? ref $_->{SOURCE}
239             : $_->{SOURCE}
240             : defined $_->{FILENAME} ? $_->{FILENAME}
241             : "",
242 0 0 0       @{ $_->{FILTERS} } > 1 ? " with filters" : (),
  0 0          
    0          
    0          
    0          
243             ", VETOING output opt."
244             if _debugging_details || _debugging_not_optimized;
245 0           $veto_output_optimization = 1;
246             }
247             elsif ( $_->{TYPE} eq "close" && $_->{KFD} == 0 ) {
248 0           $ok_to_optimize_outputs = 1;
249 0 0         _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&-, ok to optimize outputs"
250             if _debugging_details;
251             }
252             elsif ( $_->{TYPE} eq "dup" && $_->{KFD2} == 0 ) {
253 0           $veto_output_optimization = 1;
254 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&$_->{KFD2}, VETOING output opt."
255             if _debugging_details || _debugging_not_optimized;
256             }
257             elsif ( $_->{TYPE} eq "|" ) {
258 0           $saw_pipe = 1;
259             }
260             }
261              
262 0 0 0       if ( !$ok_to_optimize_outputs && !$veto_output_optimization ) {
263 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) child STDIN not redirected, VETOING non-SCALAR output opt."
264             if _debugging_details || _debugging_not_optimized;
265 0           $veto_output_optimization = 1;
266             }
267              
268 0 0 0       if ( $ok_to_optimize_outputs && $veto_output_optimization ) {
269 0           $ok_to_optimize_outputs = 0;
270 0 0 0       _debug "Win32 optimizer: (kid $kid->{NUM}) non-SCALAR output optimizations VETOed"
271             if _debugging_details || _debugging_not_optimized;
272             }
273              
274             ## SOURCE/DEST ARRAY means it's a filter.
275             ## TODO: think about checking to see if the final input/output of
276             ## a filter chain (an ARRAY SOURCE or DEST) is a scalar...but
277             ## we may be deprecating filters.
278              
279 0           for ( @{ $kid->{OPS} } ) {
  0            
280 0 0         if ( $_->{TYPE} eq ">" ) {
281 0 0 0       if (
      0        
      0        
      0        
282             ref $_->{DEST} eq "SCALAR"
283             || (
284             (
285             @{ $_->{FILTERS} } > 1
286             || ref $_->{DEST} eq "CODE"
287             || ref $_->{DEST} eq "ARRAY" ## Filters?
288             )
289             && ( $ok_to_optimize_outputs && !$veto_output_optimization )
290             )
291             ) {
292 0           $_->{RECV_THROUGH_TEMP_FILE} = 1;
293 0           next;
294             }
295             _debug
296             "Win32 optimizer: NOT optimizing (kid $kid->{NUM}) ",
297             $_->{KFD},
298             $_->{TYPE},
299             defined $_->{DEST}
300             ? ref $_->{DEST}
301             ? ref $_->{DEST}
302             : $_->{SOURCE}
303             : defined $_->{FILENAME} ? $_->{FILENAME}
304             : "",
305 0 0         @{ $_->{FILTERS} } ? " with filters" : (),
  0 0          
    0          
    0          
    0          
306             if _debugging_details;
307             }
308             }
309             }
310              
311             }
312              
313             =pod
314              
315             =item win32_parse_cmd_line
316              
317             @words = win32_parse_cmd_line( q{foo bar 'baz baz' "bat bat"} );
318              
319             returns 4 words. This parses like the bourne shell (see
320             the bit about shellwords() in L), assuming we're
321             trying to be a little cross-platform here. The only difference is
322             that "\" is *not* treated as an escape except when it precedes
323             punctuation, since it's used all over the place in DOS path specs.
324              
325             TODO: globbing? probably not (it's unDOSish).
326              
327             TODO: shebang emulation? Probably, but perhaps that should be part
328             of Run.pm so all spawned processes get the benefit.
329              
330             LIMITATIONS: shellwords dies silently on malformed input like
331              
332             a\"
333              
334             =cut
335              
336             sub win32_parse_cmd_line {
337 0     0 1   my $line = shift;
338 0           $line =~ s{(\\[\w\s])}{\\$1}g;
339 0           return shellwords $line;
340             }
341              
342             =pod
343              
344             =item win32_spawn
345              
346             Spawns a child process, possibly with STDIN, STDOUT, and STDERR (file descriptors 0, 1, and 2, respectively) redirected.
347              
348             B.
349              
350             Cannot redirect higher file descriptors due to lack of support for this in the
351             Win32 environment.
352              
353             This can be worked around by marking a handle as inheritable in the
354             parent (or leaving it marked; this is the default in perl), obtaining it's
355             Win32 handle with C or
356             C and passing it to the child using the command
357             line, the environment, or any other IPC mechanism (it's a plain old integer).
358             The child can then use C or C and possibly
359             C<&BAR">> or C<&$fd>> as need be. Ach, the pain!
360              
361             Remember to check the Win32 handle against INVALID_HANDLE_VALUE.
362              
363             =cut
364              
365             sub _save {
366 0     0     my ( $saved, $saved_as, $fd ) = @_;
367              
368             ## We can only save aside the original fds once.
369 0 0         return if exists $saved->{$fd};
370              
371 0           my $saved_fd = IPC::Run::_dup($fd);
372 0           _dont_inherit $saved_fd;
373              
374 0           $saved->{$fd} = $saved_fd;
375 0           $saved_as->{$saved_fd} = $fd;
376              
377 0           _dont_inherit $saved->{$fd};
378             }
379              
380             sub _dup2_gently {
381 0     0     my ( $saved, $saved_as, $fd1, $fd2 ) = @_;
382 0           _save $saved, $saved_as, $fd2;
383              
384 0 0         if ( exists $saved_as->{$fd2} ) {
385             ## The target fd is colliding with a saved-as fd, gotta bump
386             ## the saved-as fd to another fd.
387 0           my $orig_fd = delete $saved_as->{$fd2};
388 0           my $saved_fd = IPC::Run::_dup($fd2);
389 0           _dont_inherit $saved_fd;
390              
391 0           $saved->{$orig_fd} = $saved_fd;
392 0           $saved_as->{$saved_fd} = $orig_fd;
393             }
394 0 0         _debug "moving $fd1 to kid's $fd2" if _debugging_details;
395 0           IPC::Run::_dup2_rudely( $fd1, $fd2 );
396             }
397              
398             sub win32_spawn {
399 0     0 1   my ( $cmd, $ops ) = @_;
400              
401             ## NOTE: The debug pipe write handle is passed to pump processes as STDOUT.
402             ## and is not to the "real" child process, since they would not know
403             ## what to do with it...unlike Unix, we have no code executing in the
404             ## child before the "real" child is exec()ed.
405              
406 0           my %saved; ## Map of parent's orig fd -> saved fd
407             my %saved_as; ## Map of parent's saved fd -> orig fd, used to
408             ## detect collisions between a KFD and the fd a
409             ## parent's fd happened to be saved to.
410              
411 0           for my $op (@$ops) {
412 0 0         _dont_inherit $op->{FD} if defined $op->{FD};
413              
414 0 0 0       if ( defined $op->{KFD} && $op->{KFD} > 2 ) {
415             ## TODO: Detect this in harness()
416             ## TODO: enable temporary redirections if ever necessary, not
417             ## sure why they would be...
418             ## 4>&1 1>/dev/null 1>&4 4>&-
419 0           croak "Can't redirect fd #", $op->{KFD}, " on Win32";
420             }
421              
422             ## This is very similar logic to IPC::Run::_do_kid_and_exit().
423 0 0         if ( defined $op->{TFD} ) {
    0          
    0          
    0          
424 0 0         unless ( $op->{TFD} == $op->{KFD} ) {
425 0           _dup2_gently \%saved, \%saved_as, $op->{TFD}, $op->{KFD};
426 0           _dont_inherit $op->{TFD};
427             }
428             }
429             elsif ( $op->{TYPE} eq "dup" ) {
430             _dup2_gently \%saved, \%saved_as, $op->{KFD1}, $op->{KFD2}
431 0 0         unless $op->{KFD1} == $op->{KFD2};
432             }
433             elsif ( $op->{TYPE} eq "close" ) {
434 0           _save \%saved, \%saved_as, $op->{KFD};
435 0           IPC::Run::_close( $op->{KFD} );
436             }
437             elsif ( $op->{TYPE} eq "init" ) {
438             ## TODO: detect this in harness()
439 0           croak "init subs not allowed on Win32";
440             }
441             }
442              
443 0           my $process;
444             my $cmd_line = join " ", map {
445 0           ( my $s = $_ ) =~ s/"/"""/g;
  0            
446 0 0         $s = qq{"$s"} if /[\"\s]|^$/;
447 0           $s;
448             } @$cmd;
449              
450 0 0         _debug "cmd line: ", $cmd_line
451             if _debugging;
452              
453 0 0         Win32::Process::Create(
454             $process,
455             $cmd->[0],
456             $cmd_line,
457             1, ## Inherit handles
458             0, ## Inherit parent priortiy class. Was NORMAL_PRIORITY_CLASS
459             ".",
460             ) or croak "$!: Win32::Process::Create()";
461              
462 0           for my $orig_fd ( keys %saved ) {
463 0           IPC::Run::_dup2_rudely( $saved{$orig_fd}, $orig_fd );
464 0           IPC::Run::_close( $saved{$orig_fd} );
465             }
466              
467 0           return ( $process->GetProcessID(), $process );
468             }
469              
470             1;
471              
472             =pod
473              
474             =back
475              
476             =head1 AUTHOR
477              
478             Barries Slaymaker . Funded by Perforce Software, Inc.
479              
480             =head1 COPYRIGHT
481              
482             Copyright 2001, Barrie Slaymaker, All Rights Reserved.
483              
484             You may use this under the terms of either the GPL 2.0 or the Artistic License.
485              
486             =cut