Alien-ROOT
view release on metacpan or search on metacpan
inc/inc_IPC-Cmd/IPC/Cmd.pm view on Meta::CPAN
for my $key ( @acc ) {
no strict 'refs';
*{__PACKAGE__."::$key"} = sub {
$_[0]->{$key} = $_[1] if @_ > 1;
return $_[0]->{$key};
}
}
}
sub can_use_run_forked {
return $CAN_USE_RUN_FORKED eq "1";
}
# incompatible with POSIX::SigAction
#
sub install_layered_signal {
my ($s, $handler_code) = @_;
my %available_signals = map {$_ => 1} keys %SIG;
die("install_layered_signal got nonexistent signal name [$s]")
unless defined($available_signals{$s});
die("install_layered_signal expects coderef")
if !ref($handler_code) || ref($handler_code) ne 'CODE';
my $previous_handler = $SIG{$s};
my $sig_handler = sub {
my ($called_sig_name, @sig_param) = @_;
# $s is a closure referring to real signal name
# for which this handler is being installed.
# it is used to distinguish between
# real signal handlers and aliased signal handlers
my $signal_name = $s;
# $called_sig_name is a signal name which
# was passed to this signal handler;
# it doesn't equal $signal_name in case
# some signal handlers in %SIG point
# to other signal handler (CHLD and CLD,
# ABRT and IOT)
#
# initial signal handler for aliased signal
# calls some other signal handler which
# should not execute the same handler_code again
if ($called_sig_name eq $signal_name) {
$handler_code->($signal_name);
}
# run original signal handler if any (including aliased)
#
if (ref($previous_handler)) {
$previous_handler->($called_sig_name, @sig_param);
}
};
$SIG{$s} = $sig_handler;
}
# give process a chance sending TERM,
# waiting for a while (2 seconds)
# and killing it with KILL
sub kill_gently {
my ($pid, $opts) = @_;
require POSIX;
$opts = {} unless $opts;
$opts->{'wait_time'} = 2 unless defined($opts->{'wait_time'});
$opts->{'first_kill_type'} = 'just_process' unless $opts->{'first_kill_type'};
$opts->{'final_kill_type'} = 'just_process' unless $opts->{'final_kill_type'};
if ($opts->{'first_kill_type'} eq 'just_process') {
kill(15, $pid);
}
elsif ($opts->{'first_kill_type'} eq 'process_group') {
kill(-15, $pid);
}
my $child_finished = 0;
my $wait_start_time = time();
while (!$child_finished && $wait_start_time + $opts->{'wait_time'} > time()) {
my $waitpid = waitpid($pid, POSIX::WNOHANG);
if ($waitpid eq -1) {
$child_finished = 1;
}
Time::HiRes::usleep(250000); # quarter of a second
}
if (!$child_finished) {
if ($opts->{'final_kill_type'} eq 'just_process') {
kill(9, $pid);
}
elsif ($opts->{'final_kill_type'} eq 'process_group') {
kill(-9, $pid);
}
}
}
sub open3_run {
my ($cmd, $opts) = @_;
$opts = {} unless $opts;
my $child_in = FileHandle->new;
my $child_out = FileHandle->new;
my $child_err = FileHandle->new;
$child_out->autoflush(1);
$child_err->autoflush(1);
my $pid = open3($child_in, $child_out, $child_err, $cmd);
# push my child's pid to our parent
# so in case i am killed parent
# could stop my child (search for
# child_child_pid in parent code)
if ($opts->{'parent_info'}) {
my $ps = $opts->{'parent_info'};
print $ps "spawned $pid\n";
}
if ($child_in && $child_out->opened && $opts->{'child_stdin'}) {
# If the child process dies for any reason,
# the next write to CHLD_IN is likely to generate
# a SIGPIPE in the parent, which is fatal by default.
# So you may wish to handle this signal.
#
# from http://perldoc.perl.org/IPC/Open3.html,
# absolutely needed to catch piped commands errors.
#
local $SIG{'PIPE'} = sub { 1; };
print $child_in $opts->{'child_stdin'};
}
close($child_in);
my $child_output = {
'out' => $child_out->fileno,
'err' => $child_err->fileno,
$child_out->fileno => {
'parent_socket' => $opts->{'parent_stdout'},
'scalar_buffer' => "",
'child_handle' => $child_out,
'block_size' => ($child_out->stat)[11] || 1024,
},
$child_err->fileno => {
'parent_socket' => $opts->{'parent_stderr'},
'scalar_buffer' => "",
'child_handle' => $child_err,
'block_size' => ($child_err->stat)[11] || 1024,
},
};
my $select = IO::Select->new();
$select->add($child_out, $child_err);
# pass any signal to the child
# effectively creating process
# strongly attached to the child:
# it will terminate only after child
# has terminated (except for SIGKILL,
# which is specially handled)
foreach my $s (keys %SIG) {
my $sig_handler;
$sig_handler = sub {
kill("$s", $pid);
$SIG{$s} = $sig_handler;
};
$SIG{$s} = $sig_handler;
}
my $child_finished = 0;
my $got_sig_child = 0;
$SIG{'CHLD'} = sub { $got_sig_child = time(); };
while(!$child_finished && ($child_out->opened || $child_err->opened)) {
# parent was killed otherwise we would have got
# the same signal as parent and process it same way
if (getppid() eq "1") {
# end my process group with all the children
# (i am the process group leader, so my pid
# equals to the process group id)
#
# same thing which is done
# with $opts->{'clean_up_children'}
# in run_forked
#
kill(-9, $$);
POSIX::_exit 1;
}
if ($got_sig_child) {
if (time() - $got_sig_child > 1) {
# select->can_read doesn't return 0 after SIG_CHLD
#
# "On POSIX-compliant platforms, SIGCHLD is the signal
# sent to a process when a child process terminates."
# http://en.wikipedia.org/wiki/SIGCHLD
#
# nevertheless kill KILL wouldn't break anything here
#
kill (9, $pid);
$child_finished = 1;
}
}
Time::HiRes::usleep(1);
foreach my $fd ($select->can_read(1/100)) {
my $str = $child_output->{$fd->fileno};
psSnake::die("child stream not found: $fd") unless $str;
my $data;
my $count = $fd->sysread($data, $str->{'block_size'});
if ($count) {
if ($str->{'parent_socket'}) {
my $ph = $str->{'parent_socket'};
print $ph $data;
}
else {
$str->{'scalar_buffer'} .= $data;
}
}
elsif ($count eq 0) {
$select->remove($fd);
$fd->close();
}
else {
psSnake::die("error during sysread: " . $!);
}
}
}
my $waitpid_ret = waitpid($pid, 0);
my $real_exit = $?;
my $exit_value = $real_exit >> 8;
# since we've successfully reaped the child,
# let our parent know about this.
#
if ($opts->{'parent_info'}) {
my $ps = $opts->{'parent_info'};
# child was killed, inform parent
if ($real_exit & 127) {
print $ps "$pid killed with " . ($real_exit & 127) . "\n";
}
print $ps "reaped $pid\n";
}
if ($opts->{'parent_stdout'} || $opts->{'parent_stderr'}) {
return $exit_value;
}
else {
return {
'stdout' => $child_output->{$child_output->{'out'}}->{'scalar_buffer'},
'stderr' => $child_output->{$child_output->{'err'}}->{'scalar_buffer'},
'exit_code' => $exit_value,
};
}
}
=head2 $hashref = run_forked( COMMAND, { child_stdin => SCALAR, timeout => DIGIT, stdout_handler => CODEREF, stderr_handler => CODEREF} );
C<run_forked> is used to execute some program or a coderef,
optionally feed it with some input, get its return code
and output (both stdout and stderr into separate buffers).
In addition, it allows to terminate the program
if it takes too long to finish.
The important and distinguishing feature of run_forked
is execution timeout which at first seems to be
quite a simple task but if you think
that the program which you're spawning
might spawn some children itself (which
in their turn could do the same and so on)
it turns out to be not a simple issue.
C<run_forked> is designed to survive and
successfully terminate almost any long running task,
even a fork bomb in case your system has the resources
to survive during given timeout.
This is achieved by creating separate watchdog process
which spawns the specified program in a separate
process session and supervises it: optionally
feeds it with input, stores its exit code,
stdout and stderr, terminates it in case
it runs longer than specified.
Invocation requires the command to be executed or a coderef and optionally a hashref of options:
=over
=item C<timeout>
Specify in seconds how long to run the command before it is killed with with SIG_KILL (9),
which effectively terminates it and all of its children (direct or indirect).
=item C<child_stdin>
Specify some text that will be passed into the C<STDIN> of the executed program.
=item C<stdout_handler>
Coderef of a subroutine to call when a portion of data is received on
STDOUT from the executing program.
=item C<stderr_handler>
Coderef of a subroutine to call when a portion of data is received on
STDERR from the executing program.
=item C<discard_output>
Discards the buffering of the standard output and standard errors for return by run_forked().
With this option you have to use the std*_handlers to read what the command outputs.
Useful for commands that send a lot of output.
=item C<terminate_on_parent_sudden_death>
Enable this option if you wish all spawned processes to be killed if the initially spawned
process (the parent) is killed or dies without waiting for child processes.
=back
C<run_forked> will return a HASHREF with the following keys:
=over
=item C<exit_code>
The exit code of the executed program.
=item C<timeout>
The number of seconds the program ran for before being terminated, or 0 if no timeout occurred.
=item C<stdout>
Holds the standard output of the executed command (or empty string if
there was no STDOUT output or if C<discard_output> was used; it's always defined!)
=item C<stderr>
Holds the standard error of the executed command (or empty string if
there was no STDERR output or if C<discard_output> was used; it's always defined!)
=item C<merged>
Holds the standard output and error of the executed command merged into one stream
(or empty string if there was no output at all or if C<discard_output> was used; it's always defined!)
=item C<err_msg>
Holds some explanation in the case of an error.
=back
=cut
sub run_forked {
### container to store things in
my $self = bless {}, __PACKAGE__;
require POSIX;
if (!can_use_run_forked()) {
Carp::carp("run_forked is not available: $CAN_USE_RUN_FORKED");
return;
}
my ($cmd, $opts) = @_;
if (!$cmd) {
Carp::carp("run_forked expects command to run");
return;
}
$opts = {} unless $opts;
$opts->{'timeout'} = 0 unless $opts->{'timeout'};
$opts->{'terminate_wait_time'} = 2 unless defined($opts->{'terminate_wait_time'});
inc/inc_IPC-Cmd/IPC/Cmd.pm view on Meta::CPAN
if ($pid = fork) {
# we are a parent
close($parent_stdout_socket);
close($parent_stderr_socket);
close($parent_info_socket);
my $flags;
# prepare sockets to read from child
$flags = 0;
fcntl($child_stdout_socket, POSIX::F_GETFL, $flags) || die "can't fnctl F_GETFL: $!";
$flags |= POSIX::O_NONBLOCK;
fcntl($child_stdout_socket, POSIX::F_SETFL, $flags) || die "can't fnctl F_SETFL: $!";
$flags = 0;
fcntl($child_stderr_socket, POSIX::F_GETFL, $flags) || die "can't fnctl F_GETFL: $!";
$flags |= POSIX::O_NONBLOCK;
fcntl($child_stderr_socket, POSIX::F_SETFL, $flags) || die "can't fnctl F_SETFL: $!";
$flags = 0;
fcntl($child_info_socket, POSIX::F_GETFL, $flags) || die "can't fnctl F_GETFL: $!";
$flags |= POSIX::O_NONBLOCK;
fcntl($child_info_socket, POSIX::F_SETFL, $flags) || die "can't fnctl F_SETFL: $!";
# print "child $pid started\n";
my $child_timedout = 0;
my $child_finished = 0;
my $child_stdout = '';
my $child_stderr = '';
my $child_merged = '';
my $child_exit_code = 0;
my $child_killed_by_signal = 0;
my $parent_died = 0;
my $got_sig_child = 0;
my $got_sig_quit = 0;
my $orig_sig_child = $SIG{'CHLD'};
$SIG{'CHLD'} = sub { $got_sig_child = time(); };
if ($opts->{'terminate_on_signal'}) {
install_layered_signal($opts->{'terminate_on_signal'}, sub { $got_sig_quit = time(); });
}
my $child_child_pid;
while (!$child_finished) {
my $now = time();
if ($opts->{'terminate_on_parent_sudden_death'}) {
$opts->{'runtime'}->{'last_parent_check'} = 0
unless defined($opts->{'runtime'}->{'last_parent_check'});
# check for parent once each five seconds
if ($now - $opts->{'runtime'}->{'last_parent_check'} > 5) {
if (getppid() eq "1") {
kill_gently ($pid, {
'first_kill_type' => 'process_group',
'final_kill_type' => 'process_group',
'wait_time' => $opts->{'terminate_wait_time'}
});
$parent_died = 1;
}
$opts->{'runtime'}->{'last_parent_check'} = $now;
}
}
# user specified timeout
if ($opts->{'timeout'}) {
if ($now - $start_time > $opts->{'timeout'}) {
kill_gently ($pid, {
'first_kill_type' => 'process_group',
'final_kill_type' => 'process_group',
'wait_time' => $opts->{'terminate_wait_time'}
});
$child_timedout = 1;
}
}
# give OS 10 seconds for correct return of waitpid,
# kill process after that and finish wait loop;
# shouldn't ever happen -- remove this code?
if ($got_sig_child) {
if ($now - $got_sig_child > 10) {
print STDERR "waitpid did not return -1 for 10 seconds after SIG_CHLD, killing [$pid]\n";
kill (-9, $pid);
$child_finished = 1;
}
}
if ($got_sig_quit) {
kill_gently ($pid, {
'first_kill_type' => 'process_group',
'final_kill_type' => 'process_group',
'wait_time' => $opts->{'terminate_wait_time'}
});
$child_finished = 1;
}
my $waitpid = waitpid($pid, POSIX::WNOHANG);
# child finished, catch it's exit status
if ($waitpid ne 0 && $waitpid ne -1) {
$child_exit_code = $? >> 8;
}
if ($waitpid eq -1) {
$child_finished = 1;
next;
}
# child -> parent simple internal communication protocol
while (my $l = <$child_info_socket>) {
if ($l =~ /^spawned ([0-9]+?)\n(.*?)/so) {
$child_child_pid = $1;
$l = $2;
}
if ($l =~ /^reaped ([0-9]+?)\n(.*?)/so) {
$child_child_pid = undef;
$l = $2;
}
if ($l =~ /^[\d]+ killed with ([0-9]+?)\n(.*?)/so) {
$child_killed_by_signal = $1;
$l = $2;
}
}
while (my $l = <$child_stdout_socket>) {
if (!$opts->{'discard_output'}) {
$child_stdout .= $l;
$child_merged .= $l;
}
if ($opts->{'stdout_handler'} && ref($opts->{'stdout_handler'}) eq 'CODE') {
$opts->{'stdout_handler'}->($l);
}
}
while (my $l = <$child_stderr_socket>) {
if (!$opts->{'discard_output'}) {
$child_stderr .= $l;
$child_merged .= $l;
}
if ($opts->{'stderr_handler'} && ref($opts->{'stderr_handler'}) eq 'CODE') {
$opts->{'stderr_handler'}->($l);
}
}
Time::HiRes::usleep(1);
}
# $child_pid_pid is not defined in two cases:
# * when our child was killed before
# it had chance to tell us the pid
# of the child it spawned. we can do
# nothing in this case :(
# * our child successfully reaped its child,
# we have nothing left to do in this case
#
# defined $child_pid_pid means child's child
# has not died but nobody is waiting for it,
# killing it brutally.
#
if ($child_child_pid) {
kill_gently($child_child_pid);
}
# in case there are forks in child which
# do not forward or process signals (TERM) correctly
# kill whole child process group, effectively trying
# not to return with some children or their parts still running
#
# to be more accurate -- we need to be sure
# that this is process group created by our child
# (and not some other process group with the same pgid,
# created just after death of our child) -- fortunately
# this might happen only when process group ids
# are reused quickly (there are lots of processes
# spawning new process groups for example)
#
if ($opts->{'clean_up_children'}) {
kill(-9, $pid);
}
# print "child $pid finished\n";
close($child_stdout_socket);
close($child_stderr_socket);
close($child_info_socket);
my $o = {
'stdout' => $child_stdout,
'stderr' => $child_stderr,
'merged' => $child_merged,
'timeout' => $child_timedout ? $opts->{'timeout'} : 0,
'exit_code' => $child_exit_code,
'parent_died' => $parent_died,
'killed_by_signal' => $child_killed_by_signal,
'child_pgid' => $pid,
};
my $err_msg = '';
if ($o->{'exit_code'}) {
$err_msg .= "exited with code [$o->{'exit_code'}]\n";
}
if ($o->{'timeout'}) {
$err_msg .= "ran more than [$o->{'timeout'}] seconds\n";
}
if ($o->{'parent_died'}) {
$err_msg .= "parent died\n";
}
if ($o->{'stdout'}) {
$err_msg .= "stdout:\n" . $o->{'stdout'} . "\n";
}
if ($o->{'stderr'}) {
$err_msg .= "stderr:\n" . $o->{'stderr'} . "\n";
}
if ($o->{'killed_by_signal'}) {
$err_msg .= "killed by signal [" . $o->{'killed_by_signal'} . "]\n";
}
$o->{'err_msg'} = $err_msg;
if ($orig_sig_child) {
$SIG{'CHLD'} = $orig_sig_child;
}
else {
delete($SIG{'CHLD'});
}
return $o;
}
else {
die("cannot fork: $!") unless defined($pid);
# create new process session for open3 call,
# so we hopefully can kill all the subprocesses
# which might be spawned in it (except for those
# which do setsid theirselves -- can't do anything
# with those)
POSIX::setsid() || die("Error running setsid: " . $!);
if ($opts->{'child_BEGIN'} && ref($opts->{'child_BEGIN'}) eq 'CODE') {
$opts->{'child_BEGIN'}->();
}
close($child_stdout_socket);
close($child_stderr_socket);
close($child_info_socket);
my $child_exit_code;
# allow both external programs
# and internal perl calls
if (!ref($cmd)) {
$child_exit_code = open3_run($cmd, {
'parent_info' => $parent_info_socket,
'parent_stdout' => $parent_stdout_socket,
'parent_stderr' => $parent_stderr_socket,
'child_stdin' => $opts->{'child_stdin'},
});
}
elsif (ref($cmd) eq 'CODE') {
$child_exit_code = $cmd->({
'opts' => $opts,
'parent_info' => $parent_info_socket,
'parent_stdout' => $parent_stdout_socket,
'parent_stderr' => $parent_stderr_socket,
'child_stdin' => $opts->{'child_stdin'},
});
}
else {
print $parent_stderr_socket "Invalid command reference: " . ref($cmd) . "\n";
$child_exit_code = 1;
}
close($parent_stdout_socket);
close($parent_stderr_socket);
close($parent_info_socket);
if ($opts->{'child_END'} && ref($opts->{'child_END'}) eq 'CODE') {
$opts->{'child_END'}->();
}
POSIX::_exit $child_exit_code;
}
}
sub run {
### container to store things in
my $self = bless {}, __PACKAGE__;
my %hash = @_;
### if the user didn't provide a buffer, we'll store it here.
my $def_buf = '';
inc/inc_IPC-Cmd/IPC/Cmd.pm view on Meta::CPAN
push @buffer, $buf;
push @buff_err, $buf;
};
### flag to indicate we have a buffer captured
my $have_buffer = $self->can_capture_buffer ? 1 : 0;
### flag indicating if the subcall went ok
my $ok;
### dont look at previous errors:
local $?;
local $@;
local $!;
### we might be having a timeout set
eval {
local $SIG{ALRM} = sub { die bless sub {
ALARM_CLASS .
qq[: Command '$pp_cmd' aborted by alarm after $timeout seconds]
}, ALARM_CLASS } if $timeout;
alarm $timeout || 0;
### IPC::Run is first choice if $USE_IPC_RUN is set.
if( !IS_WIN32 and $USE_IPC_RUN and $self->can_use_ipc_run( 1 ) ) {
### ipc::run handlers needs the command as a string or an array ref
$self->_debug( "# Using IPC::Run. Have buffer: $have_buffer" )
if $DEBUG;
$ok = $self->_ipc_run( $cmd, $_out_handler, $_err_handler );
### since IPC::Open3 works on all platforms, and just fails on
### win32 for capturing buffers, do that ideally
} elsif ( $USE_IPC_OPEN3 and $self->can_use_ipc_open3( 1 ) ) {
$self->_debug("# Using IPC::Open3. Have buffer: $have_buffer")
if $DEBUG;
### in case there are pipes in there;
### IPC::Open3 will call exec and exec will do the right thing
my $method = IS_WIN32 ? '_open3_run_win32' : '_open3_run';
$ok = $self->$method(
$cmd, $_out_handler, $_err_handler, $verbose
);
### if we are allowed to run verbose, just dispatch the system command
} else {
$self->_debug( "# Using system(). Have buffer: $have_buffer" )
if $DEBUG;
$ok = $self->_system_run( $cmd, $verbose );
}
alarm 0;
};
### restore STDIN after duping, or STDIN will be closed for
### this current perl process!
$self->__reopen_fds( @{ $self->_fds} ) if $self->_fds;
my $err;
unless( $ok ) {
### alarm happened
if ( $@ and ref $@ and $@->isa( ALARM_CLASS ) ) {
$err = $@->(); # the error code is an expired alarm
### another error happened, set by the dispatchub
} else {
$err = $self->error;
}
}
### fill the buffer;
$$buffer = join '', @buffer if @buffer;
### return a list of flags and buffers (if available) in list
### context, or just a simple 'ok' in scalar
return wantarray
? $have_buffer
? ($ok, $err, \@buffer, \@buff_out, \@buff_err)
: ($ok, $err )
: $ok
}
sub _open3_run_win32 {
my $self = shift;
my $cmd = shift;
my $outhand = shift;
my $errhand = shift;
my $pipe = sub {
socketpair($_[0], $_[1], AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or return undef;
shutdown($_[0], 1); # No more writing for reader
shutdown($_[1], 0); # No more reading for writer
return 1;
};
my $open3 = sub {
local (*TO_CHLD_R, *TO_CHLD_W);
local (*FR_CHLD_R, *FR_CHLD_W);
local (*FR_CHLD_ERR_R, *FR_CHLD_ERR_W);
$pipe->(*TO_CHLD_R, *TO_CHLD_W ) or die $^E;
$pipe->(*FR_CHLD_R, *FR_CHLD_W ) or die $^E;
$pipe->(*FR_CHLD_ERR_R, *FR_CHLD_ERR_W) or die $^E;
my $pid = IPC::Open3::open3('>&TO_CHLD_R', '<&FR_CHLD_W', '<&FR_CHLD_ERR_W', @_);
return ( $pid, *TO_CHLD_W, *FR_CHLD_R, *FR_CHLD_ERR_R );
};
$cmd = [ grep { defined && length } @$cmd ] if ref $cmd;
$cmd = $self->__fix_cmd_whitespace_and_special_chars( $cmd );
my ($pid, $to_chld, $fr_chld, $fr_chld_err) =
inc/inc_IPC-Cmd/IPC/Cmd.pm view on Meta::CPAN
my %objs;
$objs{ fileno( $fr_chld ) } = $outhand;
$objs{ fileno( $fr_chld_err ) } = $errhand;
$in_sel->add( $fr_chld );
$in_sel->add( $fr_chld_err );
close($to_chld);
while ($in_sel->count() + $out_sel->count()) {
my ($ins, $outs) = IO::Select::select($in_sel, $out_sel, undef);
for my $fh (@$ins) {
my $obj = $objs{ fileno($fh) };
my $buf;
my $bytes_read = sysread($fh, $buf, 64*1024 ); #, length($buf));
if (!$bytes_read) {
$in_sel->remove($fh);
}
else {
$obj->( "$buf" );
}
}
for my $fh (@$outs) {
}
}
waitpid($pid, 0);
### some error occurred
if( $? ) {
$self->error( $self->_pp_child_error( $cmd, $? ) );
$self->ok( 0 );
return;
} else {
return $self->ok( 1 );
}
}
sub _open3_run {
my $self = shift;
my $cmd = shift;
my $_out_handler = shift;
my $_err_handler = shift;
my $verbose = shift || 0;
### Following code are adapted from Friar 'abstracts' in the
### Perl Monastery (http://www.perlmonks.org/index.pl?node_id=151886).
### XXX that code didn't work.
### we now use the following code, thanks to theorbtwo
### define them beforehand, so we always have defined FH's
### to read from.
use Symbol;
my $kidout = Symbol::gensym();
my $kiderror = Symbol::gensym();
### Dup the filehandle so we can pass 'our' STDIN to the
### child process. This stops us from having to pump input
### from ourselves to the childprocess. However, we will need
### to revive the FH afterwards, as IPC::Open3 closes it.
### We'll do the same for STDOUT and STDERR. It works without
### duping them on non-unix derivatives, but not on win32.
my @fds_to_dup = ( IS_WIN32 && !$verbose
? qw[STDIN STDOUT STDERR]
: qw[STDIN]
);
$self->_fds( \@fds_to_dup );
$self->__dup_fds( @fds_to_dup );
### pipes have to come in a quoted string, and that clashes with
### whitespace. This sub fixes up such commands so they run properly
$cmd = $self->__fix_cmd_whitespace_and_special_chars( $cmd );
### dont stringify @$cmd, so spaces in filenames/paths are
### treated properly
my $pid = eval {
IPC::Open3::open3(
'<&STDIN',
(IS_WIN32 ? '>&STDOUT' : $kidout),
(IS_WIN32 ? '>&STDERR' : $kiderror),
( ref $cmd ? @$cmd : $cmd ),
);
};
### open3 error occurred
if( $@ and $@ =~ /^open3:/ ) {
$self->ok( 0 );
$self->error( $@ );
return;
};
### use OUR stdin, not $kidin. Somehow,
### we never get the input.. so jump through
### some hoops to do it :(
my $selector = IO::Select->new(
(IS_WIN32 ? \*STDERR : $kiderror),
\*STDIN,
(IS_WIN32 ? \*STDOUT : $kidout)
);
STDOUT->autoflush(1); STDERR->autoflush(1); STDIN->autoflush(1);
$kidout->autoflush(1) if UNIVERSAL::can($kidout, 'autoflush');
$kiderror->autoflush(1) if UNIVERSAL::can($kiderror, 'autoflush');
### add an explicit break statement
### code courtesy of theorbtwo from #london.pm
my $stdout_done = 0;
my $stderr_done = 0;
OUTER: while ( my @ready = $selector->can_read ) {
for my $h ( @ready ) {
my $buf;
### $len is the amount of bytes read
my $len = sysread( $h, $buf, 4096 ); # try to read 4096 bytes
### see perldoc -f sysread: it returns undef on error,
### so bail out.
if( not defined $len ) {
warn(loc("Error reading from process: %1", $!));
last OUTER;
}
### check for $len. it may be 0, at which point we're
### done reading, so don't try to process it.
### if we would print anyway, we'd provide bogus information
$_out_handler->( "$buf" ) if $len && $h == $kidout;
$_err_handler->( "$buf" ) if $len && $h == $kiderror;
### Wait till child process is done printing to both
### stdout and stderr.
$stdout_done = 1 if $h == $kidout and $len == 0;
$stderr_done = 1 if $h == $kiderror and $len == 0;
last OUTER if ($stdout_done && $stderr_done);
}
}
waitpid $pid, 0; # wait for it to die
### restore STDIN after duping, or STDIN will be closed for
### this current perl process!
### done in the parent call now
# $self->__reopen_fds( @fds_to_dup );
### some error occurred
if( $? ) {
$self->error( $self->_pp_child_error( $cmd, $? ) );
$self->ok( 0 );
return;
} else {
return $self->ok( 1 );
}
}
### Text::ParseWords::shellwords() uses unix semantics. that will break
### on win32
{ my $parse_sub = IS_WIN32
? __PACKAGE__->can('_split_like_shell_win32')
: Text::ParseWords->can('shellwords');
sub _ipc_run {
my $self = shift;
my $cmd = shift;
my $_out_handler = shift;
my $_err_handler = shift;
STDOUT->autoflush(1); STDERR->autoflush(1);
### a command like:
# [
# '/usr/bin/gzip',
# '-cdf',
# '/Users/kane/sources/p4/other/archive-extract/t/src/x.tgz',
# '|',
# '/usr/bin/tar',
# '-tf -'
# ]
### needs to become:
# [
# ['/usr/bin/gzip', '-cdf',
# '/Users/kane/sources/p4/other/archive-extract/t/src/x.tgz']
# '|',
# ['/usr/bin/tar', '-tf -']
# ]
my @command;
my $special_chars;
my $re = do { my $x = join '', SPECIAL_CHARS; qr/([$x])/ };
if( ref $cmd ) {
my $aref = [];
for my $item (@$cmd) {
if( $item =~ $re ) {
push @command, $aref, $item;
$aref = [];
$special_chars .= $1;
} else {
push @$aref, $item;
}
}
inc/inc_IPC-Cmd/IPC/Cmd.pm view on Meta::CPAN
sub _system_run {
my $self = shift;
my $cmd = shift;
my $verbose = shift || 0;
### pipes have to come in a quoted string, and that clashes with
### whitespace. This sub fixes up such commands so they run properly
$cmd = $self->__fix_cmd_whitespace_and_special_chars( $cmd );
my @fds_to_dup = $verbose ? () : qw[STDOUT STDERR];
$self->_fds( \@fds_to_dup );
$self->__dup_fds( @fds_to_dup );
### system returns 'true' on failure -- the exit code of the cmd
$self->ok( 1 );
system( ref $cmd ? @$cmd : $cmd ) == 0 or do {
$self->error( $self->_pp_child_error( $cmd, $? ) );
$self->ok( 0 );
};
### done in the parent call now
#$self->__reopen_fds( @fds_to_dup );
return unless $self->ok;
return $self->ok;
}
{ my %sc_lookup = map { $_ => $_ } SPECIAL_CHARS;
sub __fix_cmd_whitespace_and_special_chars {
my $self = shift;
my $cmd = shift;
### command has a special char in it
if( ref $cmd and grep { $sc_lookup{$_} } @$cmd ) {
### since we have special chars, we have to quote white space
### this *may* conflict with the parsing :(
my $fixed;
my @cmd = map { / / ? do { $fixed++; QUOTE.$_.QUOTE } : $_ } @$cmd;
$self->_debug( "# Quoted $fixed arguments containing whitespace" )
if $DEBUG && $fixed;
### stringify it, so the special char isn't escaped as argument
### to the program
$cmd = join ' ', @cmd;
}
return $cmd;
}
}
### Command-line arguments (but not the command itself) must be quoted
### to ensure case preservation. Borrowed from Module::Build with adaptations.
### Patch for this supplied by Craig Berry, see RT #46288: [PATCH] Add argument
### quoting for run() on VMS
sub _quote_args_vms {
### Returns a command string with proper quoting so that the subprocess
### sees this same list of args, or if we get a single arg that is an
### array reference, quote the elements of it (except for the first)
### and return the reference.
my @args = @_;
my $got_arrayref = (scalar(@args) == 1
&& UNIVERSAL::isa($args[0], 'ARRAY'))
? 1
: 0;
@args = split(/\s+/, $args[0]) unless $got_arrayref || scalar(@args) > 1;
my $cmd = $got_arrayref ? shift @{$args[0]} : shift @args;
### Do not quote qualifiers that begin with '/' or previously quoted args.
map { if (/^[^\/\"]/) {
$_ =~ s/\"/""/g; # escape C<"> by doubling
$_ = q(").$_.q(");
}
}
($got_arrayref ? @{$args[0]}
: @args
);
$got_arrayref ? unshift(@{$args[0]}, $cmd) : unshift(@args, $cmd);
return $got_arrayref ? $args[0]
: join(' ', @args);
}
### XXX this is cribbed STRAIGHT from M::B 0.30 here:
### http://search.cpan.org/src/KWILLIAMS/Module-Build-0.30/lib/Module/Build/Platform/Windows.pm:split_like_shell
### XXX this *should* be integrated into text::parsewords
sub _split_like_shell_win32 {
# As it turns out, Windows command-parsing is very different from
# Unix command-parsing. Double-quotes mean different things,
# backslashes don't necessarily mean escapes, and so on. So we
# can't use Text::ParseWords::shellwords() to break a command string
# into words. The algorithm below was bashed out by Randy and Ken
# (mostly Randy), and there are a lot of regression tests, so we
# should feel free to adjust if desired.
local $_ = shift;
my @argv;
return @argv unless defined() && length();
my $arg = '';
my( $i, $quote_mode ) = ( 0, 0 );
while ( $i < length() ) {
my $ch = substr( $_, $i , 1 );
my $next_ch = substr( $_, $i+1, 1 );
if ( $ch eq '\\' && $next_ch eq '"' ) {
$arg .= '"';
$i++;
} elsif ( $ch eq '\\' && $next_ch eq '\\' ) {
$arg .= '\\';
( run in 0.473 second using v1.01-cache-2.11-cpan-97f6503c9c8 )