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