+++ /dev/null
-package IPC::Run::Win32Helper;
-
-=pod
-
-=head1 NAME
-
-IPC::Run::Win32Helper - helper routines for IPC::Run on Win32 platforms.
-
-=head1 SYNOPSIS
-
- use IPC::Run::Win32Helper; # Exports all by default
-
-=head1 DESCRIPTION
-
-IPC::Run needs to use sockets to redirect subprocess I/O so that the select() loop
-will work on Win32. This seems to only work on WinNT and Win2K at this time, not
-sure if it will ever work on Win95 or Win98. If you have experience in this area, please
-contact me at barries@slaysys.com, thanks!.
-
-=cut
-
-use strict;
-use Carp;
-use IO::Handle;
-use vars qw{ $VERSION @ISA @EXPORT };
-BEGIN {
- $VERSION = '0.90';
- @ISA = qw( Exporter );
- @EXPORT = qw(
- win32_spawn
- win32_parse_cmd_line
- _dont_inherit
- _inherit
- );
-}
-
-require POSIX;
-
-use Text::ParseWords;
-use Win32::Process;
-use IPC::Run::Debug;
-use Win32API::File qw(
- FdGetOsFHandle
- SetHandleInformation
- HANDLE_FLAG_INHERIT
- INVALID_HANDLE_VALUE
-);
-
-## Takes an fd or a GLOB ref, never never never a Win32 handle.
-sub _dont_inherit {
- for ( @_ ) {
- next unless defined $_;
- my $fd = $_;
- $fd = fileno $fd if ref $fd;
- _debug "disabling inheritance of ", $fd if _debugging_details;
- my $osfh = FdGetOsFHandle $fd;
- croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE;
-
- SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 0 );
- }
-}
-
-sub _inherit { #### REMOVE
- for ( @_ ) { #### REMOVE
- next unless defined $_; #### REMOVE
- my $fd = $_; #### REMOVE
- $fd = fileno $fd if ref $fd; #### REMOVE
- _debug "enabling inheritance of ", $fd if _debugging_details; #### REMOVE
- my $osfh = FdGetOsFHandle $fd; #### REMOVE
- croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE; #### REMOVE
- #### REMOVE
- SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 1 ); #### REMOVE
- } #### REMOVE
-} #### REMOVE
- #### REMOVE
-#sub _inherit {
-# for ( @_ ) {
-# next unless defined $_;
-# my $osfh = GetOsFHandle $_;
-# croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE;
-# SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
-# }
-#}
-
-=pod
-
-=head1 FUNCTIONS
-
-=over
-
-=item optimize()
-
-Most common incantations of C<run()> (I<not> C<harness()>, C<start()>,
-or C<finish()>) now use temporary files to redirect input and output
-instead of pumper processes.
-
-Temporary files are used when sending to child processes if input is
-taken from a scalar with no filter subroutines. This is the only time
-we can assume that the parent is not interacting with the child's
-redirected input as it runs.
-
-Temporary files are used when receiving from children when output is
-to a scalar or subroutine with or without filters, but only if
-the child in question closes its inputs or takes input from
-unfiltered SCALARs or named files. Normally, a child inherits its STDIN
-from its parent; to close it, use "0<&-" or the C<< noinherit => 1 >> option.
-If data is sent to the child from CODE refs, filehandles or from
-scalars through filters than the child's outputs will not be optimized
-because C<optimize()> assumes the parent is interacting with the child.
-It is ok if the output is filtered or handled by a subroutine, however.
-
-This assumes that all named files are real files (as opposed to named
-pipes) and won't change; and that a process is not communicating with
-the child indirectly (through means not visible to IPC::Run).
-These can be an invalid assumptions, but are the 99% case.
-Write me if you need an option to enable or disable optimizations; I
-suspect it will work like the C<binary()> modifier.
-
-To detect cases that you might want to optimize by closing inputs, try
-setting the C<IPCRUNDEBUG> environment variable to the special C<notopt>
-value:
-
- C:> set IPCRUNDEBUG=notopt
- C:> my_app_that_uses_IPC_Run.pl
-
-=item optimizer() rationalizations
-
-Only for that limited case can we be sure that it's ok to batch all the
-input in to a temporary file. If STDIN is from a SCALAR or from a named
-file or filehandle (again, only in C<run()>), then outputs to CODE refs
-are also assumed to be safe enough to batch through a temp file,
-otherwise only outputs to SCALAR refs are batched. This can cause a bit
-of grief if the parent process benefits from or relies on a bit of
-"early returns" coming in before the child program exits. As long as
-the output is redirected to a SCALAR ref, this will not be visible.
-When output is redirected to a subroutine or (deprecated) filters, the
-subroutine will not get any data until after the child process exits,
-and it is likely to get bigger chunks of data at once.
-
-The reason for the optimization is that, without it, "pumper" processes
-are used to overcome the inconsistencies of the Win32 API. We need to
-use anonymous pipes to connect to the child processes' stdin, stdout,
-and stderr, yet select() does not work on these. select() only works on
-sockets on Win32. So for each redirected child handle, there is
-normally a "pumper" process that connects to the parent using a
-socket--so the parent can select() on that fd--and to the child on an
-anonymous pipe--so the child can read/write a pipe.
-
-Using a socket to connect directly to the child (as at least one MSDN
-article suggests) seems to cause the trailing output from most children
-to be lost. I think this is because child processes rarely close their
-stdout and stderr explicitly, and the winsock dll does not seem to flush
-output when a process that uses it exits without explicitly closing
-them.
-
-Because of these pumpers and the inherent slowness of Win32
-CreateProcess(), child processes with redirects are quite slow to
-launch; so this routine looks for the very common case of
-reading/writing to/from scalar references in a run() routine and
-converts such reads and writes in to temporary file reads and writes.
-
-Such files are marked as FILE_ATTRIBUTE_TEMPORARY to increase speed and
-as FILE_FLAG_DELETE_ON_CLOSE so it will be cleaned up when the child
-process exits (for input files). The user's default permissions are
-used for both the temporary files and the directory that contains them,
-hope your Win32 permissions are secure enough for you. Files are
-created with the Win32API::File defaults of
-FILE_SHARE_READ|FILE_SHARE_WRITE.
-
-Setting the debug level to "details" or "gory" will give detailed
-information about the optimization process; setting it to "basic" or
-higher will tell whether or not a given call is optimized. Setting
-it to "notopt" will highlight those calls that aren't optimized.
-
-=cut
-
-sub optimize {
- my ( $h ) = @_;
-
- my @kids = @{$h->{KIDS}};
-
- my $saw_pipe;
-
- my ( $ok_to_optimize_outputs, $veto_output_optimization );
-
- for my $kid ( @kids ) {
- ( $ok_to_optimize_outputs, $veto_output_optimization ) = ()
- unless $saw_pipe;
-
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over ok of non-SCALAR output optimization"
- if _debugging_details && $ok_to_optimize_outputs;
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over veto of non-SCALAR output optimization"
- if _debugging_details && $veto_output_optimization;
-
- if ( $h->{noinherit} && ! $ok_to_optimize_outputs ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) STDIN not inherited from parent oking non-SCALAR output optimization"
- if _debugging_details && $ok_to_optimize_outputs;
- $ok_to_optimize_outputs = 1;
- }
-
- for ( @{$kid->{OPS}} ) {
- if ( substr( $_->{TYPE}, 0, 1 ) eq "<" ) {
- if ( $_->{TYPE} eq "<" ) {
- if ( @{$_->{FILTERS}} > 1 ) {
- ## Can't assume that the filters are idempotent.
- }
- elsif ( ref $_->{SOURCE} eq "SCALAR"
- || ref $_->{SOURCE} eq "GLOB"
- || UNIVERSAL::isa( $_, "IO::Handle" )
- ) {
- if ( $_->{KFD} == 0 ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) 0$_->{TYPE}",
- ref $_->{SOURCE},
- ", ok to optimize outputs"
- if _debugging_details;
- $ok_to_optimize_outputs = 1;
- }
- $_->{SEND_THROUGH_TEMP_FILE} = 1;
- next;
- }
- elsif ( ! ref $_->{SOURCE} && defined $_->{SOURCE} ) {
- if ( $_->{KFD} == 0 ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) 0<$_->{SOURCE}, ok to optimize outputs",
- if _debugging_details;
- $ok_to_optimize_outputs = 1;
- }
- next;
- }
- }
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) ",
- $_->{KFD},
- $_->{TYPE},
- defined $_->{SOURCE}
- ? ref $_->{SOURCE} ? ref $_->{SOURCE}
- : $_->{SOURCE}
- : defined $_->{FILENAME}
- ? $_->{FILENAME}
- : "",
- @{$_->{FILTERS}} > 1 ? " with filters" : (),
- ", VETOING output opt."
- if _debugging_details || _debugging_not_optimized;
- $veto_output_optimization = 1;
- }
- elsif ( $_->{TYPE} eq "close" && $_->{KFD} == 0 ) {
- $ok_to_optimize_outputs = 1;
- _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&-, ok to optimize outputs"
- if _debugging_details;
- }
- elsif ( $_->{TYPE} eq "dup" && $_->{KFD2} == 0 ) {
- $veto_output_optimization = 1;
- _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&$_->{KFD2}, VETOING output opt."
- if _debugging_details || _debugging_not_optimized;
- }
- elsif ( $_->{TYPE} eq "|" ) {
- $saw_pipe = 1;
- }
- }
-
- if ( ! $ok_to_optimize_outputs && ! $veto_output_optimization ) {
- _debug
- "Win32 optimizer: (kid $kid->{NUM}) child STDIN not redirected, VETOING non-SCALAR output opt."
- if _debugging_details || _debugging_not_optimized;
- $veto_output_optimization = 1;
- }
-
- if ( $ok_to_optimize_outputs && $veto_output_optimization ) {
- $ok_to_optimize_outputs = 0;
- _debug "Win32 optimizer: (kid $kid->{NUM}) non-SCALAR output optimizations VETOed"
- if _debugging_details || _debugging_not_optimized;
- }
-
- ## SOURCE/DEST ARRAY means it's a filter.
- ## TODO: think about checking to see if the final input/output of
- ## a filter chain (an ARRAY SOURCE or DEST) is a scalar...but
- ## we may be deprecating filters.
-
- for ( @{$kid->{OPS}} ) {
- if ( $_->{TYPE} eq ">" ) {
- if ( ref $_->{DEST} eq "SCALAR"
- || (
- ( @{$_->{FILTERS}} > 1
- || ref $_->{DEST} eq "CODE"
- || ref $_->{DEST} eq "ARRAY" ## Filters?
- )
- && ( $ok_to_optimize_outputs && ! $veto_output_optimization )
- )
- ) {
- $_->{RECV_THROUGH_TEMP_FILE} = 1;
- next;
- }
- _debug
- "Win32 optimizer: NOT optimizing (kid $kid->{NUM}) ",
- $_->{KFD},
- $_->{TYPE},
- defined $_->{DEST}
- ? ref $_->{DEST} ? ref $_->{DEST}
- : $_->{SOURCE}
- : defined $_->{FILENAME}
- ? $_->{FILENAME}
- : "",
- @{$_->{FILTERS}} ? " with filters" : (),
- if _debugging_details;
- }
- }
- }
-
-}
-
-=pod
-
-=item win32_parse_cmd_line
-
- @words = win32_parse_cmd_line( q{foo bar 'baz baz' "bat bat"} );
-
-returns 4 words. This parses like the bourne shell (see
-the bit about shellwords() in L<Text::ParseWords>), assuming we're
-trying to be a little cross-platform here. The only difference is
-that "\" is *not* treated as an escape except when it precedes
-punctuation, since it's used all over the place in DOS path specs.
-
-TODO: globbing? probably not (it's unDOSish).
-
-TODO: shebang emulation? Probably, but perhaps that should be part
-of Run.pm so all spawned processes get the benefit.
-
-LIMITATIONS: shellwords dies silently on malformed input like
-
- a\"
-
-=cut
-
-sub win32_parse_cmd_line {
- my $line = shift;
- $line =~ s{(\\[\w\s])}{\\$1}g;
- return shellwords $line;
-}
-
-=pod
-
-=item win32_spawn
-
-Spawns a child process, possibly with STDIN, STDOUT, and STDERR (file descriptors 0, 1, and 2, respectively) redirected.
-
-B<LIMITATIONS>.
-
-Cannot redirect higher file descriptors due to lack of support for this in the
-Win32 environment.
-
-This can be worked around by marking a handle as inheritable in the
-parent (or leaving it marked; this is the default in perl), obtaining it's
-Win32 handle with C<Win32API::GetOSFHandle(FH)> or
-C<Win32API::FdGetOsFHandle($fd)> and passing it to the child using the command
-line, the environment, or any other IPC mechanism (it's a plain old integer).
-The child can then use C<OsFHandleOpen()> or C<OsFHandleOpenFd()> and possibly
-C<<open FOO ">&BAR">> or C<<open FOO ">&$fd>> as need be. Ach, the pain!
-
-Remember to check the Win32 handle against INVALID_HANDLE_VALUE.
-
-=cut
-
-sub _save {
- my ( $saved, $saved_as, $fd ) = @_;
-
- ## We can only save aside the original fds once.
- return if exists $saved->{$fd};
-
- my $saved_fd = IPC::Run::_dup( $fd );
- _dont_inherit $saved_fd;
-
- $saved->{$fd} = $saved_fd;
- $saved_as->{$saved_fd} = $fd;
-
- _dont_inherit $saved->{$fd};
-}
-
-sub _dup2_gently {
- my ( $saved, $saved_as, $fd1, $fd2 ) = @_;
- _save $saved, $saved_as, $fd2;
-
- if ( exists $saved_as->{$fd2} ) {
- ## The target fd is colliding with a saved-as fd, gotta bump
- ## the saved-as fd to another fd.
- my $orig_fd = delete $saved_as->{$fd2};
- my $saved_fd = IPC::Run::_dup( $fd2 );
- _dont_inherit $saved_fd;
-
- $saved->{$orig_fd} = $saved_fd;
- $saved_as->{$saved_fd} = $orig_fd;
- }
- _debug "moving $fd1 to kid's $fd2" if _debugging_details;
- IPC::Run::_dup2_rudely( $fd1, $fd2 );
-}
-
-sub win32_spawn {
- my ( $cmd, $ops) = @_;
-
- ## NOTE: The debug pipe write handle is passed to pump processes as STDOUT.
- ## and is not to the "real" child process, since they would not know
- ## what to do with it...unlike Unix, we have no code executing in the
- ## child before the "real" child is exec()ed.
-
- my %saved; ## Map of parent's orig fd -> saved fd
- my %saved_as; ## Map of parent's saved fd -> orig fd, used to
- ## detect collisions between a KFD and the fd a
- ## parent's fd happened to be saved to.
-
- for my $op ( @$ops ) {
- _dont_inherit $op->{FD} if defined $op->{FD};
-
- if ( defined $op->{KFD} && $op->{KFD} > 2 ) {
- ## TODO: Detect this in harness()
- ## TODO: enable temporary redirections if ever necessary, not
- ## sure why they would be...
- ## 4>&1 1>/dev/null 1>&4 4>&-
- croak "Can't redirect fd #", $op->{KFD}, " on Win32";
- }
-
- ## This is very similar logic to IPC::Run::_do_kid_and_exit().
- if ( defined $op->{TFD} ) {
- unless ( $op->{TFD} == $op->{KFD} ) {
- _dup2_gently \%saved, \%saved_as, $op->{TFD}, $op->{KFD};
- _dont_inherit $op->{TFD};
- }
- }
- elsif ( $op->{TYPE} eq "dup" ) {
- _dup2_gently \%saved, \%saved_as, $op->{KFD1}, $op->{KFD2}
- unless $op->{KFD1} == $op->{KFD2};
- }
- elsif ( $op->{TYPE} eq "close" ) {
- _save \%saved, \%saved_as, $op->{KFD};
- IPC::Run::_close( $op->{KFD} );
- }
- elsif ( $op->{TYPE} eq "init" ) {
- ## TODO: detect this in harness()
- croak "init subs not allowed on Win32";
- }
- }
-
- my $process;
- my $cmd_line = join " ", map {
- ( my $s = $_ ) =~ s/"/"""/g;
- $s = qq{"$s"} if /[\"\s]|^$/;
- $s;
- } @$cmd;
-
- _debug "cmd line: ", $cmd_line
- if _debugging;
-
- Win32::Process::Create(
- $process,
- $cmd->[0],
- $cmd_line,
- 1, ## Inherit handles
- NORMAL_PRIORITY_CLASS,
- ".",
- ) or croak "$!: Win32::Process::Create()";
-
- for my $orig_fd ( keys %saved ) {
- IPC::Run::_dup2_rudely( $saved{$orig_fd}, $orig_fd );
- IPC::Run::_close( $saved{$orig_fd} );
- }
-
- return ( $process->GetProcessID(), $process );
-}
-
-
-1;
-
-=pod
-
-=back
-
-=head1 AUTHOR
-
-Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
-
-=head1 COPYRIGHT
-
-Copyright 2001, Barrie Slaymaker, All Rights Reserved.
-
-You may use this under the terms of either the GPL 2.0 or the Artistic License.
-
-=cut