File Coverage

blib/lib/IPC/Run/Win32IO.pm
Criterion Covered Total %
statement 48 181 26.5
branch 0 116 0.0
condition 0 12 0.0
subroutine 18 30 60.0
pod 1 1 100.0
total 67 340 19.7


line stmt bran cond sub pod time code
1             package IPC::Run::Win32IO;
2              
3             =pod
4              
5             =head1 NAME
6              
7             IPC::Run::Win32IO - helper routines for IPC::Run on Win32 platforms.
8              
9             =head1 SYNOPSIS
10              
11             use IPC::Run::Win32IO; # 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()
16             loop will work on Win32. This seems to only work on WinNT and Win2K at this
17             time, not sure if it will ever work on Win95 or Win98. If you have experience
18             in this area, please contact me at barries@slaysys.com, thanks!.
19              
20             =head1 DESCRIPTION
21              
22             A specialized IO class used on Win32.
23              
24             =cut
25              
26 1     1   600 use strict;
  1         2  
  1         33  
27 1     1   5 use Carp;
  1         3  
  1         51  
28 1     1   6 use IO::Handle;
  1         2  
  1         58  
29 1     1   596 use Socket;
  1         3843  
  1         473  
30             require POSIX;
31              
32 1     1   8 use vars qw{$VERSION};
  1         2  
  1         50  
33              
34             BEGIN {
35 1     1   20 $VERSION = '20200505.0';
36             }
37              
38 1     1   5 use Socket qw( IPPROTO_TCP TCP_NODELAY );
  1         2  
  1         141  
39 1     1   7 use Symbol;
  1         2  
  1         50  
40 1     1   6 use Text::ParseWords;
  1         2  
  1         69  
41 1     1   11 use Win32::Process;
  1         2  
  1         46  
42 1     1   6 use IPC::Run::Debug qw( :default _debugging_level );
  1         2  
  1         197  
43 1     1   6 use IPC::Run::Win32Helper qw( _inherit _dont_inherit );
  1         2  
  1         53  
44 1     1   5 use Fcntl qw( O_TEXT O_RDONLY );
  1         2  
  1         53  
45              
46 1     1   13 use base qw( IPC::Run::IO );
  1         2  
  1         505  
47             my @cleanup_fields;
48              
49             BEGIN {
50             ## These fields will be set to undef in _cleanup to close
51             ## the handles.
52 1     1   38 @cleanup_fields = (
53             'SEND_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
54             'RECV_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
55             'TEMP_FILE_NAME', ## The name of the temp file, needed for
56             ## error reporting / debugging only.
57              
58             'PARENT_HANDLE', ## The handle of the socket for the parent
59             'PUMP_SOCKET_HANDLE', ## The socket handle for the pump
60             'PUMP_PIPE_HANDLE', ## The anon pipe handle for the pump
61             'CHILD_HANDLE', ## The anon pipe handle for the child
62              
63             'TEMP_FILE_HANDLE', ## The Win32 filehandle for the temp file
64             );
65             }
66              
67             ## REMOVE OSFHandleOpen
68 1         139 use Win32API::File qw(
69             GetOsFHandle
70             OsFHandleOpenFd
71             OsFHandleOpen
72             FdGetOsFHandle
73             SetHandleInformation
74             SetFilePointer
75             HANDLE_FLAG_INHERIT
76             INVALID_HANDLE_VALUE
77              
78             createFile
79             WriteFile
80             ReadFile
81             CloseHandle
82              
83             FILE_ATTRIBUTE_TEMPORARY
84             FILE_FLAG_DELETE_ON_CLOSE
85             FILE_FLAG_WRITE_THROUGH
86              
87             FILE_BEGIN
88 1     1   7 );
  1         3  
