File Coverage

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