{PTYS}->{$pty_id} = undef;
}
my $source = $5;
my @filters;
my $binmode;
unless ( length $source ) {
if ( ! $succinct ) {
while ( @args > 1
&& (
( ref $args[1] && ! UNIVERSAL::isa $args[1], "IPC::Run::Timer" )
|| UNIVERSAL::isa $args[0], "IPC::Run::binmode_pseudo_filter"
)
) {
if ( UNIVERSAL::isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
$binmode = shift( @args )->();
}
else {
push @filters, shift @args
}
}
}
$source = shift @args;
croak "'$_' missing a source" if _empty $source;
_debug(
'Kid ', $cur_kid->{NUM}, "'s input fd ", $kfd,
' has ', scalar( @filters ), ' filters.'
) if _debugging_details && @filters;
};
my IPC::Run::IO $pipe = IPC::Run::IO->_new_internal(
$type, $kfd, $pty_id, $source, $binmode, @filters
);
if ( ( ref $source eq 'GLOB' || UNIVERSAL::isa $source, 'IO::Handle' )
&& $type !~ /^{DONT_CLOSE} = 1; ## this FD is not closed by us.
_dont_inherit( $source ) if Win32_MODE;
}
push @{$cur_kid->{OPS}}, $pipe;
}
elsif ( /^() (>>?) (&) () (.*)$/x
|| /^() (&) (>pipe) () () $/x
|| /^() (>pipe)(&) () () $/x
|| /^(\d*)() (>pipe) () () $/x
|| /^() (&) (>pty) ( \w*)> () $/x
## TODO: || /^() (>pty) (\d*)> (&) () $/x
|| /^(\d*)() (>pty) ( \w*)> () $/x
|| /^() (&) (>>?) () (.*)$/x
|| /^(\d*)() (>>?) () (.*)$/x
) {
croak "No command before '$_'" unless $cur_kid;
$succinct = ! $first_parse;
my $type = (
$2 eq '>pipe' || $3 eq '>pipe'
? '>pipe'
: $2 eq '>pty' || $3 eq '>pty'
? '>pty>'
: '>'
);
my $kfd = length $1 ? $1 : 1;
my $trunc = ! ( $2 eq '>>' || $3 eq '>>' );
my $pty_id = (
$2 eq '>pty' || $3 eq '>pty'
? length $4 ? $4 : 0
: undef
);
my $stderr_too =
$2 eq '&'
|| $3 eq '&'
|| ( ! length $1 && substr( $type, 0, 4 ) eq '>pty' );
my $dest = $5;
my @filters;
my $binmode = 0;
unless ( length $dest ) {
if ( ! $succinct ) {
## unshift...shift: '>' filters source...sink left...right
while ( @args > 1
&& (
( ref $args[1] && ! UNIVERSAL::isa $args[1], "IPC::Run::Timer" )
|| UNIVERSAL::isa $args[0], "IPC::Run::binmode_pseudo_filter"
)
) {
if ( UNIVERSAL::isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
$binmode = shift( @args )->();
}
else {
unshift @filters, shift @args;
}
}
}
$dest = shift @args;
_debug(
'Kid ', $cur_kid->{NUM}, "'s output fd ", $kfd,
' has ', scalar( @filters ), ' filters.'
) if _debugging_details && @filters;
if ( $type eq '>pty>' ) {
## do the require here to cause early error reporting
require IO::Pty;
## Just flag the pyt's existence for now. _open_pipes()
## will new an IO::Pty for each key.
$self->{PTYS}->{$pty_id} = undef;
}
}
croak "'$_' missing a destination" if _empty $dest;
my $pipe = IPC::Run::IO->_new_internal(
$type, $kfd, $pty_id, $dest, $binmode, @filters
);
$pipe->{TRUNC} = $trunc;
if ( ( UNIVERSAL::isa( $dest, 'GLOB' ) || UNIVERSAL::isa( $dest, 'IO::Handle' ) )
&& $type !~ /^>(pty>|pipe)$/
) {
_debug "setting DONT_CLOSE" if _debugging_details;
$pipe->{DONT_CLOSE} = 1; ## this FD is not closed by us.
}
push @{$cur_kid->{OPS}}, $pipe;
push @{$cur_kid->{OPS}}, {
TYPE => 'dup',
KFD1 => 1,
KFD2 => 2,
} if $stderr_too;
}
elsif ( $_ eq "|" ) {
croak "No command before '$_'" unless $cur_kid;
unshift @{$cur_kid->{OPS}}, {
TYPE => '|',
KFD => 1,
};
$succinct = 1;
$assumed_fd = 1;
$cur_kid = undef;
}
elsif ( $_ eq "&" ) {
croak "No command before '$_'" unless $cur_kid;
unshift @{$cur_kid->{OPS}}, {
TYPE => 'close',
KFD => 0,
};
$succinct = 1;
$assumed_fd = 0;
$cur_kid = undef;
}
elsif ( $_ eq 'init' ) {
croak "No command before '$_'" unless $cur_kid;
push @{$cur_kid->{OPS}}, {
TYPE => 'init',
SUB => shift @args,
};
}
elsif ( ! ref $_ ) {
$self->{$_} = shift @args;
}
elsif ( $_ eq 'init' ) {
croak "No command before '$_'" unless $cur_kid;
push @{$cur_kid->{OPS}}, {
TYPE => 'init',
SUB => shift @args,
};
}
elsif ( $succinct && $first_parse ) {
## It's not an opcode, and no explicit opcodes have been
## seen yet, so assume it's a file name.
unshift @args, $_;
if ( ! $assumed_fd ) {
$_ = "$assumed_fd<",
}
else {
$_ = "$assumed_fd>",
}
_debug "assuming '", $_, "'" if _debugging_details;
++$assumed_fd;
$first_parse = 0;
goto REPARSE;
}
else {
croak join(
'',
'Unexpected ',
( ref() ? $_ : 'scalar' ),
' in harness() parameter ',
$arg_count - @args
);
}
};
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
} }
die join( '', @errs ) if @errs;
$self->{STATE} = _harnessed;
# $self->timeout( $options->{timeout} ) if exists $options->{timeout};
return $self;
}
sub _open_pipes {
my IPC::Run $self = shift;
my @errs;
my @close_on_fail;
## When a pipe character is seen, a pipe is created. $pipe_read_fd holds
## the dangling read end of the pipe until we get to the next process.
my $pipe_read_fd;
## Output descriptors for the last command are shared by all children.
## @output_fds_accum accumulates the current set of output fds.
my @output_fds_accum;
for ( sort keys %{$self->{PTYS}} ) {
_debug "opening pty '", $_, "'" if _debugging_details;
my $pty = _pty;
$self->{PTYS}->{$_} = $pty;
}
for ( @{$self->{IOS}} ) {
eval { $_->init; };
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
else {
push @close_on_fail, $_;
}
}
## Loop through the kids and their OPS, interpreting any that require
## parent-side actions.
for my $kid ( @{$self->{KIDS}} ) {
unless ( ref $kid->{VAL} eq 'CODE' ) {
$kid->{PATH} = _search_path $kid->{VAL}->[0];
}
if ( defined $pipe_read_fd ) {
_debug "placing write end of pipe on kid $kid->{NUM}'s stdin"
if _debugging_details;
unshift @{$kid->{OPS}}, {
TYPE => 'PIPE', ## Prevent next loop from triggering on this
KFD => 0,
TFD => $pipe_read_fd,
};
$pipe_read_fd = undef;
}
@output_fds_accum = ();
for my $op ( @{$kid->{OPS}} ) {
# next if $op->{IS_DEBUG};
my $ok = eval {
if ( $op->{TYPE} eq '<' ) {
my $source = $op->{SOURCE};
if ( ! ref $source ) {
_debug(
"kid ", $kid->{NUM}, " to read ", $op->{KFD},
" from '" . $source, "' (read only)"
) if _debugging_details;
croak "simulated open failure"
if $self->{_simulate_open_failure};
$op->{TFD} = _sysopen( $source, O_RDONLY );
push @close_on_fail, $op->{TFD};
}
elsif ( UNIVERSAL::isa( $source, 'GLOB' )
|| UNIVERSAL::isa( $source, 'IO::Handle' )
) {
croak
"Unopened filehandle in input redirect for $op->{KFD}"
unless defined fileno $source;
$op->{TFD} = fileno $source;
_debug(
"kid ", $kid->{NUM}, " to read ", $op->{KFD},
" from fd ", $op->{TFD}
) if _debugging_details;
}
elsif ( UNIVERSAL::isa( $source, 'SCALAR' ) ) {
_debug(
"kid ", $kid->{NUM}, " to read ", $op->{KFD},
" from SCALAR"
) if _debugging_details;
$op->open_pipe( $self->_debug_fd );
push @close_on_fail, $op->{KFD}, $op->{FD};
my $s = '';
$op->{KIN_REF} = \$s;
}
elsif ( UNIVERSAL::isa( $source, 'CODE' ) ) {
_debug(
'kid ', $kid->{NUM}, ' to read ', $op->{KFD}, ' from CODE'
) if _debugging_details;
$op->open_pipe( $self->_debug_fd );
push @close_on_fail, $op->{KFD}, $op->{FD};
my $s = '';
$op->{KIN_REF} = \$s;
}
else {
croak(
"'"
. ref( $source )
. "' not allowed as a source for input redirection"
);
}
$op->_init_filters;
}
elsif ( $op->{TYPE} eq '{KFD},
' from a pipe IPC::Run opens and returns',
) if _debugging_details;
my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{SOURCE} );
_debug "caller will write to ", fileno $op->{SOURCE}
if _debugging_details;
$op->{TFD} = $r;
$op->{FD} = undef; # we don't manage this fd
$op->_init_filters;
}
elsif ( $op->{TYPE} eq '{KFD}, " from pty '", $op->{PTY_ID}, "'",
) if _debugging_details;
for my $source ( $op->{SOURCE} ) {
if ( UNIVERSAL::isa( $source, 'SCALAR' ) ) {
_debug(
"kid ", $kid->{NUM}, " to read ", $op->{KFD},
" from SCALAR via pty '", $op->{PTY_ID}, "'"
) if _debugging_details;
my $s = '';
$op->{KIN_REF} = \$s;
}
elsif ( UNIVERSAL::isa( $source, 'CODE' ) ) {
_debug(
"kid ", $kid->{NUM}, " to read ", $op->{KFD},
" from CODE via pty '", $op->{PTY_ID}, "'"
) if _debugging_details;
my $s = '';
$op->{KIN_REF} = \$s;
}
else {
croak(
"'"
. ref( $source )
. "' not allowed as a source for '{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno;
$op->{TFD} = undef; # The fd isn't known until after fork().
$op->_init_filters;
}
elsif ( $op->{TYPE} eq '>' ) {
## N> output redirection.
my $dest = $op->{DEST};
if ( ! ref $dest ) {
_debug(
"kid ", $kid->{NUM}, " to write ", $op->{KFD},
" to '", $dest, "' (write only, create, ",
( $op->{TRUNC} ? 'truncate' : 'append' ),
")"
) if _debugging_details;
croak "simulated open failure"
if $self->{_simulate_open_failure};
$op->{TFD} = _sysopen(
$dest,
( O_WRONLY
| O_CREAT
| ( $op->{TRUNC} ? O_TRUNC : O_APPEND )
)
);
if ( Win32_MODE ) {
## I have no idea why this is needed to make the current
## file position survive the gyrations TFD must go
## through...
POSIX::lseek( $op->{TFD}, 0, POSIX::SEEK_END() );
}
push @close_on_fail, $op->{TFD};
}
elsif ( UNIVERSAL::isa( $dest, 'GLOB' ) ) {
croak(
"Unopened filehandle in output redirect, command $kid->{NUM}"
) unless defined fileno $dest;
## Turn on autoflush, mostly just to flush out
## existing output.
my $old_fh = select( $dest ); $| = 1; select( $old_fh );
$op->{TFD} = fileno $dest;
_debug(
'kid to write ', $op->{KFD}, ' to handle ', $op->{TFD}
) if _debugging_details;
}
elsif ( UNIVERSAL::isa( $dest, 'SCALAR' ) ) {
_debug(
"kid ", $kid->{NUM}, " to write $op->{KFD} to SCALAR"
) if _debugging_details;
$op->open_pipe( $self->_debug_fd );
push @close_on_fail, $op->{FD}, $op->{TFD};
$$dest = '' if $op->{TRUNC};
}
elsif ( UNIVERSAL::isa( $dest, 'CODE' ) ) {
_debug(
"kid $kid->{NUM} to write $op->{KFD} to CODE"
) if _debugging_details;
$op->open_pipe( $self->_debug_fd );
push @close_on_fail, $op->{FD}, $op->{TFD};
}
else {
croak(
"'"
. ref( $dest )
. "' not allowed as a sink for output redirection"
);
}
$output_fds_accum[$op->{KFD}] = $op;
$op->_init_filters;
}
elsif ( $op->{TYPE} eq '>pipe' ) {
## N> output redirection to a pipe we open, but don't select()
## on.
_debug(
"kid ", $kid->{NUM}, " to write ", $op->{KFD},
' to a pipe IPC::Run opens and returns'
) if _debugging_details;
my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{DEST} );
_debug "caller will read from ", fileno $op->{DEST}
if _debugging_details;
$op->{TFD} = $w;
$op->{FD} = undef; # we don't manage this fd
$op->_init_filters;
$output_fds_accum[$op->{KFD}] = $op;
}
elsif ( $op->{TYPE} eq '>pty>' ) {
my $dest = $op->{DEST};
if ( UNIVERSAL::isa( $dest, 'SCALAR' ) ) {
_debug(
"kid ", $kid->{NUM}, " to write ", $op->{KFD},
" to SCALAR via pty '", $op->{PTY_ID}, "'"
) if _debugging_details;
$$dest = '' if $op->{TRUNC};
}
elsif ( UNIVERSAL::isa( $dest, 'CODE' ) ) {
_debug(
"kid ", $kid->{NUM}, " to write ", $op->{KFD},
" to CODE via pty '", $op->{PTY_ID}, "'"
) if _debugging_details;
}
else {
croak(
"'"
. ref( $dest )
. "' not allowed as a sink for output redirection"
);
}
$op->{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno;
$op->{TFD} = undef; # The fd isn't known until after fork().
$output_fds_accum[$op->{KFD}] = $op;
$op->_init_filters;
}
elsif ( $op->{TYPE} eq '|' ) {
_debug(
"pipelining $kid->{NUM} and "
. ( $kid->{NUM} + 1 )
) if _debugging_details;
( $pipe_read_fd, $op->{TFD} ) = _pipe;
if ( Win32_MODE ) {
_dont_inherit( $pipe_read_fd );
_dont_inherit( $op->{TFD} );
}
@output_fds_accum = ();
}
elsif ( $op->{TYPE} eq '&' ) {
@output_fds_accum = ();
} # end if $op->{TYPE} tree
1;
}; # end eval
unless ( $ok ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
} # end for ( OPS }
}
if ( @errs ) {
for ( @close_on_fail ) {
_close( $_ );
$_ = undef;
}
for ( keys %{$self->{PTYS}} ) {
next unless $self->{PTYS}->{$_};
close $self->{PTYS}->{$_};
$self->{PTYS}->{$_} = undef;
}
die join( '', @errs )
}
## give all but the last child all of the output file descriptors
## These will be reopened (and thus rendered useless) if the child
## dup2s on to these descriptors, since we unshift these. This way
## each process emits output to the same file descriptors that the
## last child will write to. This is probably not quite correct,
## since each child should write to the file descriptors inherited
## from the parent.
## TODO: fix the inheritance of output file descriptors.
## NOTE: This sharing of OPS among kids means that we can't easily put
## a kid number in each OPS structure to ping the kid when all ops
## have closed (when $self->{PIPES} has emptied). This means that we
## need to scan the KIDS whenever @{$self->{PIPES}} is empty to see
## if there any of them are still alive.
for ( my $num = 0; $num < $#{$self->{KIDS}}; ++$num ) {
for ( reverse @output_fds_accum ) {
next unless defined $_;
_debug(
'kid ', $self->{KIDS}->[$num]->{NUM}, ' also to write ', $_->{KFD},
' to ', ref $_->{DEST}
) if _debugging_details;
unshift @{$self->{KIDS}->[$num]->{OPS}}, $_;
}
}
## Open the debug pipe if we need it
## Create the list of PIPES we need to scan and the bit vectors needed by
## select(). Do this first so that _cleanup can _clobber() them if an
## exception occurs.
@{$self->{PIPES}} = ();
$self->{RIN} = '';
$self->{WIN} = '';
$self->{EIN} = '';
## PIN is a vec()tor that indicates who's paused.
$self->{PIN} = '';
for my $kid ( @{$self->{KIDS}} ) {
for ( @{$kid->{OPS}} ) {
if ( defined $_->{FD} ) {
_debug(
'kid ', $kid->{NUM}, '[', $kid->{PID}, "]'s ", $_->{KFD},
' is my ', $_->{FD}
) if _debugging_details;
vec( $self->{ $_->{TYPE} =~ /^ ? 'WIN' : 'RIN' }, $_->{FD}, 1 ) = 1;
# vec( $self->{EIN}, $_->{FD}, 1 ) = 1;
push @{$self->{PIPES}}, $_;
}
}
}
for my $io ( @{$self->{IOS}} ) {
my $fd = $io->fileno;
vec( $self->{RIN}, $fd, 1 ) = 1 if $io->mode =~ /r/;
vec( $self->{WIN}, $fd, 1 ) = 1 if $io->mode =~ /w/;
# vec( $self->{EIN}, $fd, 1 ) = 1;
push @{$self->{PIPES}}, $io;
}
## Put filters on the end of the filter chains to read & write the pipes.
## Clear pipe states
for my $pipe ( @{$self->{PIPES}} ) {
$pipe->{SOURCE_EMPTY} = 0;
$pipe->{PAUSED} = 0;
if ( $pipe->{TYPE} =~ /^>/ ) {
my $pipe_reader = sub {
my ( undef, $out_ref ) = @_;
return undef unless defined $pipe->{FD};
return 0 unless vec( $self->{ROUT}, $pipe->{FD}, 1 );
vec( $self->{ROUT}, $pipe->{FD}, 1 ) = 0;
_debug_desc_fd( 'reading from', $pipe ) if _debugging_details;
my $in = eval { _read( $pipe->{FD} ) };
if ( $@ ) {
$in = '';
## IO::Pty throws the Input/output error if the kid dies.
## read() throws the bad file descriptor message if the
## kid dies on Win32.
die $@ unless
$@ =~ /^Input\/output error: read/ ||
($@ =~ /input or output/ && $^O =~ /aix/)
|| ( Win32_MODE && $@ =~ /Bad file descriptor/ );
}
unless ( length $in ) {
$self->_clobber( $pipe );
return undef;
}
## Protect the position so /.../g matches may be used.
my $pos = pos $$out_ref;
$$out_ref .= $in;
pos( $$out_ref ) = $pos;
return 1;
};
## Input filters are the last filters
push @{$pipe->{FILTERS}}, $pipe_reader;
push @{$self->{TEMP_FILTERS}}, $pipe_reader;
}
else {
my $pipe_writer = sub {
my ( $in_ref, $out_ref ) = @_;
return undef unless defined $pipe->{FD};
return 0
unless vec( $self->{WOUT}, $pipe->{FD}, 1 )
|| $pipe->{PAUSED};
vec( $self->{WOUT}, $pipe->{FD}, 1 ) = 0;
if ( ! length $$in_ref ) {
if ( ! defined get_more_input ) {
$self->_clobber( $pipe );
return undef;
}
}
unless ( length $$in_ref ) {
unless ( $pipe->{PAUSED} ) {
_debug_desc_fd( 'pausing', $pipe ) if _debugging_details;
vec( $self->{WIN}, $pipe->{FD}, 1 ) = 0;
# vec( $self->{EIN}, $pipe->{FD}, 1 ) = 0;
vec( $self->{PIN}, $pipe->{FD}, 1 ) = 1;
$pipe->{PAUSED} = 1;
}
return 0;
}
_debug_desc_fd( 'writing to', $pipe ) if _debugging_details;
my $c = _write( $pipe->{FD}, $$in_ref );
substr( $$in_ref, 0, $c, '' );
return 1;
};
## Output filters are the first filters
unshift @{$pipe->{FILTERS}}, $pipe_writer;
push @{$self->{TEMP_FILTERS}}, $pipe_writer;
}
}
}
sub _dup2_gently {
## A METHOD, NOT A FUNCTION, NEEDS $self!
my IPC::Run $self = shift;
my ( $files, $fd1, $fd2 ) = @_;
## Moves TFDs that are using the destination fd out of the
## way before calling _dup2
for ( @$files ) {
next unless defined $_->{TFD};
$_->{TFD} = _dup( $_->{TFD} ) if $_->{TFD} == $fd2;
}
$self->{DEBUG_FD} = _dup $self->{DEBUG_FD}
if defined $self->{DEBUG_FD} && $self->{DEBUG_FD} == $fd2;
_dup2_rudely( $fd1, $fd2 );
}
=pod
=item close_terminal
This is used as (or in) an init sub to cast off the bonds of a controlling
terminal. It must precede all other redirection ops that affect
STDIN, STDOUT, or STDERR to be guaranteed effective.
=cut
sub close_terminal {
## Cast of the bonds of a controlling terminal
POSIX::setsid() || croak "POSIX::setsid() failed";
_debug "closing stdin, out, err"
if _debugging_details;
close STDIN;
close STDERR;
close STDOUT;
}
sub _do_kid_and_exit {
my IPC::Run $self = shift;
my ( $kid ) = @_;
## For unknown reasons, placing these two statements in the eval{}
## causes the eval {} to not catch errors after they are executed in
## perl 5.6.0, godforsaken version that it is...not sure about 5.6.1.
## Part of this could be that these symbols get destructed when
## exiting the eval, and that destruction might be what's (wrongly)
## confusing the eval{}, allowing the exception to probpogate.
my $s1 = gensym;
my $s2 = gensym;
eval {
local $cur_self = $self;
_set_child_debug_name( ref $kid->{VAL} eq "CODE"
? "CODE"
: basename( $kid->{VAL}->[0] )
);
## close parent FD's first so they're out of the way.
## Don't close STDIN, STDOUT, STDERR: they should be inherited or
## overwritten below.
my @needed = $self->{noinherit} ? () : ( 1, 1, 1 );
$needed[ $self->{SYNC_WRITER_FD} ] = 1;
$needed[ $self->{DEBUG_FD} ] = 1 if defined $self->{DEBUG_FD};
for ( @{$kid->{OPS}} ) {
$needed[ $_->{TFD} ] = 1 if defined $_->{TFD};
}
## TODO: use the forthcoming IO::Pty to close the terminal and
## make the first pty for this child the controlling terminal.
## This will also make it so that pty-laden kids don't cause
## other kids to lose stdin/stdout/stderr.
my @closed;
if ( %{$self->{PTYS}} ) {
## Clean up the parent's fds.
for ( keys %{$self->{PTYS}} ) {
_debug "Cleaning up parent's ptty '$_'" if _debugging_details;
my $slave = $self->{PTYS}->{$_}->slave;
$closed[ $self->{PTYS}->{$_}->fileno ] = 1;
close $self->{PTYS}->{$_};
$self->{PTYS}->{$_} = $slave;
}
close_terminal;
$closed[ $_ ] = 1 for ( 0..2 );
}
for my $sibling ( @{$self->{KIDS}} ) {
for ( @{$sibling->{OPS}} ) {
if ( $_->{TYPE} =~ /^.pty.$/ ) {
$_->{TFD} = $self->{PTYS}->{$_->{PTY_ID}}->fileno;
$needed[$_->{TFD}] = 1;
}
# for ( $_->{FD}, ( $sibling != $kid ? $_->{TFD} : () ) ) {
# if ( defined $_ && ! $closed[$_] && ! $needed[$_] ) {
# _close( $_ );
# $closed[$_] = 1;
# $_ = undef;
# }
# }
}
}
## This is crude: we have no way of keeping track of browsing all open
## fds, so we scan to a fairly high fd.
_debug "open fds: ", join " ", keys %fds if _debugging_details;
for (keys %fds) {
if ( ! $closed[$_] && ! $needed[$_] ) {
_close( $_ );
$closed[$_] = 1;
}
}
## Lazy closing is so the same fd (ie the same TFD value) can be dup2'ed on
## several times.
my @lazy_close;
for ( @{$kid->{OPS}} ) {
if ( defined $_->{TFD} ) {
unless ( $_->{TFD} == $_->{KFD} ) {
$self->_dup2_gently( $kid->{OPS}, $_->{TFD}, $_->{KFD} );
push @lazy_close, $_->{TFD};
}
}
elsif ( $_->{TYPE} eq 'dup' ) {
$self->_dup2_gently( $kid->{OPS}, $_->{KFD1}, $_->{KFD2} )
unless $_->{KFD1} == $_->{KFD2};
}
elsif ( $_->{TYPE} eq 'close' ) {
for ( $_->{KFD} ) {
if ( ! $closed[$_] ) {
_close( $_ );
$closed[$_] = 1;
$_ = undef;
}
}
}
elsif ( $_->{TYPE} eq 'init' ) {
$_->{SUB}->();
}
}
for ( @lazy_close ) {
unless ( $closed[$_] ) {
_close( $_ );
$closed[$_] = 1;
}
}
if ( ref $kid->{VAL} ne 'CODE' ) {
open $s1, ">&=$self->{SYNC_WRITER_FD}"
or croak "$! setting filehandle to fd SYNC_WRITER_FD";
fcntl $s1, F_SETFD, 1;
if ( defined $self->{DEBUG_FD} ) {
open $s2, ">&=$self->{DEBUG_FD}"
or croak "$! setting filehandle to fd DEBUG_FD";
fcntl $s2, F_SETFD, 1;
}
my @cmd = ( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] );
_debug 'execing ', join " ", map { /[\s\"]/ ? "'$_'" : $_ } @cmd
if _debugging;
die "exec failed: simulating exec() failure"
if $self->{_simulate_exec_failure};
_exec $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}];
croak "exec failed: $!";
}
};
if ( $@ ) {
_write $self->{SYNC_WRITER_FD}, $@;
## Avoid DESTROY.
POSIX::exit 1;
}
## We must be executing code in the child, otherwise exec() would have
## prevented us from being here.
_close $self->{SYNC_WRITER_FD};
_debug 'calling fork()ed CODE ref' if _debugging;
POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD};
## TODO: Overload CORE::GLOBAL::exit...
$kid->{VAL}->();
## There are bugs in perl closures up to and including 5.6.1
## that may keep this next line from having any effect, and it
## won't have any effect if our caller has kept a copy of it, but
## this may cause the closure to be cleaned up. Maybe.
$kid->{VAL} = undef;
## Use POSIX::exit to avoid global destruction, since this might
## cause DESTROY() to be called on objects created in the parent
## and thus cause double cleanup. For instance, if DESTROY() unlinks
## a file in the child, we don't want the parent to suddenly miss
## it.
POSIX::exit 0;
}
=pod
=item start
$h = start(
\@cmd, \$in, \$out, ...,
timeout( 30, name => "process timeout" ),
$stall_timeout = timeout( 10, name => "stall timeout" ),
);
$h = start \@cmd, '<', \$in, '|', \@cmd2, ...;
start() accepts a harness or harness specification and returns a harness
after building all of the pipes and launching (via fork()/exec(), or, maybe
someday, spawn()) all the child processes. It does not send or receive any
data on the pipes, see pump() and finish() for that.
You may call harness() and then pass it's result to start() if you like,
but you only need to if it helps you structure or tune your application.
If you do call harness(), you may skip start() and proceed directly to
pump.
start() also starts all timers in the harness. See L
for more information.
start() flushes STDOUT and STDERR to help you avoid duplicate output.
It has no way of asking Perl to flush all your open filehandles, so
you are going to need to flush any others you have open. Sorry.
Here's how if you don't want to alter the state of $| for your
filehandle:
$ofh = select HANDLE; $of = $|; $| = 1; $| = $of; select $ofh;
If you don't mind leaving output unbuffered on HANDLE, you can do
the slightly shorter
$ofh = select HANDLE; $| = 1; select $ofh;
Or, you can use IO::Handle's flush() method:
use IO::Handle;
flush HANDLE;
Perl needs the equivalent of C's fflush( (FILE *)NULL ).
=cut
sub start {
# $SIG{__DIE__} = sub { my $s = shift; Carp::cluck $s; die $s };
my $options;
if ( @_ && ref $_[-1] eq 'HASH' ) {
$options = pop;
require Data::Dumper;
carp "Passing in options as a hash is deprecated:\n", Data::Dumper::Dumper( $options );
}
my IPC::Run $self;
if ( @_ == 1 && UNIVERSAL::isa( $_[0], __PACKAGE__ ) ) {
$self = shift;
$self->{$_} = $options->{$_} for keys %$options;
}
else {
$self = harness( @_, $options ? $options : () );
}
local $cur_self = $self;
$self->kill_kill if $self->{STATE} == _started;
_debug "** starting" if _debugging;
$_->{RESULT} = undef for @{$self->{KIDS}};
## Assume we're not being called from &run. It will correct our
## assumption if need be. This affects whether &_select_loop clears
## input queues to '' when they're empty.
$self->{clear_ins} = 1;
IPC::Run::Win32Helper::optimize $self
if Win32_MODE && $in_run;
my @errs;
for ( @{$self->{TIMERS}} ) {
eval { $_->start };
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
}
eval { $self->_open_pipes };
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
if ( ! @errs ) {
## This is a bit of a hack, we should do it for all open filehandles.
## Since there's no way I know of to enumerate open filehandles, we
## autoflush STDOUT and STDERR. This is done so that the children don't
## inherit output buffers chock full o' redundant data. It's really
## confusing to track that down.
{ my $ofh = select STDOUT; local $| = 1; select $ofh; }
{ my $ofh = select STDERR; local $| = 1; select $ofh; }
for my $kid ( @{$self->{KIDS}} ) {
$kid->{RESULT} = undef;
_debug "child: ",
ref( $kid->{VAL} ) eq "CODE"
? "CODE ref"
: (
"`",
join( " ", map /[^\w.-]/ ? "'$_'" : $_, @{$kid->{VAL}} ),
"`"
) if _debugging_details;
eval {
croak "simulated failure of fork"
if $self->{_simulate_fork_failure};
unless ( Win32_MODE ) {
$self->_spawn( $kid );
}
else {
## TODO: Test and debug spawing code. Someday.
_debug(
'spawning ',
join(
' ',
map(
"'$_'",
( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] )
)
)
) if _debugging;
## The external kid wouldn't know what to do with it anyway.
## This is only used by the "helper" pump processes on Win32.
_dont_inherit( $self->{DEBUG_FD} );
( $kid->{PID}, $kid->{PROCESS} ) =
IPC::Run::Win32Helper::win32_spawn(
[ $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ],
$kid->{OPS},
);
_debug "spawn() = ", $kid->{PID} if _debugging;
}
};
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
}
}
## Close all those temporary filehandles that the kids needed.
for my $pty ( values %{$self->{PTYS}} ) {
close $pty->slave;
}
my @closed;
for my $kid ( @{$self->{KIDS}} ) {
for ( @{$kid->{OPS}} ) {
my $close_it = eval {
defined $_->{TFD}
&& ! $_->{DONT_CLOSE}
&& ! $closed[$_->{TFD}]
&& ( ! Win32_MODE || ! $_->{RECV_THROUGH_TEMP_FILE} ) ## Win32 hack
};
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
if ( $close_it || $@ ) {
eval {
_close( $_->{TFD} );
$closed[$_->{TFD}] = 1;
$_->{TFD} = undef;
};
if ( $@ ) {
push @errs, $@;
_debug 'caught ', $@ if _debugging;
}
}
}
}
confess "gak!" unless defined $self->{PIPES};
if ( @errs ) {
eval { $self->_cleanup };
warn $@ if $@;
die join( '', @errs );
}
$self->{STATE} = _started;
return $self;
}
sub adopt {
## NOT FUNCTIONAL YET, NEED TO CLOSE FDS BETTER IN CHILDREN. SEE
## t/adopt.t for a test suite.
my IPC::Run $self = shift;
for my $adoptee ( @_ ) {
push @{$self->{IOS}}, @{$adoptee->{IOS}};
## NEED TO RENUMBER THE KIDS!!
push @{$self->{KIDS}}, @{$adoptee->{KIDS}};
push @{$self->{PIPES}}, @{$adoptee->{PIPES}};
$self->{PTYS}->{$_} = $adoptee->{PTYS}->{$_}
for keys %{$adoptee->{PYTS}};
push @{$self->{TIMERS}}, @{$adoptee->{TIMERS}};
$adoptee->{STATE} = _finished;
}
}
sub _clobber {
my IPC::Run $self = shift;
my ( $file ) = @_;
_debug_desc_fd( "closing", $file ) if _debugging_details;
my $doomed = $file->{FD};
my $dir = $file->{TYPE} =~ /^ ? 'WIN' : 'RIN';
vec( $self->{$dir}, $doomed, 1 ) = 0;
# vec( $self->{EIN}, $doomed, 1 ) = 0;
vec( $self->{PIN}, $doomed, 1 ) = 0;
if ( $file->{TYPE} =~ /^(.)pty.$/ ) {
if ( $1 eq '>' ) {
## Only close output ptys. This is so that ptys as inputs are
## never autoclosed, which would risk losing data that was
## in the slave->parent queue.
_debug_desc_fd "closing pty", $file if _debugging_details;
close $self->{PTYS}->{$file->{PTY_ID}}
if defined $self->{PTYS}->{$file->{PTY_ID}};
$self->{PTYS}->{$file->{PTY_ID}} = undef;
}
}
elsif ( UNIVERSAL::isa( $file, 'IPC::Run::IO' ) ) {
$file->close unless $file->{DONT_CLOSE};
}
else {
_close( $doomed );
}
@{$self->{PIPES}} = grep
defined $_->{FD} && ( $_->{TYPE} ne $file->{TYPE} || $_->{FD} ne $doomed),
@{$self->{PIPES}};
$file->{FD} = undef;
}
sub _select_loop {
my IPC::Run $self = shift;
my $io_occurred;
my $not_forever = 0.01;
SELECT:
while ( $self->pumpable ) {
if ( $io_occurred && $self->{break_on_io} ) {
_debug "exiting _select(): io occured and break_on_io set"
if _debugging_details;
last;
}
my $timeout = $self->{non_blocking} ? 0 : undef;
if ( @{$self->{TIMERS}} ) {
my $now = time;
my $time_left;
for ( @{$self->{TIMERS}} ) {
next unless $_->is_running;
$time_left = $_->check( $now );
## Return when a timer expires
return if defined $time_left && ! $time_left;
$timeout = $time_left
if ! defined $timeout || $time_left < $timeout;
}
}
##
## See if we can unpause any input channels
##
my $paused = 0;
for my $file ( @{$self->{PIPES}} ) {
next unless $file->{PAUSED} && $file->{TYPE} =~ /^;
_debug_desc_fd( "checking for more input", $file ) if _debugging_details;
my $did;
1 while $did = $file->_do_filters( $self );
if ( defined $file->{FD} && ! defined( $did ) || $did ) {
_debug_desc_fd( "unpausing", $file ) if _debugging_details;
$file->{PAUSED} = 0;
vec( $self->{WIN}, $file->{FD}, 1 ) = 1;
# vec( $self->{EIN}, $file->{FD}, 1 ) = 1;
vec( $self->{PIN}, $file->{FD}, 1 ) = 0;
}
else {
## This gets incremented occasionally when the IO channel
## was actually closed. That's a bug, but it seems mostly
## harmless: it causes us to exit if break_on_io, or to set
## the timeout to not be forever. I need to fix it, though.
++$paused;
}
}
if ( _debugging_details ) {
my $map = join(
'',
map {
my $out;
$out = 'r' if vec( $self->{RIN}, $_, 1 );
$out = $out ? 'b' : 'w' if vec( $self->{WIN}, $_, 1 );
$out = 'p' if ! $out && vec( $self->{PIN}, $_, 1 );
$out = $out ? uc( $out ) : 'x' if vec( $self->{EIN}, $_, 1 );
$out = '-' unless $out;
$out;
} (0..1024)
);
$map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/;
_debug 'fds for select: ', $map if _debugging_details;
}
## _do_filters may have closed our last fd, and we need to see if
## we have I/O, or are just waiting for children to exit.
my $p = $self->pumpable;
last unless $p;
if ( $p > 0 && ( ! defined $timeout || $timeout > 0.1 ) ) {
## No I/O will wake the select loop up, but we have children
## lingering, so we need to poll them with a short timeout.
## Otherwise, assume more input will be coming.
$timeout = $not_forever;
$not_forever *= 2;
$not_forever = 0.5 if $not_forever >= 0.5;
}
## Make sure we don't block forever in select() because inputs are
## paused.
if ( ! defined $timeout && ! ( @{$self->{PIPES}} - $paused ) ) {
## Need to return if we're in pump and all input is paused, or
## we'll loop until all inputs are unpaused, which is darn near
## forever. And a day.
if ( $self->{break_on_io} ) {
_debug "exiting _select(): no I/O to do and timeout=forever"
if _debugging;
last;
}
## Otherwise, assume more input will be coming.
$timeout = $not_forever;
$not_forever *= 2;
$not_forever = 0.5 if $not_forever >= 0.5;
}
_debug 'timeout=', defined $timeout ? $timeout : 'forever'
if _debugging_details;
my $nfound;
unless ( Win32_MODE ) {
$nfound = select(
$self->{ROUT} = $self->{RIN},
$self->{WOUT} = $self->{WIN},
$self->{EOUT} = $self->{EIN},
$timeout
);
}
else {
my @in = map $self->{$_}, qw( RIN WIN EIN );
## Win32's select() on Win32 seems to die if passed vectors of
## all 0's. Need to report this when I get back online.
for ( @in ) {
$_ = undef unless index( ( unpack "b*", $_ ), 1 ) >= 0;
}
$nfound = select(
$self->{ROUT} = $in[0],
$self->{WOUT} = $in[1],
$self->{EOUT} = $in[2],
$timeout
);
for ( $self->{ROUT}, $self->{WOUT}, $self->{EOUT} ) {
$_ = "" unless defined $_;
}
}
last if ! $nfound && $self->{non_blocking};
croak "$! in select" if $nfound < 0 and $! != POSIX::EINTR;
## TODO: Analyze the EINTR failure mode and see if this patch
## is adequate and optimal.
## TODO: Add an EINTR test to the test suite.
if ( _debugging_details ) {
my $map = join(
'',
map {
my $out;
$out = 'r' if vec( $self->{ROUT}, $_, 1 );
$out = $out ? 'b' : 'w' if vec( $self->{WOUT}, $_, 1 );
$out = $out ? uc( $out ) : 'x' if vec( $self->{EOUT}, $_, 1 );
$out = '-' unless $out;
$out;
} (0..128)
);
$map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/;
_debug "selected ", $map;
}
## Need to copy since _clobber alters @{$self->{PIPES}}.
## TODO: Rethink _clobber(). Rethink $file->{PAUSED}, too.
my @pipes = @{$self->{PIPES}};
$io_occurred = $_->poll( $self ) ? 1 : $io_occurred for @pipes;
# FILE:
# for my $pipe ( @pipes ) {
# ## Pipes can be shared among kids. If another kid closes the
# ## pipe, then it's {FD} will be undef. Also, on Win32, pipes can
# ## be optimized to be files, in which case the FD is left undef
# ## so we don't try to select() on it.
# if ( $pipe->{TYPE} =~ /^>/
# && defined $pipe->{FD}
# && vec( $self->{ROUT}, $pipe->{FD}, 1 )
# ) {
# _debug_desc_fd( "filtering data from", $pipe ) if _debugging_details;
#confess "phooey" unless UNIVERSAL::isa( $pipe, "IPC::Run::IO" );
# $io_occurred = 1 if $pipe->_do_filters( $self );
#
# next FILE unless defined $pipe->{FD};
# }
#
# ## On Win32, pipes to the child can be optimized to be files
# ## and FD left undefined so we won't select on it.
# if ( $pipe->{TYPE} =~ /^
# && defined $pipe->{FD}
# && vec( $self->{WOUT}, $pipe->{FD}, 1 )
# ) {
# _debug_desc_fd( "filtering data to", $pipe ) if _debugging_details;
# $io_occurred = 1 if $pipe->_do_filters( $self );
#
# next FILE unless defined $pipe->{FD};
# }
#
# if ( defined $pipe->{FD} && vec( $self->{EOUT}, $pipe->{FD}, 1 ) ) {
# ## BSD seems to sometimes raise the exceptional condition flag
# ## when a pipe is closed before we read it's last data. This
# ## causes spurious warnings and generally renders the exception
# ## mechanism useless for our purposes. The exception
# ## flag semantics are too variable (they're device driver
# ## specific) for me to easily map to any automatic action like
# ## warning or croaking (try running v0.42 if you don't beleive me
# ## :-).
# warn "Exception on descriptor $pipe->{FD}";
# }
# }
}
return;
}
sub _cleanup {
my IPC::Run $self = shift;
_debug "cleaning up" if _debugging_details;
for ( values %{$self->{PTYS}} ) {
next unless ref $_;
eval {
_debug "closing slave fd ", fileno $_->slave if _debugging_data;
close $_->slave;
};
carp $@ . " while closing ptys" if $@;
eval {
_debug "closing master fd ", fileno $_ if _debugging_data;
close $_;
};
carp $@ . " closing ptys" if $@;
}
_debug "cleaning up pipes" if _debugging_details;
## _clobber modifies PIPES
$self->_clobber( $self->{PIPES}->[0] ) while @{$self->{PIPES}};
for my $kid ( @{$self->{KIDS}} ) {
_debug "cleaning up kid ", $kid->{NUM} if _debugging_details;
if ( ! length $kid->{PID} ) {
_debug 'never ran child ', $kid->{NUM}, ", can't reap"
if _debugging;
for my $op ( @{$kid->{OPS}} ) {
_close( $op->{TFD} )
if defined $op->{TFD} && ! defined $op->{TEMP_FILE_HANDLE};
}
}
elsif ( ! defined $kid->{RESULT} ) {
_debug 'reaping child ', $kid->{NUM}, ' (pid ', $kid->{PID}, ')'
if _debugging;
my $pid = waitpid $kid->{PID}, 0;
$kid->{RESULT} = $?;
_debug 'reaped ', $pid, ', $?=', $kid->{RESULT}
if _debugging;
}
# if ( defined $kid->{DEBUG_FD} ) {
# die;
# @{$kid->{OPS}} = grep
# ! defined $_->{KFD} || $_->{KFD} != $kid->{DEBUG_FD},
# @{$kid->{OPS}};
# $kid->{DEBUG_FD} = undef;
# }
_debug "cleaning up filters" if _debugging_details;
for my $op ( @{$kid->{OPS}} ) {
@{$op->{FILTERS}} = grep {
my $filter = $_;
! grep $filter == $_, @{$self->{TEMP_FILTERS}};
} @{$op->{FILTERS}};
}
for my $op ( @{$kid->{OPS}} ) {
$op->_cleanup( $self ) if UNIVERSAL::isa( $op, "IPC::Run::IO" );
}
}
$self->{STATE} = _finished;
@{$self->{TEMP_FILTERS}} = ();
_debug "done cleaning up" if _debugging_details;
POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD};
$self->{DEBUG_FD} = undef;
}
=pod
=item pump
pump $h;
$h->pump;
Pump accepts a single parameter harness. It blocks until it delivers some
input or recieves some output. It returns TRUE if there is still input or
output to be done, FALSE otherwise.
pump() will automatically call start() if need be, so you may call harness()
then proceed to pump() if that helps you structure your application.
If pump() is called after all harnessed activities have completed, a "process
ended prematurely" exception to be thrown. This allows for simple scripting
of external applications without having to add lots of error handling code at
each step of the script:
$h = harness \@smbclient, \$in, \$out, $err;
$in = "cd /foo\n";
$h->pump until $out =~ /^smb.*> \Z/m;
die "error cding to /foo:\n$out" if $out =~ "ERR";
$out = '';
$in = "mget *\n";
$h->pump until $out =~ /^smb.*> \Z/m;
die "error retrieving files:\n$out" if $out =~ "ERR";
$h->finish;
warn $err if $err;
=cut
sub pump {
die "pump() takes only a a single harness as a parameter"
unless @_ == 1 && UNIVERSAL::isa( $_[0], __PACKAGE__ );
my IPC::Run $self = shift;
local $cur_self = $self;
_debug "** pumping"
if _debugging;
# my $r = eval {
$self->start if $self->{STATE} < _started;
croak "process ended prematurely" unless $self->pumpable;
$self->{auto_close_ins} = 0;
$self->{break_on_io} = 1;
$self->_select_loop;
return $self->pumpable;
# };
# if ( $@ ) {
# my $x = $@;
# _debug $x if _debugging && $x;
# eval { $self->_cleanup };
# warn $@ if $@;
# die $x;
# }
# return $r;
}
=pod
=item pump_nb
pump_nb $h;
$h->pump_nb;
"pump() non-blocking", pumps if anything's ready to be pumped, returns
immediately otherwise. This is useful if you're doing some long-running
task in the foreground, but don't want to starve any child processes.
=cut
sub pump_nb {
my IPC::Run $self = shift;
$self->{non_blocking} = 1;
my $r = eval { $self->pump };
$self->{non_blocking} = 0;
die $@ if $@;
return $r;
}
=pod
=item pumpable
Returns TRUE if calling pump() won't throw an immediate "process ended
prematurely" exception. This means that there are open I/O channels or
active processes. May yield the parent processes' time slice for 0.01
second if all pipes are to the child and all are paused. In this case
we can't tell if the child is dead, so we yield the processor and
then attempt to reap the child in a nonblocking way.
=cut
## Undocumented feature (don't depend on it outside this module):
## returns -1 if we have I/O channels open, or >0 if no I/O channels
## open, but we have kids running. This allows the select loop
## to poll for child exit.
sub pumpable {
my IPC::Run $self = shift;
## There's a catch-22 we can get in to if there is only one pipe left
## open to the child and it's paused (ie the SCALAR it's tied to
## is ''). It's paused, so we're not select()ing on it, so we don't
## check it to see if the child attached to it is alive and it stays
## in @{$self->{PIPES}} forever. So, if all pipes are paused, see if
## we can reap the child.
return -1 if grep !$_->{PAUSED}, @{$self->{PIPES}};
## See if the child is dead.
$self->reap_nb;
return 0 unless $self->_running_kids;
## If we reap_nb and it's not dead yet, yield to it to see if it
## exits.
##
## A better solution would be to unpause all the pipes, but I tried that
## and it never errored on linux. Sigh.
select undef, undef, undef, 0.0001;
## try again
$self->reap_nb;
return 0 unless $self->_running_kids;
return -1; ## There are pipes waiting
}
sub _running_kids {
my IPC::Run $self = shift;
return grep
defined $_->{PID} && ! defined $_->{RESULT},
@{$self->{KIDS}};
}
=pod
=item reap_nb
Attempts to reap child processes, but does not block.
Does not currently take any parameters, one day it will allow specific
children to be reaped.
Only call this from a signal handler if your C is recent enough
to have safe signal handling (5.6.1 did not, IIRC, but it was beign discussed
on perl5-porters). Calling this (or doing any significant work) in a signal
handler on older Cs is asking for seg faults.
=cut
my $still_runnings;
sub reap_nb {
my IPC::Run $self = shift;
local $cur_self = $self;
## No more pipes, look to see if all the kids yet live, reaping those
## that haven't. I'd use $SIG{CHLD}/$SIG{CLD}, but that's broken
## on older (SYSV) platforms and perhaps less portable than waitpid().
## This could be slow with a lot of kids, but that's rare and, well,
## a lot of kids is slow in the first place.
## Oh, and this keeps us from reaping other children the process
## may have spawned.
for my $kid ( @{$self->{KIDS}} ) {
if ( Win32_MODE ) {
next if ! defined $kid->{PROCESS} || defined $kid->{RESULT};
unless ( $kid->{PROCESS}->Wait( 0 ) ) {
_debug "kid $kid->{NUM} ($kid->{PID}) still running"
if _debugging_details;
next;
}
_debug "kid $kid->{NUM} ($kid->{PID}) exited"
if _debugging;
$kid->{PROCESS}->GetExitCode( $kid->{RESULT} )
or croak "$! while GetExitCode()ing for Win32 process";
unless ( defined $kid->{RESULT} ) {
$kid->{RESULT} = "0 but true";
$? = $kid->{RESULT} = 0x0F;
}
else {
$? = $kid->{RESULT} << 8;
}
}
else {
next if ! defined $kid->{PID} || defined $kid->{RESULT};
my $pid = waitpid $kid->{PID}, POSIX::WNOHANG();
unless ( $pid ) {
_debug "$kid->{NUM} ($kid->{PID}) still running"
if _debugging_details;
next;
}
if ( $pid < 0 ) {
_debug "No such process: $kid->{PID}\n" if _debugging;
$kid->{RESULT} = "unknown result, unknown PID";
}
else {
_debug "kid $kid->{NUM} ($kid->{PID}) exited"
if _debugging;
confess "waitpid returned the wrong PID: $pid instead of $kid->{PID}"
unless $pid = $kid->{PID};
_debug "$kid->{PID} returned $?\n" if _debugging;
$kid->{RESULT} = $?;
}
}
}
}
=pod
=item finish
This must be called after the last start() or pump() call for a harness,
or your system will accumulate defunct processes and you may "leak"
file descriptors.
finish() returns TRUE if all children returned 0 (and were not signaled and did
not coredump, ie ! $?), and FALSE otherwise (this is like run(), and the
opposite of system()).
Once a harness has been finished, it may be run() or start()ed again,
including by pump()s auto-start.
If this throws an exception rather than a normal exit, the harness may
be left in an unstable state, it's best to kill the harness to get rid
of all the child processes, etc.
Specifically, if a timeout expires in finish(), finish() will not
kill all the children. Call C<<$h->kill_kill>> in this case if you care.
This differs from the behavior of L