89              
90             # FILE_ATTRIBUTE_HIDDEN
91             # FILE_ATTRIBUTE_SYSTEM
92              
93             BEGIN {
94             ## Force AUTOLOADED constants to be, well, constant by getting them
95             ## to AUTOLOAD before compilation continues. Sigh.
96 1     1   28 () = (
97             SOL_SOCKET,
98             SO_REUSEADDR,
99             IPPROTO_TCP,
100             TCP_NODELAY,
101             HANDLE_FLAG_INHERIT,
102             INVALID_HANDLE_VALUE,
103             );
104             }
105              
106 1     1   55 use constant temp_file_flags => ( FILE_ATTRIBUTE_TEMPORARY() | FILE_FLAG_DELETE_ON_CLOSE() | FILE_FLAG_WRITE_THROUGH() );
  1         2  
  1         15  
107              
108             # FILE_ATTRIBUTE_HIDDEN() |
109             # FILE_ATTRIBUTE_SYSTEM() |
110             my $tmp_file_counter;
111             my $tmp_dir;
112              
113             sub _cleanup {
114 0     0     my IPC::Run::Win32IO $self = shift;
115 0           my ($harness) = @_;
116              
117             $self->_recv_through_temp_file($harness)
118 0 0         if $self->{RECV_THROUGH_TEMP_FILE};
119              
120             CloseHandle( $self->{TEMP_FILE_HANDLE} )
121 0 0         if defined $self->{TEMP_FILE_HANDLE};
122              
123             close( $self->{CHILD_HANDLE} )
124 0 0         if defined $self->{CHILD_HANDLE};
125              
126 0           $self->{$_} = undef for @cleanup_fields;
127             }
128              
129             sub _create_temp_file {
130 0     0     my IPC::Run::Win32IO $self = shift;
131              
132             ## Create a hidden temp file that Win32 will delete when we close
133             ## it.
134 0 0         unless ( defined $tmp_dir ) {
135 0           $tmp_dir = File::Spec->catdir( File::Spec->tmpdir, "IPC-Run.tmp" );
136              
137             ## Trust in the user's umask.
138             ## This could possibly be a security hole, perhaps
139             ## we should offer an option. Hmmmm, really, people coding
140             ## security conscious apps should audit this code and
141             ## tell me how to make it better. Nice cop-out :).
142 0 0         unless ( -d $tmp_dir ) {
143 0 0         mkdir $tmp_dir or croak "$!: $tmp_dir";
144             }
145             }
146              
147 0           $self->{TEMP_FILE_NAME} = File::Spec->catfile(
148             ## File name is designed for easy sorting and not conflicting
149             ## with other processes. This should allow us to use "t"runcate
150             ## access in CreateFile in case something left some droppings
151             ## around (which should never happen because we specify
152             ## FLAG_DELETE_ON_CLOSE.
153             ## heh, belt and suspenders are better than bug reports; God forbid
154             ## that NT should ever crash before a temp file gets deleted!
155             $tmp_dir, sprintf "Win32io-%06d-%08d", $$, $tmp_file_counter++
156             );
157              
158             $self->{TEMP_FILE_HANDLE} = createFile(
159             $self->{TEMP_FILE_NAME},
160 0 0         "trw", ## new, truncate, read, write
161             {
162             Flags => temp_file_flags,
163             },
164             ) or croak "Can't create temporary file, $self->{TEMP_FILE_NAME}: $^E";
165              
166 0           $self->{TFD} = OsFHandleOpenFd $self->{TEMP_FILE_HANDLE}, 0;
167 0           $self->{FD} = undef;
168              
169             _debug
170             "Win32 Optimizer: temp file (",
171             $self->{KFD},
172             $self->{TYPE},
173             $self->{TFD},
174             ", fh ",
175             $self->{TEMP_FILE_HANDLE},
176             "): ",
177             $self->{TEMP_FILE_NAME}
178 0 0         if _debugging_details;
179             }
180              
181             sub _reset_temp_file_pointer {
182 0     0     my $self = shift;
183 0 0         SetFilePointer( $self->{TEMP_FILE_HANDLE}, 0, 0, FILE_BEGIN )
184             or confess "$^E seeking on (fd $self->{TFD}) $self->{TEMP_FILE_NAME} for kid's fd $self->{KFD}";
185             }
186              
187             sub _send_through_temp_file {
188 0     0     my IPC::Run::Win32IO $self = shift;
189              
190             _debug "Win32 optimizer: optimizing " . " $self->{KFD} $self->{TYPE} temp file instead of ",
191             ref $self->{SOURCE} || $self->{SOURCE}
192 0 0 0       if _debugging_details;
193              
194 0           $self->_create_temp_file;
195              
196 0 0         if ( defined ${ $self->{SOURCE} } ) {
  0            
197 0           my $bytes_written = 0;
198 0           my $data_ref;
199 0 0         if ( $self->binmode ) {
200 0           $data_ref = $self->{SOURCE};
201             }
202             else {
203 0           my $data = ${ $self->{SOURCE} }; # Ugh, a copy.
  0            
204 0           $data =~ s/(?
205 0           $data_ref = \$data;
206             }
207              
208             WriteFile(
209             $self->{TEMP_FILE_HANDLE},
210 0 0         $$data_ref,
211             0, ## Write entire buffer
212             $bytes_written,
213             [], ## Not overlapped.
214             ) or croak "$^E writing $self->{TEMP_FILE_NAME} for kid to read on fd $self->{KFD}";
215 0 0         _debug "Win32 optimizer: wrote $bytes_written to temp file $self->{TEMP_FILE_NAME}"
216             if _debugging_data;
217              
218 0           $self->_reset_temp_file_pointer;
219              
220             }
221              
222 0 0         _debug "Win32 optimizer: kid to read $self->{KFD} from temp file on $self->{TFD}"
223             if _debugging_details;
224             }
225              
226             sub _init_recv_through_temp_file {
227 0     0     my IPC::Run::Win32IO $self = shift;
228              
229 0           $self->_create_temp_file;
230             }
231              
232             ## TODO: Use the Win32 API in the select loop to see if the file has grown
233             ## and read it incrementally if it has.
234             sub _recv_through_temp_file {
235 0     0     my IPC::Run::Win32IO $self = shift;
236              
237             ## This next line kicks in if the run() never got to initting things
238             ## and needs to clean up.
239 0 0         return undef unless defined $self->{TEMP_FILE_HANDLE};
240              
241 0           push @{ $self->{FILTERS} }, sub {
242 0     0     my ( undef, $out_ref ) = @_;
243              
244 0 0         return undef unless defined $self->{TEMP_FILE_HANDLE};
245              
246 0           my $r;
247             my $s;
248             ReadFile(
249             $self->{TEMP_FILE_HANDLE},
250 0 0         $s,
251             999_999, ## Hmmm, should read the size.
252             $r,
253             []
254             ) or croak "$^E reading from $self->{TEMP_FILE_NAME}";
255              
256 0 0         _debug "ReadFile( $self->{TFD} ) = $r chars '$s'" if _debugging_data;
257              
258 0 0         return undef unless $r;
259              
260 0 0         $s =~ s/\r\n/\n/g unless $self->binmode;
261              
262 0           my $pos = pos $$out_ref;
263 0           $$out_ref .= $s;
264 0           pos($out_ref) = $pos;
265 0           return 1;
266 0           };
267              
268 0           my ($harness) = @_;
269              
270 0           $self->_reset_temp_file_pointer;
271              
272 0           1 while $self->_do_filters($harness);
273              
274 0           pop @{ $self->{FILTERS} };
  0            
275              
276 0           IPC::Run::_close( $self->{TFD} );
277             }
278              
279             =head1 SUBROUTINES
280              
281             =over
282              
283             =item poll
284              
285             Windows version of IPC::Run::IP::poll.
286              
287             =back
288              
289             =cut
290              
291             sub poll {
292 0     0 1   my IPC::Run::Win32IO $self = shift;
293              
294 0 0 0       return if $self->{SEND_THROUGH_TEMP_FILE} || $self->{RECV_THROUGH_TEMP_FILE};
295              
296 0           return $self->SUPER::poll(@_);
297             }
298              
299             ## When threaded Perls get good enough, we should use threads here.
300             ## The problem with threaded perls is that they dup() all sorts of
301             ## filehandles and fds and don't allow sufficient control over
302             ## closing off the ones we don't want.
303              
304             sub _spawn_pumper {
305 0     0     my ( $stdin, $stdout, $debug_fd, $binmode, $child_label, @opts ) = @_;
306 0           my ( $stdin_fd, $stdout_fd ) = ( fileno $stdin, fileno $stdout );
307              
308 0 0         _debug "pumper stdin = ", $stdin_fd if _debugging_details;
309 0 0         _debug "pumper stdout = ", $stdout_fd if _debugging_details;
310 0           _inherit $stdin_fd, $stdout_fd, $debug_fd;
311 0           my @I_options = map qq{"-I$_"}, @INC;
312              
313 0 0         my $cmd_line = join(
314             " ",
315             qq{"$^X"},
316             @I_options,
317             qw(-MIPC::Run::Win32Pump -e 1 ),
318             ## I'm using this clunky way of passing filehandles to the child process
319             ## in order to avoid some kind of premature closure of filehandles
320             ## problem I was having with VCP's test suite when passing them
321             ## via CreateProcess. All of the ## REMOVE code is stuff I'd like
322             ## to be rid of and the ## ADD code is what I'd like to use.
323             FdGetOsFHandle($stdin_fd), ## REMOVE
324             FdGetOsFHandle($stdout_fd), ## REMOVE
325             FdGetOsFHandle($debug_fd), ## REMOVE
326             $binmode ? 1 : 0,
327             $$, $^T, _debugging_level, qq{"$child_label"},
328             @opts
329             );
330              
331             # open SAVEIN, "<&STDIN" or croak "$! saving STDIN"; #### ADD
332             # open SAVEOUT, ">&STDOUT" or croak "$! saving STDOUT"; #### ADD
333             # open SAVEERR, ">&STDERR" or croak "$! saving STDERR"; #### ADD
334             # _dont_inherit \*SAVEIN; #### ADD
335             # _dont_inherit \*SAVEOUT; #### ADD
336             # _dont_inherit \*SAVEERR; #### ADD
337             # open STDIN, "<&$stdin_fd" or croak "$! dup2()ing $stdin_fd (pumper's STDIN)"; #### ADD
338             # open STDOUT, ">&$stdout_fd" or croak "$! dup2()ing $stdout_fd (pumper's STDOUT)"; #### ADD
339             # open STDERR, ">&$debug_fd" or croak "$! dup2()ing $debug_fd (pumper's STDERR/debug_fd)"; #### ADD
340              
341 0 0         _debug "pump cmd line: ", $cmd_line if _debugging_details;
342              
343 0           my $process;
344 0 0         Win32::Process::Create(
345             $process,
346             $^X,
347             $cmd_line,
348             1, ## Inherit handles
349             NORMAL_PRIORITY_CLASS,
350             ".",
351             ) or croak "$!: Win32::Process::Create()";
352              
353             # open STDIN, "<&SAVEIN" or croak "$! restoring STDIN"; #### ADD
354             # open STDOUT, ">&SAVEOUT" or croak "$! restoring STDOUT"; #### ADD
355             # open STDERR, ">&SAVEERR" or croak "$! restoring STDERR"; #### ADD
356             # close SAVEIN or croak "$! closing SAVEIN"; #### ADD
357             # close SAVEOUT or croak "$! closing SAVEOUT"; #### ADD
358             # close SAVEERR or croak "$! closing SAVEERR"; #### ADD
359              
360 0 0         close $stdin or croak "$! closing pumper's stdin in parent";
361 0 0         close $stdout or croak "$! closing pumper's stdout in parent";
362              
363             # Don't close $debug_fd, we need it, as do other pumpers.
364              
365             # Pause a moment to allow the child to get up and running and emit
366             # debug messages. This does not always work.
367             # select undef, undef, undef, 1 if _debugging_details;
368              
369 0 0         _debug "_spawn_pumper pid = ", $process->GetProcessID
370             if _debugging_data;
371             }
372              
373             my $loopback = inet_aton "127.0.0.1";
374             my $tcp_proto = getprotobyname('tcp');
375             croak "$!: getprotobyname('tcp')" unless defined $tcp_proto;
376              
377             sub _socket {
378 0     0     my ($server) = @_;
379 0   0       $server ||= gensym;
380 0           my $client = gensym;
381              
382 0           my $listener = gensym;
383 0 0         socket $listener, PF_INET, SOCK_STREAM, $tcp_proto
384             or croak "$!: socket()";
385 0 0         setsockopt $listener, SOL_SOCKET, SO_REUSEADDR, pack( "l", 0 )
386             or croak "$!: setsockopt()";
387              
388 0 0         unless ( bind $listener, sockaddr_in( 0, $loopback ) ) {
389 0           croak "Error binding: $!";
390             }
391              
392 0           my ($port) = sockaddr_in( getsockname($listener) );
393              
394 0 0         _debug "win32 port = $port" if _debugging_details;
395              
396 0 0         listen $listener, my $queue_size = 1
397             or croak "$!: listen()";
398              
399             {
400 0 0         socket $client, PF_INET, SOCK_STREAM, $tcp_proto
401             or croak "$!: socket()";
402              
403 0           my $paddr = sockaddr_in( $port, $loopback );
404              
405 0 0         connect $client, $paddr
406             or croak "$!: connect()";
407              
408 0 0         croak "$!: accept" unless defined $paddr;
409              
410             ## The windows "default" is SO_DONTLINGER, which should make
411             ## sure all socket data goes through. I have my doubts based
412             ## on experimentation, but nothing prompts me to set SO_LINGER
413             ## at this time...
414 0 0         setsockopt $client, IPPROTO_TCP, TCP_NODELAY, pack( "l", 0 )
415             or croak "$!: setsockopt()";
416             }
417              
418             {
419 0 0         _debug "accept()ing on port $port" if _debugging_details;
  0            
  0            
420 0           my $paddr = accept( $server, $listener );
421 0 0         croak "$!: accept()" unless defined $paddr;
422             }
423              
424 0 0         _debug "win32 _socket = ( ", fileno $server, ", ", fileno $client, " ) on port $port"
425             if _debugging_details;
426 0           return ( $server, $client );
427             }
428              
429             sub _open_socket_pipe {
430 0     0     my IPC::Run::Win32IO $self = shift;
431 0           my ( $debug_fd, $parent_handle ) = @_;
432              
433 0           my $is_send_to_child = $self->dir eq "<";
434              
435 0           $self->{CHILD_HANDLE} = gensym;
436 0           $self->{PUMP_PIPE_HANDLE} = gensym;
437              
438             (
439             $self->{PARENT_HANDLE},
440             $self->{PUMP_SOCKET_HANDLE}
441 0           ) = _socket $parent_handle;
442              
443             ## These binmodes seem to have no effect on Win2K, but just to be safe
444             ## I do them.
445 0 0         binmode $self->{PARENT_HANDLE} or die $!;
446 0 0         binmode $self->{PUMP_SOCKET_HANDLE} or die $!;
447              
448             _debug "PUMP_SOCKET_HANDLE = ", fileno $self->{PUMP_SOCKET_HANDLE}
449 0 0         if _debugging_details;
450             ##my $buf;
451             ##$buf = "write on child end of " . fileno( $self->{WRITE_HANDLE} ) . "\n\n\n\n\n";
452             ##POSIX::write(fileno $self->{WRITE_HANDLE}, $buf, length $buf) or warn "$! in syswrite";
453             ##$buf = "write on parent end of " . fileno( $self->{CHILD_HANDLE} ) . "\r\n";
454             ##POSIX::write(fileno $self->{CHILD_HANDLE},$buf, length $buf) or warn "$! in syswrite";
455             ## $self->{CHILD_HANDLE}->autoflush( 1 );
456             ## $self->{WRITE_HANDLE}->autoflush( 1 );
457              
458             ## Now fork off a data pump and arrange to return the correct fds.
459 0 0         if ($is_send_to_child) {
460             pipe $self->{CHILD_HANDLE}, $self->{PUMP_PIPE_HANDLE}
461 0 0         or croak "$! opening child pipe";
462             _debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
463 0 0         if _debugging_details;
464             _debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
465 0 0         if _debugging_details;
466             }
467             else {
468             pipe $self->{PUMP_PIPE_HANDLE}, $self->{CHILD_HANDLE}
469 0 0         or croak "$! opening child pipe";
470             _debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
471 0 0         if _debugging_details;
472             _debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
473 0 0         if _debugging_details;
474             }
475              
476             ## These binmodes seem to have no effect on Win2K, but just to be safe
477             ## I do them.
478 0           binmode $self->{CHILD_HANDLE};
479 0           binmode $self->{PUMP_PIPE_HANDLE};
480              
481             ## No child should ever see this.
482 0           _dont_inherit $self->{PARENT_HANDLE};
483              
484             ## We clear the inherit flag so these file descriptors are not inherited.
485             ## It'll be dup()ed on to STDIN/STDOUT/STDERR before CreateProcess is
486             ## called and *that* fd will be inheritable.
487 0           _dont_inherit $self->{PUMP_SOCKET_HANDLE};
488 0           _dont_inherit $self->{PUMP_PIPE_HANDLE};
489 0           _dont_inherit $self->{CHILD_HANDLE};
490              
491             ## Need to return $self so the HANDLEs don't get freed.
492             ## Return $self, $parent_fd, $child_fd
493             my ( $parent_fd, $child_fd ) = (
494             fileno $self->{PARENT_HANDLE},
495             fileno $self->{CHILD_HANDLE}
496 0           );
497              
498             ## Both PUMP_..._HANDLEs will be closed, no need to worry about
499             ## inheritance.
500 0 0 0       _debug "binmode on" if _debugging_data && $self->binmode;
501             _spawn_pumper(
502             $is_send_to_child
503             ? ( $self->{PUMP_SOCKET_HANDLE}, $self->{PUMP_PIPE_HANDLE} )
504 0 0         : ( $self->{PUMP_PIPE_HANDLE}, $self->{PUMP_SOCKET_HANDLE} ),
505             $debug_fd,
506             $self->binmode,
507             $child_fd . $self->dir . "pump" . $self->dir . $parent_fd,
508             );
509              
510             {
511 0           my $foo;
  0            
512 0 0         confess "PARENT_HANDLE no longer open"
513             unless POSIX::read( $parent_fd, $foo, 0 );
514             }
515              
516 0 0         _debug "win32_fake_pipe = ( $parent_fd, $child_fd )"
517             if _debugging_details;
518              
519 0           $self->{FD} = $parent_fd;
520 0           $self->{TFD} = $child_fd;
521             }
522              
523             sub _do_open {
524 0     0     my IPC::Run::Win32IO $self = shift;
525              
526 0 0         if ( $self->{SEND_THROUGH_TEMP_FILE} ) {
    0          
527 0           return $self->_send_through_temp_file(@_);
528             }
529             elsif ( $self->{RECV_THROUGH_TEMP_FILE} ) {
530 0           return $self->_init_recv_through_temp_file(@_);
531             }
532             else {
533 0           return $self->_open_socket_pipe(@_);
534             }
535             }
536              
537             1;
538              
539             =pod
540              
541             =head1 AUTHOR
542              
543             Barries Slaymaker . Funded by Perforce Software, Inc.
544              
545             =head1 COPYRIGHT
546              
547             Copyright 2001, Barrie Slaymaker, All Rights Reserved.
548              
549             You may use this under the terms of either the GPL 2.0 or the Artistic License.
550              
551             =cut