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