Initial Commit
This commit is contained in:
310
database/perl/vendor/lib/IPC/Run/Debug.pm
vendored
Normal file
310
database/perl/vendor/lib/IPC/Run/Debug.pm
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
package IPC::Run::Debug;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::Debug - debugging routines for IPC::Run
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
##
|
||||
## Environment variable usage
|
||||
##
|
||||
## To force debugging off and shave a bit of CPU and memory
|
||||
## by compile-time optimizing away all debugging code in IPC::Run
|
||||
## (debug => ...) options to IPC::Run will be ignored.
|
||||
export IPCRUNDEBUG=none
|
||||
|
||||
## To force debugging on (levels are from 0..10)
|
||||
export IPCRUNDEBUG=basic
|
||||
|
||||
## Leave unset or set to "" to compile in debugging support and
|
||||
## allow runtime control of it using the debug option.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Controls IPC::Run debugging. Debugging levels are now set by using words,
|
||||
but the numbers shown are still supported for backwards compatibility:
|
||||
|
||||
0 none disabled (special, see below)
|
||||
1 basic what's running
|
||||
2 data what's being sent/received
|
||||
3 details what's going on in more detail
|
||||
4 gory way too much detail for most uses
|
||||
10 all use this when submitting bug reports
|
||||
noopts optimizations forbidden due to inherited STDIN
|
||||
|
||||
The C<none> level is special when the environment variable IPCRUNDEBUG
|
||||
is set to this the first time IPC::Run::Debug is loaded: it prevents
|
||||
the debugging code from being compiled in to the remaining IPC::Run modules,
|
||||
saving a bit of cpu.
|
||||
|
||||
To do this in a script, here's a way that allows it to be overridden:
|
||||
|
||||
BEGIN {
|
||||
unless ( defined $ENV{IPCRUNDEBUG} ) {
|
||||
eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
|
||||
or die $@;
|
||||
}
|
||||
}
|
||||
|
||||
This should force IPC::Run to not be debuggable unless somebody sets
|
||||
the IPCRUNDEBUG flag; modify this formula to grep @ARGV if need be:
|
||||
|
||||
BEGIN {
|
||||
unless ( grep /^--debug/, @ARGV ) {
|
||||
eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
|
||||
or die $@;
|
||||
}
|
||||
|
||||
Both of those are untested.
|
||||
|
||||
=cut
|
||||
|
||||
## We use @EXPORT for the end user's convenience: there's only one function
|
||||
## exported, it's homonymous with the module, it's an unusual name, and
|
||||
## it can be suppressed by "use IPC::Run ();".
|
||||
|
||||
use strict;
|
||||
use Exporter;
|
||||
use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS};
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
@ISA = qw( Exporter );
|
||||
@EXPORT = qw(
|
||||
_debug
|
||||
_debug_desc_fd
|
||||
_debugging
|
||||
_debugging_data
|
||||
_debugging_details
|
||||
_debugging_gory_details
|
||||
_debugging_not_optimized
|
||||
_set_child_debug_name
|
||||
);
|
||||
|
||||
@EXPORT_OK = qw(
|
||||
_debug_init
|
||||
_debugging_level
|
||||
_map_fds
|
||||
);
|
||||
%EXPORT_TAGS = (
|
||||
default => \@EXPORT,
|
||||
all => [ @EXPORT, @EXPORT_OK ],
|
||||
);
|
||||
}
|
||||
|
||||
my $disable_debugging = defined $ENV{IPCRUNDEBUG}
|
||||
&& ( !$ENV{IPCRUNDEBUG}
|
||||
|| lc $ENV{IPCRUNDEBUG} eq "none" );
|
||||
|
||||
eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@;
|
||||
sub _map_fds() { "" }
|
||||
sub _debug {}
|
||||
sub _debug_desc_fd {}
|
||||
sub _debug_init {}
|
||||
sub _set_child_debug_name {}
|
||||
sub _debugging() { 0 }
|
||||
sub _debugging_level() { 0 }
|
||||
sub _debugging_data() { 0 }
|
||||
sub _debugging_details() { 0 }
|
||||
sub _debugging_gory_details() { 0 }
|
||||
sub _debugging_not_optimized() { 0 }
|
||||
|
||||
1;
|
||||
STUBS
|
||||
|
||||
use POSIX ();
|
||||
|
||||
sub _map_fds {
|
||||
my $map = '';
|
||||
my $digit = 0;
|
||||
my $in_use;
|
||||
my $dummy;
|
||||
for my $fd (0..63) {
|
||||
## I'd like a quicker way (less user, cpu & especially sys and kernel
|
||||
## calls) to detect open file descriptors. Let me know...
|
||||
## Hmmm, could do a 0 length read and check for bad file descriptor...
|
||||
## but that segfaults on Win32
|
||||
my $test_fd = POSIX::dup( $fd );
|
||||
$in_use = defined $test_fd;
|
||||
POSIX::close $test_fd if $in_use;
|
||||
$map .= $in_use ? $digit : '-';
|
||||
$digit = 0 if ++$digit > 9;
|
||||
}
|
||||
warn "No fds open???" unless $map =~ /\d/;
|
||||
$map =~ s/(.{1,12})-*$/$1/;
|
||||
return $map;
|
||||
}
|
||||
|
||||
use vars qw( $parent_pid );
|
||||
|
||||
$parent_pid = $$;
|
||||
|
||||
## TODO: move debugging to its own module and make it compile-time
|
||||
## optimizable.
|
||||
|
||||
## Give kid process debugging nice names
|
||||
my $debug_name;
|
||||
|
||||
sub _set_child_debug_name {
|
||||
$debug_name = shift;
|
||||
}
|
||||
|
||||
## There's a bit of hackery going on here.
|
||||
##
|
||||
## We want to have any code anywhere be able to emit
|
||||
## debugging statements without knowing what harness the code is
|
||||
## being called in/from, since we'd need to pass a harness around to
|
||||
## everything.
|
||||
##
|
||||
## Thus, $cur_self was born.
|
||||
#
|
||||
my %debug_levels = (
|
||||
none => 0,
|
||||
basic => 1,
|
||||
data => 2,
|
||||
details => 3,
|
||||
gore => 4,
|
||||
gory_details => 4,
|
||||
"gory details" => 4,
|
||||
gory => 4,
|
||||
gorydetails => 4,
|
||||
all => 10,
|
||||
notopt => 0,
|
||||
);
|
||||
|
||||
my $warned;
|
||||
|
||||
sub _debugging_level() {
|
||||
my $level = 0;
|
||||
|
||||
$level = $IPC::Run::cur_self->{debug} || 0
|
||||
if $IPC::Run::cur_self
|
||||
&& ( $IPC::Run::cur_self->{debug} || 0 ) >= $level;
|
||||
|
||||
if ( defined $ENV{IPCRUNDEBUG} ) {
|
||||
my $v = $ENV{IPCRUNDEBUG};
|
||||
$v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/;
|
||||
unless ( defined $v ) {
|
||||
$warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n";
|
||||
$v = 1;
|
||||
}
|
||||
$level = $v if $v > $level;
|
||||
}
|
||||
return $level;
|
||||
}
|
||||
|
||||
sub _debugging_atleast($) {
|
||||
my $min_level = shift || 1;
|
||||
|
||||
my $level = _debugging_level;
|
||||
|
||||
return $level >= $min_level ? $level : 0;
|
||||
}
|
||||
|
||||
sub _debugging() { _debugging_atleast 1 }
|
||||
sub _debugging_data() { _debugging_atleast 2 }
|
||||
sub _debugging_details() { _debugging_atleast 3 }
|
||||
sub _debugging_gory_details() { _debugging_atleast 4 }
|
||||
sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" }
|
||||
|
||||
sub _debug_init {
|
||||
## This routine is called only in spawned children to fake out the
|
||||
## debug routines so they'll emit debugging info.
|
||||
$IPC::Run::cur_self = {};
|
||||
( $parent_pid,
|
||||
$^T,
|
||||
$IPC::Run::cur_self->{debug},
|
||||
$IPC::Run::cur_self->{DEBUG_FD},
|
||||
$debug_name
|
||||
) = @_;
|
||||
}
|
||||
|
||||
|
||||
sub _debug {
|
||||
# return unless _debugging || _debugging_not_optimized;
|
||||
|
||||
my $fd = defined &IPC::Run::_debug_fd
|
||||
? IPC::Run::_debug_fd()
|
||||
: fileno STDERR;
|
||||
|
||||
my $s;
|
||||
my $debug_id;
|
||||
$debug_id = join(
|
||||
" ",
|
||||
join(
|
||||
"",
|
||||
defined $IPC::Run::cur_self ? "#$IPC::Run::cur_self->{ID}" : (),
|
||||
"($$)",
|
||||
),
|
||||
defined $debug_name && length $debug_name ? $debug_name : (),
|
||||
);
|
||||
my $prefix = join(
|
||||
"",
|
||||
"IPC::Run",
|
||||
sprintf( " %04d", time - $^T ),
|
||||
( _debugging_details ? ( " ", _map_fds ) : () ),
|
||||
length $debug_id ? ( " [", $debug_id, "]" ) : (),
|
||||
": ",
|
||||
);
|
||||
|
||||
my $msg = join( '', map defined $_ ? $_ : "<undef>", @_ );
|
||||
chomp $msg;
|
||||
$msg =~ s{^}{$prefix}gm;
|
||||
$msg .= "\n";
|
||||
POSIX::write( $fd, $msg, length $msg );
|
||||
}
|
||||
|
||||
|
||||
my @fd_descs = ( 'stdin', 'stdout', 'stderr' );
|
||||
|
||||
sub _debug_desc_fd {
|
||||
return unless _debugging;
|
||||
my $text = shift;
|
||||
my $op = pop;
|
||||
my $kid = $_[0];
|
||||
|
||||
Carp::carp join " ", caller(0), $text, $op if defined $op && UNIVERSAL::isa( $op, "IO::Pty" );
|
||||
|
||||
_debug(
|
||||
$text,
|
||||
' ',
|
||||
( defined $op->{FD}
|
||||
? $op->{FD} < 3
|
||||
? ( $fd_descs[$op->{FD}] )
|
||||
: ( 'fd ', $op->{FD} )
|
||||
: $op->{FD}
|
||||
),
|
||||
( defined $op->{KFD}
|
||||
? (
|
||||
' (kid',
|
||||
( defined $kid ? ( ' ', $kid->{NUM}, ) : () ),
|
||||
"'s ",
|
||||
( $op->{KFD} < 3
|
||||
? $fd_descs[$op->{KFD}]
|
||||
: defined $kid
|
||||
&& defined $kid->{DEBUG_FD}
|
||||
&& $op->{KFD} == $kid->{DEBUG_FD}
|
||||
? ( 'debug (', $op->{KFD}, ')' )
|
||||
: ( 'fd ', $op->{KFD} )
|
||||
),
|
||||
')',
|
||||
)
|
||||
: ()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
SUBS
|
||||
|
||||
=pod
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barrie Slaymaker <barries@slaysys.com>, with numerous suggestions by p5p.
|
||||
|
||||
=cut
|
||||
575
database/perl/vendor/lib/IPC/Run/IO.pm
vendored
Normal file
575
database/perl/vendor/lib/IPC/Run/IO.pm
vendored
Normal file
@@ -0,0 +1,575 @@
|
||||
package IPC::Run::IO;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::IO -- I/O channels for IPC::Run.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<NOT IMPLEMENTED YET ON Win32! Win32 does not allow select() on
|
||||
normal file descriptors; IPC::RUN::IO needs to use IPC::Run::Win32Helper
|
||||
to do this.>
|
||||
|
||||
use IPC::Run qw( io );
|
||||
|
||||
## The sense of '>' and '<' is opposite of perl's open(),
|
||||
## but agrees with IPC::Run.
|
||||
$io = io( "filename", '>', \$recv );
|
||||
$io = io( "filename", 'r', \$recv );
|
||||
|
||||
## Append to $recv:
|
||||
$io = io( "filename", '>>', \$recv );
|
||||
$io = io( "filename", 'ra', \$recv );
|
||||
|
||||
$io = io( "filename", '<', \$send );
|
||||
$io = io( "filename", 'w', \$send );
|
||||
|
||||
$io = io( "filename", '<<', \$send );
|
||||
$io = io( "filename", 'wa', \$send );
|
||||
|
||||
## Handles / IO objects that the caller opens:
|
||||
$io = io( \*HANDLE, '<', \$send );
|
||||
|
||||
$f = IO::Handle->new( ... ); # Any subclass of IO::Handle
|
||||
$io = io( $f, '<', \$send );
|
||||
|
||||
require IPC::Run::IO;
|
||||
$io = IPC::Run::IO->new( ... );
|
||||
|
||||
## Then run(), harness(), or start():
|
||||
run $io, ...;
|
||||
|
||||
## You can, of course, use io() or IPC::Run::IO->new() as an
|
||||
## argument to run(), harness, or start():
|
||||
run io( ... );
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class and module allows filehandles and filenames to be harnessed for
|
||||
I/O when used IPC::Run, independent of anything else IPC::Run is doing
|
||||
(except that errors & exceptions can affect all things that IPC::Run is
|
||||
doing).
|
||||
|
||||
=head1 SUBCLASSING
|
||||
|
||||
INCOMPATIBLE CHANGE: due to the awkwardness introduced in ripping pseudohashes
|
||||
out of Perl, this class I<no longer> uses the fields pragma.
|
||||
|
||||
=cut
|
||||
|
||||
## This class is also used internally by IPC::Run in a very intimate way,
|
||||
## since this is a partial factoring of code from IPC::Run plus some code
|
||||
## needed to do standalone channels. This factoring process will continue
|
||||
## at some point. Don't know how far how fast.
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use Fcntl;
|
||||
use Symbol;
|
||||
|
||||
use IPC::Run::Debug;
|
||||
use IPC::Run qw( Win32_MODE );
|
||||
|
||||
use vars qw{$VERSION};
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
if (Win32_MODE) {
|
||||
eval "use IPC::Run::Win32Helper; require IPC::Run::Win32IO; 1"
|
||||
or ( $@ && die )
|
||||
or die "$!";
|
||||
}
|
||||
}
|
||||
|
||||
sub _empty($);
|
||||
*_empty = \&IPC::Run::_empty;
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over 4
|
||||
|
||||
=item new
|
||||
|
||||
I think it takes >> or << along with some other data.
|
||||
|
||||
TODO: Needs more thorough documentation. Patches welcome.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
$class = ref $class || $class;
|
||||
|
||||
my ( $external, $type, $internal ) = ( shift, shift, pop );
|
||||
|
||||
croak "$class: '$_' is not a valid I/O operator"
|
||||
unless $type =~ /^(?:<<?|>>?)$/;
|
||||
|
||||
my IPC::Run::IO $self = $class->_new_internal( $type, undef, undef, $internal, undef, @_ );
|
||||
|
||||
if ( !ref $external ) {
|
||||
$self->{FILENAME} = $external;
|
||||
}
|
||||
elsif ( ref $external eq 'GLOB' || UNIVERSAL::isa( $external, 'IO::Handle' ) ) {
|
||||
$self->{HANDLE} = $external;
|
||||
$self->{DONT_CLOSE} = 1;
|
||||
}
|
||||
else {
|
||||
croak "$class: cannot accept " . ref($external) . " to do I/O with";
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
## IPC::Run uses this ctor, since it preparses things and needs more
|
||||
## smarts.
|
||||
sub _new_internal {
|
||||
my $class = shift;
|
||||
$class = ref $class || $class;
|
||||
|
||||
$class = "IPC::Run::Win32IO"
|
||||
if Win32_MODE && $class eq "IPC::Run::IO";
|
||||
|
||||
my IPC::Run::IO $self;
|
||||
$self = bless {}, $class;
|
||||
|
||||
my ( $type, $kfd, $pty_id, $internal, $binmode, @filters ) = @_;
|
||||
|
||||
# Older perls (<=5.00503, at least) don't do list assign to
|
||||
# psuedo-hashes well.
|
||||
$self->{TYPE} = $type;
|
||||
$self->{KFD} = $kfd;
|
||||
$self->{PTY_ID} = $pty_id;
|
||||
$self->binmode($binmode);
|
||||
$self->{FILTERS} = [@filters];
|
||||
|
||||
## Add an adapter to the end of the filter chain (which is usually just the
|
||||
## read/writer sub pushed by IPC::Run) to the DEST or SOURCE, if need be.
|
||||
if ( $self->op =~ />/ ) {
|
||||
croak "'$_' missing a destination" if _empty $internal;
|
||||
$self->{DEST} = $internal;
|
||||
if ( UNIVERSAL::isa( $self->{DEST}, 'CODE' ) ) {
|
||||
## Put a filter on the end of the filter chain to pass the
|
||||
## output on to the CODE ref. For SCALAR refs, the last
|
||||
## filter in the chain writes directly to the scalar itself. See
|
||||
## _init_filters(). For CODE refs, however, we need to adapt from
|
||||
## the SCALAR to calling the CODE.
|
||||
unshift(
|
||||
@{ $self->{FILTERS} },
|
||||
sub {
|
||||
my ($in_ref) = @_;
|
||||
|
||||
return IPC::Run::input_avail() && do {
|
||||
$self->{DEST}->($$in_ref);
|
||||
$$in_ref = '';
|
||||
1;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
croak "'$_' missing a source" if _empty $internal;
|
||||
$self->{SOURCE} = $internal;
|
||||
if ( UNIVERSAL::isa( $internal, 'CODE' ) ) {
|
||||
push(
|
||||
@{ $self->{FILTERS} },
|
||||
sub {
|
||||
my ( $in_ref, $out_ref ) = @_;
|
||||
return 0 if length $$out_ref;
|
||||
|
||||
return undef
|
||||
if $self->{SOURCE_EMPTY};
|
||||
|
||||
my $in = $internal->();
|
||||
unless ( defined $in ) {
|
||||
$self->{SOURCE_EMPTY} = 1;
|
||||
return undef;
|
||||
}
|
||||
return 0 unless length $in;
|
||||
$$out_ref = $in;
|
||||
|
||||
return 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
elsif ( UNIVERSAL::isa( $internal, 'SCALAR' ) ) {
|
||||
push(
|
||||
@{ $self->{FILTERS} },
|
||||
sub {
|
||||
my ( $in_ref, $out_ref ) = @_;
|
||||
return 0 if length $$out_ref;
|
||||
|
||||
## pump() clears auto_close_ins, finish() sets it.
|
||||
return $self->{HARNESS}->{auto_close_ins} ? undef : 0
|
||||
if IPC::Run::_empty ${ $self->{SOURCE} }
|
||||
|| $self->{SOURCE_EMPTY};
|
||||
|
||||
$$out_ref = $$internal;
|
||||
eval { $$internal = '' }
|
||||
if $self->{HARNESS}->{clear_ins};
|
||||
|
||||
$self->{SOURCE_EMPTY} = $self->{HARNESS}->{auto_close_ins};
|
||||
|
||||
return 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
=item filename
|
||||
|
||||
Gets/sets the filename. Returns the value after the name change, if
|
||||
any.
|
||||
|
||||
=cut
|
||||
|
||||
sub filename {
|
||||
my IPC::Run::IO $self = shift;
|
||||
$self->{FILENAME} = shift if @_;
|
||||
return $self->{FILENAME};
|
||||
}
|
||||
|
||||
=item init
|
||||
|
||||
Does initialization required before this can be run. This includes open()ing
|
||||
the file, if necessary, and clearing the destination scalar if necessary.
|
||||
|
||||
=cut
|
||||
|
||||
sub init {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
$self->{SOURCE_EMPTY} = 0;
|
||||
${ $self->{DEST} } = ''
|
||||
if $self->mode =~ /r/ && ref $self->{DEST} eq 'SCALAR';
|
||||
|
||||
$self->open if defined $self->filename;
|
||||
$self->{FD} = $self->fileno;
|
||||
|
||||
if ( !$self->{FILTERS} ) {
|
||||
$self->{FBUFS} = undef;
|
||||
}
|
||||
else {
|
||||
@{ $self->{FBUFS} } = map {
|
||||
my $s = "";
|
||||
\$s;
|
||||
} ( @{ $self->{FILTERS} }, '' );
|
||||
|
||||
$self->{FBUFS}->[0] = $self->{DEST}
|
||||
if $self->{DEST} && ref $self->{DEST} eq 'SCALAR';
|
||||
push @{ $self->{FBUFS} }, $self->{SOURCE};
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item open
|
||||
|
||||
If a filename was passed in, opens it. Determines if the handle is open
|
||||
via fileno(). Throws an exception on error.
|
||||
|
||||
=cut
|
||||
|
||||
my %open_flags = (
|
||||
'>' => O_RDONLY,
|
||||
'>>' => O_RDONLY,
|
||||
'<' => O_WRONLY | O_CREAT | O_TRUNC,
|
||||
'<<' => O_WRONLY | O_CREAT | O_APPEND,
|
||||
);
|
||||
|
||||
sub open {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
croak "IPC::Run::IO: Can't open() a file with no name"
|
||||
unless defined $self->{FILENAME};
|
||||
$self->{HANDLE} = gensym unless $self->{HANDLE};
|
||||
|
||||
_debug "opening '", $self->filename, "' mode '", $self->mode, "'"
|
||||
if _debugging_data;
|
||||
sysopen(
|
||||
$self->{HANDLE},
|
||||
$self->filename,
|
||||
$open_flags{ $self->op },
|
||||
) or croak "IPC::Run::IO: $! opening '$self->{FILENAME}', mode '" . $self->mode . "'";
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item open_pipe
|
||||
|
||||
If this is a redirection IO object, this opens the pipe in a platform
|
||||
independent manner.
|
||||
|
||||
=cut
|
||||
|
||||
sub _do_open {
|
||||
my $self = shift;
|
||||
my ( $child_debug_fd, $parent_handle ) = @_;
|
||||
|
||||
if ( $self->dir eq "<" ) {
|
||||
( $self->{TFD}, $self->{FD} ) = IPC::Run::_pipe_nb;
|
||||
if ($parent_handle) {
|
||||
CORE::open $parent_handle, ">&=$self->{FD}"
|
||||
or croak "$! duping write end of pipe for caller";
|
||||
}
|
||||
}
|
||||
else {
|
||||
( $self->{FD}, $self->{TFD} ) = IPC::Run::_pipe;
|
||||
if ($parent_handle) {
|
||||
CORE::open $parent_handle, "<&=$self->{FD}"
|
||||
or croak "$! duping read end of pipe for caller";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub open_pipe {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
## Hmmm, Maybe allow named pipes one day. But until then...
|
||||
croak "IPC::Run::IO: Can't pipe() when a file name has been set"
|
||||
if defined $self->{FILENAME};
|
||||
|
||||
$self->_do_open(@_);
|
||||
|
||||
## return ( child_fd, parent_fd )
|
||||
return $self->dir eq "<"
|
||||
? ( $self->{TFD}, $self->{FD} )
|
||||
: ( $self->{FD}, $self->{TFD} );
|
||||
}
|
||||
|
||||
sub _cleanup { ## Called from Run.pm's _cleanup
|
||||
my $self = shift;
|
||||
undef $self->{FAKE_PIPE};
|
||||
}
|
||||
|
||||
=item close
|
||||
|
||||
Closes the handle. Throws an exception on failure.
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
sub close {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
if ( defined $self->{HANDLE} ) {
|
||||
close $self->{HANDLE}
|
||||
or croak(
|
||||
"IPC::Run::IO: $! closing "
|
||||
. (
|
||||
defined $self->{FILENAME}
|
||||
? "'$self->{FILENAME}'"
|
||||
: "handle"
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
IPC::Run::_close( $self->{FD} );
|
||||
}
|
||||
|
||||
$self->{FD} = undef;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item fileno
|
||||
|
||||
Returns the fileno of the handle. Throws an exception on failure.
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
sub fileno {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
my $fd = fileno $self->{HANDLE};
|
||||
croak(
|
||||
"IPC::Run::IO: $! "
|
||||
. (
|
||||
defined $self->{FILENAME}
|
||||
? "'$self->{FILENAME}'"
|
||||
: "handle"
|
||||
)
|
||||
) unless defined $fd;
|
||||
|
||||
return $fd;
|
||||
}
|
||||
|
||||
=item mode
|
||||
|
||||
Returns the operator in terms of 'r', 'w', and 'a'. There is a state
|
||||
'ra', unlike Perl's open(), which indicates that data read from the
|
||||
handle or file will be appended to the output if the output is a scalar.
|
||||
This is only meaningful if the output is a scalar, it has no effect if
|
||||
the output is a subroutine.
|
||||
|
||||
The redirection operators can be a little confusing, so here's a reference
|
||||
table:
|
||||
|
||||
> r Read from handle in to process
|
||||
< w Write from process out to handle
|
||||
>> ra Read from handle in to process, appending it to existing
|
||||
data if the destination is a scalar.
|
||||
<< wa Write from process out to handle, appending to existing
|
||||
data if IPC::Run::IO opened a named file.
|
||||
|
||||
=cut
|
||||
|
||||
sub mode {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
croak "IPC::Run::IO: unexpected arguments for mode(): @_" if @_;
|
||||
|
||||
## TODO: Optimize this
|
||||
return ( $self->{TYPE} =~ /</ ? 'w' : 'r' ) . ( $self->{TYPE} =~ /<<|>>/ ? 'a' : '' );
|
||||
}
|
||||
|
||||
=item op
|
||||
|
||||
Returns the operation: '<', '>', '<<', '>>'. See L</mode> if you want
|
||||
to spell these 'r', 'w', etc.
|
||||
|
||||
=cut
|
||||
|
||||
sub op {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
croak "IPC::Run::IO: unexpected arguments for op(): @_" if @_;
|
||||
|
||||
return $self->{TYPE};
|
||||
}
|
||||
|
||||
=item binmode
|
||||
|
||||
Sets/gets whether this pipe is in binmode or not. No effect off of Win32
|
||||
OSs, of course, and on Win32, no effect after the harness is start()ed.
|
||||
|
||||
=cut
|
||||
|
||||
sub binmode {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
$self->{BINMODE} = shift if @_;
|
||||
|
||||
return $self->{BINMODE};
|
||||
}
|
||||
|
||||
=item dir
|
||||
|
||||
Returns the first character of $self->op. This is either "<" or ">".
|
||||
|
||||
=cut
|
||||
|
||||
sub dir {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
croak "IPC::Run::IO: unexpected arguments for dir(): @_" if @_;
|
||||
|
||||
return substr $self->{TYPE}, 0, 1;
|
||||
}
|
||||
|
||||
##
|
||||
## Filter Scaffolding
|
||||
##
|
||||
#my $filter_op ; ## The op running a filter chain right now
|
||||
#my $filter_num; ## Which filter is being run right now.
|
||||
|
||||
use vars (
|
||||
'$filter_op', ## The op running a filter chain right now
|
||||
'$filter_num' ## Which filter is being run right now.
|
||||
);
|
||||
|
||||
sub _init_filters {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
confess "\$self not an IPC::Run::IO" unless UNIVERSAL::isa( $self, "IPC::Run::IO" );
|
||||
$self->{FBUFS} = [];
|
||||
|
||||
$self->{FBUFS}->[0] = $self->{DEST}
|
||||
if $self->{DEST} && ref $self->{DEST} eq 'SCALAR';
|
||||
|
||||
return unless $self->{FILTERS} && @{ $self->{FILTERS} };
|
||||
|
||||
push @{ $self->{FBUFS} }, map {
|
||||
my $s = "";
|
||||
\$s;
|
||||
} ( @{ $self->{FILTERS} }, '' );
|
||||
|
||||
push @{ $self->{FBUFS} }, $self->{SOURCE};
|
||||
}
|
||||
|
||||
=item poll
|
||||
|
||||
TODO: Needs confirmation that this is correct. Was previously undocumented.
|
||||
|
||||
I believe this is polling the IO for new input and then returns undef if there will never be any more input, 0 if there is none now, but there might be in the future, and TRUE if more input was gotten.
|
||||
|
||||
=cut
|
||||
|
||||
sub poll {
|
||||
my IPC::Run::IO $self = shift;
|
||||
my ($harness) = @_;
|
||||
|
||||
if ( defined $self->{FD} ) {
|
||||
my $d = $self->dir;
|
||||
if ( $d eq "<" ) {
|
||||
if ( vec $harness->{WOUT}, $self->{FD}, 1 ) {
|
||||
_debug_desc_fd( "filtering data to", $self )
|
||||
if _debugging_details;
|
||||
return $self->_do_filters($harness);
|
||||
}
|
||||
}
|
||||
elsif ( $d eq ">" ) {
|
||||
if ( vec $harness->{ROUT}, $self->{FD}, 1 ) {
|
||||
_debug_desc_fd( "filtering data from", $self )
|
||||
if _debugging_details;
|
||||
return $self->_do_filters($harness);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _do_filters {
|
||||
my IPC::Run::IO $self = shift;
|
||||
|
||||
( $self->{HARNESS} ) = @_;
|
||||
|
||||
my ( $saved_op, $saved_num ) = ( $IPC::Run::filter_op, $IPC::Run::filter_num );
|
||||
$IPC::Run::filter_op = $self;
|
||||
$IPC::Run::filter_num = -1;
|
||||
my $redos = 0;
|
||||
my $r;
|
||||
{
|
||||
$@ = '';
|
||||
$r = eval { IPC::Run::get_more_input(); };
|
||||
|
||||
# Detect Resource temporarily unavailable and re-try 200 times (2 seconds), assuming select behaves (which it doesn't always? need ref)
|
||||
if ( ( $@ || '' ) =~ $IPC::Run::_EAGAIN && $redos++ < 200 ) {
|
||||
select( undef, undef, undef, 0.01 );
|
||||
redo;
|
||||
}
|
||||
}
|
||||
( $IPC::Run::filter_op, $IPC::Run::filter_num ) = ( $saved_op, $saved_num );
|
||||
$self->{HARNESS} = undef;
|
||||
die "ack ", $@ if $@;
|
||||
return $r;
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barrie Slaymaker <barries@slaysys.com>
|
||||
|
||||
=head1 TODO
|
||||
|
||||
Implement bidirectionality.
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
670
database/perl/vendor/lib/IPC/Run/Timer.pm
vendored
Normal file
670
database/perl/vendor/lib/IPC/Run/Timer.pm
vendored
Normal file
@@ -0,0 +1,670 @@
|
||||
package IPC::Run::Timer;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::Timer -- Timer channels for IPC::Run.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use IPC::Run qw( run timer timeout );
|
||||
## or IPC::Run::Timer ( timer timeout );
|
||||
## or IPC::Run::Timer ( :all );
|
||||
|
||||
## A non-fatal timer:
|
||||
$t = timer( 5 ); # or...
|
||||
$t = IO::Run::Timer->new( 5 );
|
||||
run $t, ...;
|
||||
|
||||
## A timeout (which is a timer that dies on expiry):
|
||||
$t = timeout( 5 ); # or...
|
||||
$t = IO::Run::Timer->new( 5, exception => "harness timed out" );
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class and module allows timers and timeouts to be created for use
|
||||
by IPC::Run. A timer simply expires when it's time is up. A timeout
|
||||
is a timer that throws an exception when it expires.
|
||||
|
||||
Timeouts are usually a bit simpler to use than timers: they throw an
|
||||
exception on expiration so you don't need to check them:
|
||||
|
||||
## Give @cmd 10 seconds to get started, then 5 seconds to respond
|
||||
my $t = timeout( 10 );
|
||||
$h = start(
|
||||
\@cmd, \$in, \$out,
|
||||
$t,
|
||||
);
|
||||
pump $h until $out =~ /prompt/;
|
||||
|
||||
$in = "some stimulus";
|
||||
$out = '';
|
||||
$t->time( 5 )
|
||||
pump $h until $out =~ /expected response/;
|
||||
|
||||
You do need to check timers:
|
||||
|
||||
## Give @cmd 10 seconds to get started, then 5 seconds to respond
|
||||
my $t = timer( 10 );
|
||||
$h = start(
|
||||
\@cmd, \$in, \$out,
|
||||
$t,
|
||||
);
|
||||
pump $h until $t->is_expired || $out =~ /prompt/;
|
||||
|
||||
$in = "some stimulus";
|
||||
$out = '';
|
||||
$t->time( 5 )
|
||||
pump $h until $out =~ /expected response/ || $t->is_expired;
|
||||
|
||||
Timers and timeouts that are reset get started by start() and
|
||||
pump(). Timers change state only in pump(). Since run() and
|
||||
finish() both call pump(), they act like pump() with respect to
|
||||
timers.
|
||||
|
||||
Timers and timeouts have three states: reset, running, and expired.
|
||||
Setting the timeout value resets the timer, as does calling
|
||||
the reset() method. The start() method starts (or restarts) a
|
||||
timer with the most recently set time value, no matter what state
|
||||
it's in.
|
||||
|
||||
=head2 Time values
|
||||
|
||||
All time values are in seconds. Times may be any kind of perl number,
|
||||
e.g. as integer or floating point seconds, optionally preceded by
|
||||
punctuation-separated days, hours, and minutes.
|
||||
|
||||
Examples:
|
||||
|
||||
1 1 second
|
||||
1.1 1.1 seconds
|
||||
60 60 seconds
|
||||
1:0 1 minute
|
||||
1:1 1 minute, 1 second
|
||||
1:90 2 minutes, 30 seconds
|
||||
1:2:3:4.5 1 day, 2 hours, 3 minutes, 4.5 seconds
|
||||
'inf' the infinity perl special number (the timer never finishes)
|
||||
|
||||
Absolute date/time strings are *not* accepted: year, month and
|
||||
day-of-month parsing is not available (patches welcome :-).
|
||||
|
||||
=head2 Interval fudging
|
||||
|
||||
When calculating an end time from a start time and an interval, IPC::Run::Timer
|
||||
instances add a little fudge factor. This is to ensure that no time will
|
||||
expire before the interval is up.
|
||||
|
||||
First a little background. Time is sampled in discrete increments. We'll
|
||||
call the
|
||||
exact moment that the reported time increments from one interval to the
|
||||
next a tick, and the interval between ticks as the time period. Here's
|
||||
a diagram of three ticks and the periods between them:
|
||||
|
||||
|
||||
-0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
|
||||
^ ^ ^
|
||||
|<--- period 0 ---->|<--- period 1 ---->|
|
||||
| | |
|
||||
tick 0 tick 1 tick 2
|
||||
|
||||
To see why the fudge factor is necessary, consider what would happen
|
||||
when a timer with an interval of 1 second is started right at the end of
|
||||
period 0:
|
||||
|
||||
|
||||
-0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
|
||||
^ ^ ^ ^
|
||||
| | | |
|
||||
| | | |
|
||||
tick 0 |tick 1 tick 2
|
||||
|
|
||||
start $t
|
||||
|
||||
Assuming that check() is called many times per period, then the timer
|
||||
is likely to expire just after tick 1, since the time reported will have
|
||||
lept from the value '0' to the value '1':
|
||||
|
||||
-0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
|
||||
^ ^ ^ ^ ^
|
||||
| | | | |
|
||||
| | | | |
|
||||
tick 0 |tick 1| tick 2
|
||||
| |
|
||||
start $t |
|
||||
|
|
||||
check $t
|
||||
|
||||
Adding a fudge of '1' in this example means that the timer is guaranteed
|
||||
not to expire before tick 2.
|
||||
|
||||
The fudge is not added to an interval of '0'.
|
||||
|
||||
This means that intervals guarantee a minimum interval. Given that
|
||||
the process running perl may be suspended for some period of time, or that
|
||||
it gets busy doing something time-consuming, there are no other guarantees on
|
||||
how long it will take a timer to expire.
|
||||
|
||||
=head1 SUBCLASSING
|
||||
|
||||
INCOMPATIBLE CHANGE: Due to the awkwardness introduced by ripping
|
||||
pseudohashes out of Perl, this class I<no longer> uses the fields
|
||||
pragma.
|
||||
|
||||
=head1 FUNCTIONS & METHODS
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use Fcntl;
|
||||
use Symbol;
|
||||
use Exporter;
|
||||
use Scalar::Util ();
|
||||
use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS );
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
@ISA = qw( Exporter );
|
||||
@EXPORT_OK = qw(
|
||||
check
|
||||
end_time
|
||||
exception
|
||||
expire
|
||||
interval
|
||||
is_expired
|
||||
is_reset
|
||||
is_running
|
||||
name
|
||||
reset
|
||||
start
|
||||
timeout
|
||||
timer
|
||||
);
|
||||
|
||||
%EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
|
||||
}
|
||||
|
||||
require IPC::Run;
|
||||
use IPC::Run::Debug;
|
||||
|
||||
##
|
||||
## Some helpers
|
||||
##
|
||||
my $resolution = 1;
|
||||
|
||||
sub _parse_time {
|
||||
for ( $_[0] ) {
|
||||
my $val;
|
||||
if ( not defined $_ ) {
|
||||
$val = $_;
|
||||
}
|
||||
else {
|
||||
my @f = split( /:/, $_, -1 );
|
||||
if ( scalar @f > 4 ) {
|
||||
croak "IPC::Run: expected <= 4 elements in time string '$_'";
|
||||
}
|
||||
for (@f) {
|
||||
if ( not Scalar::Util::looks_like_number($_) ) {
|
||||
croak "IPC::Run: non-numeric element '$_' in time string '$_'";
|
||||
}
|
||||
}
|
||||
my ( $s, $m, $h, $d ) = reverse @f;
|
||||
$val = ( ( ( $d || 0 ) * 24 + ( $h || 0 ) ) * 60 + ( $m || 0 ) ) * 60 + ( $s || 0 );
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
sub _calc_end_time {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
my $interval = $self->interval;
|
||||
$interval += $resolution if $interval;
|
||||
$self->end_time( $self->start_time + $interval );
|
||||
}
|
||||
|
||||
=item timer
|
||||
|
||||
A constructor function (not method) of IPC::Run::Timer instances:
|
||||
|
||||
$t = timer( 5 );
|
||||
$t = timer( 5, name => 'stall timer', debug => 1 );
|
||||
|
||||
$t = timer;
|
||||
$t->interval( 5 );
|
||||
|
||||
run ..., $t;
|
||||
run ..., $t = timer( 5 );
|
||||
|
||||
This convenience function is a shortened spelling of
|
||||
|
||||
IPC::Run::Timer->new( ... );
|
||||
|
||||
. It returns a timer in the reset state with a given interval.
|
||||
|
||||
If an exception is provided, it will be thrown when the timer notices that
|
||||
it has expired (in check()). The name is for debugging usage, if you plan on
|
||||
having multiple timers around. If no name is provided, a name like "timer #1"
|
||||
will be provided.
|
||||
|
||||
=cut
|
||||
|
||||
sub timer {
|
||||
return IPC::Run::Timer->new(@_);
|
||||
}
|
||||
|
||||
=item timeout
|
||||
|
||||
A constructor function (not method) of IPC::Run::Timer instances:
|
||||
|
||||
$t = timeout( 5 );
|
||||
$t = timeout( 5, exception => "kablooey" );
|
||||
$t = timeout( 5, name => "stall", exception => "kablooey" );
|
||||
|
||||
$t = timeout;
|
||||
$t->interval( 5 );
|
||||
|
||||
run ..., $t;
|
||||
run ..., $t = timeout( 5 );
|
||||
|
||||
A This convenience function is a shortened spelling of
|
||||
|
||||
IPC::Run::Timer->new( exception => "IPC::Run: timeout ...", ... );
|
||||
|
||||
. It returns a timer in the reset state that will throw an
|
||||
exception when it expires.
|
||||
|
||||
Takes the same parameters as L</timer>, any exception passed in overrides
|
||||
the default exception.
|
||||
|
||||
=cut
|
||||
|
||||
sub timeout {
|
||||
my $t = IPC::Run::Timer->new(@_);
|
||||
$t->exception( "IPC::Run: timeout on " . $t->name )
|
||||
unless defined $t->exception;
|
||||
return $t;
|
||||
}
|
||||
|
||||
=item new
|
||||
|
||||
IPC::Run::Timer->new() ;
|
||||
IPC::Run::Timer->new( 5 ) ;
|
||||
IPC::Run::Timer->new( 5, exception => 'kablooey' ) ;
|
||||
|
||||
Constructor. See L</timer> for details.
|
||||
|
||||
=cut
|
||||
|
||||
my $timer_counter;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
$class = ref $class || $class;
|
||||
|
||||
my IPC::Run::Timer $self = bless {}, $class;
|
||||
|
||||
$self->{STATE} = 0;
|
||||
$self->{DEBUG} = 0;
|
||||
$self->{NAME} = "timer #" . ++$timer_counter;
|
||||
|
||||
while (@_) {
|
||||
my $arg = shift;
|
||||
if ( $arg eq 'exception' ) {
|
||||
$self->exception(shift);
|
||||
}
|
||||
elsif ( $arg eq 'name' ) {
|
||||
$self->name(shift);
|
||||
}
|
||||
elsif ( $arg eq 'debug' ) {
|
||||
$self->debug(shift);
|
||||
}
|
||||
else {
|
||||
$self->interval($arg);
|
||||
}
|
||||
}
|
||||
|
||||
_debug $self->name . ' constructed'
|
||||
if $self->{DEBUG} || _debugging_details;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
=item check
|
||||
|
||||
check $t;
|
||||
check $t, $now;
|
||||
$t->check;
|
||||
|
||||
Checks to see if a timer has expired since the last check. Has no effect
|
||||
on non-running timers. This will throw an exception if one is defined.
|
||||
|
||||
IPC::Run::pump() calls this routine for any timers in the harness.
|
||||
|
||||
You may pass in a version of now, which is useful in case you have
|
||||
it lying around or you want to check several timers with a consistent
|
||||
concept of the current time.
|
||||
|
||||
Returns the time left before end_time or 0 if end_time is no longer
|
||||
in the future or the timer is not running
|
||||
(unless, of course, check() expire()s the timer and this
|
||||
results in an exception being thrown).
|
||||
|
||||
Returns undef if the timer is not running on entry, 0 if check() expires it,
|
||||
and the time left if it's left running.
|
||||
|
||||
=cut
|
||||
|
||||
sub check {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
return undef if !$self->is_running;
|
||||
return 0 if $self->is_expired;
|
||||
|
||||
my ($now) = @_;
|
||||
$now = _parse_time($now);
|
||||
$now = time unless defined $now;
|
||||
|
||||
_debug( "checking ", $self->name, " (end time ", $self->end_time, ") at ", $now ) if $self->{DEBUG} || _debugging_details;
|
||||
|
||||
my $left = $self->end_time - $now;
|
||||
return $left if $left > 0;
|
||||
|
||||
$self->expire;
|
||||
return 0;
|
||||
}
|
||||
|
||||
=item debug
|
||||
|
||||
Sets/gets the current setting of the debugging flag for this timer. This
|
||||
has no effect if debugging is not enabled for the current harness.
|
||||
|
||||
=cut
|
||||
|
||||
sub debug {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
$self->{DEBUG} = shift if @_;
|
||||
return $self->{DEBUG};
|
||||
}
|
||||
|
||||
=item end_time
|
||||
|
||||
$et = $t->end_time;
|
||||
$et = end_time $t;
|
||||
|
||||
$t->end_time( time + 10 );
|
||||
|
||||
Returns the time when this timer will or did expire. Even if this time is
|
||||
in the past, the timer may not be expired, since check() may not have been
|
||||
called yet.
|
||||
|
||||
Note that this end_time is not start_time($t) + interval($t), since some
|
||||
small extra amount of time is added to make sure that the timer does not
|
||||
expire before interval() elapses. If this were not so, then
|
||||
|
||||
Changing end_time() while a timer is running will set the expiration time.
|
||||
Changing it while it is expired has no affect, since reset()ing a timer always
|
||||
clears the end_time().
|
||||
|
||||
=cut
|
||||
|
||||
sub end_time {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if (@_) {
|
||||
$self->{END_TIME} = shift;
|
||||
_debug $self->name, ' end_time set to ', $self->{END_TIME}
|
||||
if $self->{DEBUG} > 2 || _debugging_details;
|
||||
}
|
||||
return $self->{END_TIME};
|
||||
}
|
||||
|
||||
=item exception
|
||||
|
||||
$x = $t->exception;
|
||||
$t->exception( $x );
|
||||
$t->exception( undef );
|
||||
|
||||
Sets/gets the exception to throw, if any. 'undef' means that no
|
||||
exception will be thrown. Exception does not need to be a scalar: you
|
||||
may ask that references be thrown.
|
||||
|
||||
=cut
|
||||
|
||||
sub exception {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if (@_) {
|
||||
$self->{EXCEPTION} = shift;
|
||||
_debug $self->name, ' exception set to ', $self->{EXCEPTION}
|
||||
if $self->{DEBUG} || _debugging_details;
|
||||
}
|
||||
return $self->{EXCEPTION};
|
||||
}
|
||||
|
||||
=item interval
|
||||
|
||||
$i = interval $t;
|
||||
$i = $t->interval;
|
||||
$t->interval( $i );
|
||||
|
||||
Sets the interval. Sets the end time based on the start_time() and the
|
||||
interval (and a little fudge) if the timer is running.
|
||||
|
||||
=cut
|
||||
|
||||
sub interval {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if (@_) {
|
||||
$self->{INTERVAL} = _parse_time(shift);
|
||||
_debug $self->name, ' interval set to ', $self->{INTERVAL}
|
||||
if $self->{DEBUG} > 2 || _debugging_details;
|
||||
|
||||
$self->_calc_end_time if $self->state;
|
||||
}
|
||||
return $self->{INTERVAL};
|
||||
}
|
||||
|
||||
=item expire
|
||||
|
||||
expire $t;
|
||||
$t->expire;
|
||||
|
||||
Sets the state to expired (undef).
|
||||
Will throw an exception if one
|
||||
is defined and the timer was not already expired. You can expire a
|
||||
reset timer without starting it.
|
||||
|
||||
=cut
|
||||
|
||||
sub expire {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if ( defined $self->state ) {
|
||||
_debug $self->name . ' expired'
|
||||
if $self->{DEBUG} || _debugging;
|
||||
|
||||
$self->state(undef);
|
||||
croak $self->exception if $self->exception;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item is_running
|
||||
|
||||
=cut
|
||||
|
||||
sub is_running {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
return $self->state ? 1 : 0;
|
||||
}
|
||||
|
||||
=item is_reset
|
||||
|
||||
=cut
|
||||
|
||||
sub is_reset {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
return defined $self->state && $self->state == 0;
|
||||
}
|
||||
|
||||
=item is_expired
|
||||
|
||||
=cut
|
||||
|
||||
sub is_expired {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
return !defined $self->state;
|
||||
}
|
||||
|
||||
=item name
|
||||
|
||||
Sets/gets this timer's name. The name is only used for debugging
|
||||
purposes so you can tell which freakin' timer is doing what.
|
||||
|
||||
=cut
|
||||
|
||||
sub name {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
|
||||
$self->{NAME} = shift if @_;
|
||||
return
|
||||
defined $self->{NAME} ? $self->{NAME}
|
||||
: defined $self->{EXCEPTION} ? 'timeout'
|
||||
: 'timer';
|
||||
}
|
||||
|
||||
=item reset
|
||||
|
||||
reset $t;
|
||||
$t->reset;
|
||||
|
||||
Resets the timer to the non-running, non-expired state and clears
|
||||
the end_time().
|
||||
|
||||
=cut
|
||||
|
||||
sub reset {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
$self->state(0);
|
||||
$self->end_time(undef);
|
||||
_debug $self->name . ' reset'
|
||||
if $self->{DEBUG} || _debugging;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item start
|
||||
|
||||
start $t;
|
||||
$t->start;
|
||||
start $t, $interval;
|
||||
start $t, $interval, $now;
|
||||
|
||||
Starts or restarts a timer. This always sets the start_time. It sets the
|
||||
end_time based on the interval if the timer is running or if no end time
|
||||
has been set.
|
||||
|
||||
You may pass an optional interval or current time value.
|
||||
|
||||
Not passing a defined interval causes the previous interval setting to be
|
||||
re-used unless the timer is reset and an end_time has been set
|
||||
(an exception is thrown if no interval has been set).
|
||||
|
||||
Not passing a defined current time value causes the current time to be used.
|
||||
|
||||
Passing a current time value is useful if you happen to have a time value
|
||||
lying around or if you want to make sure that several timers are started
|
||||
with the same concept of start time. You might even need to lie to an
|
||||
IPC::Run::Timer, occasionally.
|
||||
|
||||
=cut
|
||||
|
||||
sub start {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
|
||||
my ( $interval, $now ) = map { _parse_time($_) } @_;
|
||||
$now = _parse_time($now);
|
||||
$now = time unless defined $now;
|
||||
|
||||
$self->interval($interval) if defined $interval;
|
||||
|
||||
## start()ing a running or expired timer clears the end_time, so that the
|
||||
## interval is used. So does specifying an interval.
|
||||
$self->end_time(undef) if !$self->is_reset || $interval;
|
||||
|
||||
croak "IPC::Run: no timer interval or end_time defined for " . $self->name
|
||||
unless defined $self->interval || defined $self->end_time;
|
||||
|
||||
$self->state(1);
|
||||
$self->start_time($now);
|
||||
## The "+ 1" is in case the START_TIME was sampled at the end of a
|
||||
## tick (which are one second long in this module).
|
||||
$self->_calc_end_time
|
||||
unless defined $self->end_time;
|
||||
|
||||
_debug(
|
||||
$self->name, " started at ", $self->start_time,
|
||||
", with interval ", $self->interval, ", end_time ", $self->end_time
|
||||
) if $self->{DEBUG} || _debugging;
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item start_time
|
||||
|
||||
Sets/gets the start time, in seconds since the epoch. Setting this manually
|
||||
is a bad idea, it's better to call L</start>() at the correct time.
|
||||
|
||||
=cut
|
||||
|
||||
sub start_time {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if (@_) {
|
||||
$self->{START_TIME} = _parse_time(shift);
|
||||
_debug $self->name, ' start_time set to ', $self->{START_TIME}
|
||||
if $self->{DEBUG} > 2 || _debugging;
|
||||
}
|
||||
|
||||
return $self->{START_TIME};
|
||||
}
|
||||
|
||||
=item state
|
||||
|
||||
$s = state $t;
|
||||
$t->state( $s );
|
||||
|
||||
Get/Set the current state. Only use this if you really need to transfer the
|
||||
state to/from some variable.
|
||||
Use L</expire>, L</start>, L</reset>, L</is_expired>, L</is_running>,
|
||||
L</is_reset>.
|
||||
|
||||
Note: Setting the state to 'undef' to expire a timer will not throw an
|
||||
exception.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub state {
|
||||
my IPC::Run::Timer $self = shift;
|
||||
if (@_) {
|
||||
$self->{STATE} = shift;
|
||||
_debug $self->name, ' state set to ', $self->{STATE}
|
||||
if $self->{DEBUG} > 2 || _debugging;
|
||||
}
|
||||
return $self->{STATE};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 TODO
|
||||
|
||||
use Time::HiRes; if it's present.
|
||||
|
||||
Add detection and parsing of [[[HH:]MM:]SS formatted times and intervals.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barrie Slaymaker <barries@slaysys.com>
|
||||
|
||||
=cut
|
||||
486
database/perl/vendor/lib/IPC/Run/Win32Helper.pm
vendored
Normal file
486
database/perl/vendor/lib/IPC/Run/Win32Helper.pm
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
package IPC::Run::Win32Helper;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::Win32Helper - helper routines for IPC::Run on Win32 platforms.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use IPC::Run::Win32Helper; # Exports all by default
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
IPC::Run needs to use sockets to redirect subprocess I/O so that the select() loop
|
||||
will work on Win32. This seems to only work on WinNT and Win2K at this time, not
|
||||
sure if it will ever work on Win95 or Win98. If you have experience in this area, please
|
||||
contact me at barries@slaysys.com, thanks!.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use IO::Handle;
|
||||
use vars qw{ $VERSION @ISA @EXPORT };
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
@ISA = qw( Exporter );
|
||||
@EXPORT = qw(
|
||||
win32_spawn
|
||||
win32_parse_cmd_line
|
||||
_dont_inherit
|
||||
_inherit
|
||||
);
|
||||
}
|
||||
|
||||
require POSIX;
|
||||
|
||||
use Text::ParseWords;
|
||||
use Win32::Process;
|
||||
use IPC::Run::Debug;
|
||||
use Win32API::File qw(
|
||||
FdGetOsFHandle
|
||||
SetHandleInformation
|
||||
HANDLE_FLAG_INHERIT
|
||||
INVALID_HANDLE_VALUE
|
||||
);
|
||||
|
||||
## Takes an fd or a GLOB ref, never never never a Win32 handle.
|
||||
sub _dont_inherit {
|
||||
for (@_) {
|
||||
next unless defined $_;
|
||||
my $fd = $_;
|
||||
$fd = fileno $fd if ref $fd;
|
||||
_debug "disabling inheritance of ", $fd if _debugging_details;
|
||||
my $osfh = FdGetOsFHandle $fd;
|
||||
croak $^E if !defined $osfh || $osfh == INVALID_HANDLE_VALUE;
|
||||
|
||||
SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub _inherit { #### REMOVE
|
||||
for (@_) { #### REMOVE
|
||||
next unless defined $_; #### REMOVE
|
||||
my $fd = $_; #### REMOVE
|
||||
$fd = fileno $fd if ref $fd; #### REMOVE
|
||||
_debug "enabling inheritance of ", $fd if _debugging_details; #### REMOVE
|
||||
my $osfh = FdGetOsFHandle $fd; #### REMOVE
|
||||
croak $^E if !defined $osfh || $osfh == INVALID_HANDLE_VALUE; #### REMOVE
|
||||
#### REMOVE
|
||||
SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 1 ); #### REMOVE
|
||||
} #### REMOVE
|
||||
} #### REMOVE
|
||||
#### REMOVE
|
||||
#sub _inherit {
|
||||
# for ( @_ ) {
|
||||
# next unless defined $_;
|
||||
# my $osfh = GetOsFHandle $_;
|
||||
# croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE;
|
||||
# SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
|
||||
# }
|
||||
#}
|
||||
|
||||
=pod
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item optimize()
|
||||
|
||||
Most common incantations of C<run()> (I<not> C<harness()>, C<start()>,
|
||||
or C<finish()>) now use temporary files to redirect input and output
|
||||
instead of pumper processes.
|
||||
|
||||
Temporary files are used when sending to child processes if input is
|
||||
taken from a scalar with no filter subroutines. This is the only time
|
||||
we can assume that the parent is not interacting with the child's
|
||||
redirected input as it runs.
|
||||
|
||||
Temporary files are used when receiving from children when output is
|
||||
to a scalar or subroutine with or without filters, but only if
|
||||
the child in question closes its inputs or takes input from
|
||||
unfiltered SCALARs or named files. Normally, a child inherits its STDIN
|
||||
from its parent; to close it, use "0<&-" or the C<< noinherit => 1 >> option.
|
||||
If data is sent to the child from CODE refs, filehandles or from
|
||||
scalars through filters than the child's outputs will not be optimized
|
||||
because C<optimize()> assumes the parent is interacting with the child.
|
||||
It is ok if the output is filtered or handled by a subroutine, however.
|
||||
|
||||
This assumes that all named files are real files (as opposed to named
|
||||
pipes) and won't change; and that a process is not communicating with
|
||||
the child indirectly (through means not visible to IPC::Run).
|
||||
These can be an invalid assumptions, but are the 99% case.
|
||||
Write me if you need an option to enable or disable optimizations; I
|
||||
suspect it will work like the C<binary()> modifier.
|
||||
|
||||
To detect cases that you might want to optimize by closing inputs, try
|
||||
setting the C<IPCRUNDEBUG> environment variable to the special C<notopt>
|
||||
value:
|
||||
|
||||
C:> set IPCRUNDEBUG=notopt
|
||||
C:> my_app_that_uses_IPC_Run.pl
|
||||
|
||||
=item optimizer() rationalizations
|
||||
|
||||
Only for that limited case can we be sure that it's ok to batch all the
|
||||
input in to a temporary file. If STDIN is from a SCALAR or from a named
|
||||
file or filehandle (again, only in C<run()>), then outputs to CODE refs
|
||||
are also assumed to be safe enough to batch through a temp file,
|
||||
otherwise only outputs to SCALAR refs are batched. This can cause a bit
|
||||
of grief if the parent process benefits from or relies on a bit of
|
||||
"early returns" coming in before the child program exits. As long as
|
||||
the output is redirected to a SCALAR ref, this will not be visible.
|
||||
When output is redirected to a subroutine or (deprecated) filters, the
|
||||
subroutine will not get any data until after the child process exits,
|
||||
and it is likely to get bigger chunks of data at once.
|
||||
|
||||
The reason for the optimization is that, without it, "pumper" processes
|
||||
are used to overcome the inconsistencies of the Win32 API. We need to
|
||||
use anonymous pipes to connect to the child processes' stdin, stdout,
|
||||
and stderr, yet select() does not work on these. select() only works on
|
||||
sockets on Win32. So for each redirected child handle, there is
|
||||
normally a "pumper" process that connects to the parent using a
|
||||
socket--so the parent can select() on that fd--and to the child on an
|
||||
anonymous pipe--so the child can read/write a pipe.
|
||||
|
||||
Using a socket to connect directly to the child (as at least one MSDN
|
||||
article suggests) seems to cause the trailing output from most children
|
||||
to be lost. I think this is because child processes rarely close their
|
||||
stdout and stderr explicitly, and the winsock dll does not seem to flush
|
||||
output when a process that uses it exits without explicitly closing
|
||||
them.
|
||||
|
||||
Because of these pumpers and the inherent slowness of Win32
|
||||
CreateProcess(), child processes with redirects are quite slow to
|
||||
launch; so this routine looks for the very common case of
|
||||
reading/writing to/from scalar references in a run() routine and
|
||||
converts such reads and writes in to temporary file reads and writes.
|
||||
|
||||
Such files are marked as FILE_ATTRIBUTE_TEMPORARY to increase speed and
|
||||
as FILE_FLAG_DELETE_ON_CLOSE so it will be cleaned up when the child
|
||||
process exits (for input files). The user's default permissions are
|
||||
used for both the temporary files and the directory that contains them,
|
||||
hope your Win32 permissions are secure enough for you. Files are
|
||||
created with the Win32API::File defaults of
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE.
|
||||
|
||||
Setting the debug level to "details" or "gory" will give detailed
|
||||
information about the optimization process; setting it to "basic" or
|
||||
higher will tell whether or not a given call is optimized. Setting
|
||||
it to "notopt" will highlight those calls that aren't optimized.
|
||||
|
||||
=cut
|
||||
|
||||
sub optimize {
|
||||
my ($h) = @_;
|
||||
|
||||
my @kids = @{ $h->{KIDS} };
|
||||
|
||||
my $saw_pipe;
|
||||
|
||||
my ( $ok_to_optimize_outputs, $veto_output_optimization );
|
||||
|
||||
for my $kid (@kids) {
|
||||
( $ok_to_optimize_outputs, $veto_output_optimization ) = ()
|
||||
unless $saw_pipe;
|
||||
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over ok of non-SCALAR output optimization"
|
||||
if _debugging_details && $ok_to_optimize_outputs;
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over veto of non-SCALAR output optimization"
|
||||
if _debugging_details && $veto_output_optimization;
|
||||
|
||||
if ( $h->{noinherit} && !$ok_to_optimize_outputs ) {
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) STDIN not inherited from parent oking non-SCALAR output optimization"
|
||||
if _debugging_details && $ok_to_optimize_outputs;
|
||||
$ok_to_optimize_outputs = 1;
|
||||
}
|
||||
|
||||
for ( @{ $kid->{OPS} } ) {
|
||||
if ( substr( $_->{TYPE}, 0, 1 ) eq "<" ) {
|
||||
if ( $_->{TYPE} eq "<" ) {
|
||||
if ( @{ $_->{FILTERS} } > 1 ) {
|
||||
## Can't assume that the filters are idempotent.
|
||||
}
|
||||
elsif (ref $_->{SOURCE} eq "SCALAR"
|
||||
|| ref $_->{SOURCE} eq "GLOB"
|
||||
|| UNIVERSAL::isa( $_, "IO::Handle" ) ) {
|
||||
if ( $_->{KFD} == 0 ) {
|
||||
_debug
|
||||
"Win32 optimizer: (kid $kid->{NUM}) 0$_->{TYPE}",
|
||||
ref $_->{SOURCE},
|
||||
", ok to optimize outputs"
|
||||
if _debugging_details;
|
||||
$ok_to_optimize_outputs = 1;
|
||||
}
|
||||
$_->{SEND_THROUGH_TEMP_FILE} = 1;
|
||||
next;
|
||||
}
|
||||
elsif ( !ref $_->{SOURCE} && defined $_->{SOURCE} ) {
|
||||
if ( $_->{KFD} == 0 ) {
|
||||
_debug
|
||||
"Win32 optimizer: (kid $kid->{NUM}) 0<$_->{SOURCE}, ok to optimize outputs",
|
||||
if _debugging_details;
|
||||
$ok_to_optimize_outputs = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
_debug
|
||||
"Win32 optimizer: (kid $kid->{NUM}) ",
|
||||
$_->{KFD},
|
||||
$_->{TYPE},
|
||||
defined $_->{SOURCE}
|
||||
? ref $_->{SOURCE}
|
||||
? ref $_->{SOURCE}
|
||||
: $_->{SOURCE}
|
||||
: defined $_->{FILENAME} ? $_->{FILENAME}
|
||||
: "",
|
||||
@{ $_->{FILTERS} } > 1 ? " with filters" : (),
|
||||
", VETOING output opt."
|
||||
if _debugging_details || _debugging_not_optimized;
|
||||
$veto_output_optimization = 1;
|
||||
}
|
||||
elsif ( $_->{TYPE} eq "close" && $_->{KFD} == 0 ) {
|
||||
$ok_to_optimize_outputs = 1;
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&-, ok to optimize outputs"
|
||||
if _debugging_details;
|
||||
}
|
||||
elsif ( $_->{TYPE} eq "dup" && $_->{KFD2} == 0 ) {
|
||||
$veto_output_optimization = 1;
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&$_->{KFD2}, VETOING output opt."
|
||||
if _debugging_details || _debugging_not_optimized;
|
||||
}
|
||||
elsif ( $_->{TYPE} eq "|" ) {
|
||||
$saw_pipe = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$ok_to_optimize_outputs && !$veto_output_optimization ) {
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) child STDIN not redirected, VETOING non-SCALAR output opt."
|
||||
if _debugging_details || _debugging_not_optimized;
|
||||
$veto_output_optimization = 1;
|
||||
}
|
||||
|
||||
if ( $ok_to_optimize_outputs && $veto_output_optimization ) {
|
||||
$ok_to_optimize_outputs = 0;
|
||||
_debug "Win32 optimizer: (kid $kid->{NUM}) non-SCALAR output optimizations VETOed"
|
||||
if _debugging_details || _debugging_not_optimized;
|
||||
}
|
||||
|
||||
## SOURCE/DEST ARRAY means it's a filter.
|
||||
## TODO: think about checking to see if the final input/output of
|
||||
## a filter chain (an ARRAY SOURCE or DEST) is a scalar...but
|
||||
## we may be deprecating filters.
|
||||
|
||||
for ( @{ $kid->{OPS} } ) {
|
||||
if ( $_->{TYPE} eq ">" ) {
|
||||
if (
|
||||
ref $_->{DEST} eq "SCALAR"
|
||||
|| (
|
||||
(
|
||||
@{ $_->{FILTERS} } > 1
|
||||
|| ref $_->{DEST} eq "CODE"
|
||||
|| ref $_->{DEST} eq "ARRAY" ## Filters?
|
||||
)
|
||||
&& ( $ok_to_optimize_outputs && !$veto_output_optimization )
|
||||
)
|
||||
) {
|
||||
$_->{RECV_THROUGH_TEMP_FILE} = 1;
|
||||
next;
|
||||
}
|
||||
_debug
|
||||
"Win32 optimizer: NOT optimizing (kid $kid->{NUM}) ",
|
||||
$_->{KFD},
|
||||
$_->{TYPE},
|
||||
defined $_->{DEST}
|
||||
? ref $_->{DEST}
|
||||
? ref $_->{DEST}
|
||||
: $_->{SOURCE}
|
||||
: defined $_->{FILENAME} ? $_->{FILENAME}
|
||||
: "",
|
||||
@{ $_->{FILTERS} } ? " with filters" : (),
|
||||
if _debugging_details;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item win32_parse_cmd_line
|
||||
|
||||
@words = win32_parse_cmd_line( q{foo bar 'baz baz' "bat bat"} );
|
||||
|
||||
returns 4 words. This parses like the bourne shell (see
|
||||
the bit about shellwords() in L<Text::ParseWords>), assuming we're
|
||||
trying to be a little cross-platform here. The only difference is
|
||||
that "\" is *not* treated as an escape except when it precedes
|
||||
punctuation, since it's used all over the place in DOS path specs.
|
||||
|
||||
TODO: globbing? probably not (it's unDOSish).
|
||||
|
||||
TODO: shebang emulation? Probably, but perhaps that should be part
|
||||
of Run.pm so all spawned processes get the benefit.
|
||||
|
||||
LIMITATIONS: shellwords dies silently on malformed input like
|
||||
|
||||
a\"
|
||||
|
||||
=cut
|
||||
|
||||
sub win32_parse_cmd_line {
|
||||
my $line = shift;
|
||||
$line =~ s{(\\[\w\s])}{\\$1}g;
|
||||
return shellwords $line;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item win32_spawn
|
||||
|
||||
Spawns a child process, possibly with STDIN, STDOUT, and STDERR (file descriptors 0, 1, and 2, respectively) redirected.
|
||||
|
||||
B<LIMITATIONS>.
|
||||
|
||||
Cannot redirect higher file descriptors due to lack of support for this in the
|
||||
Win32 environment.
|
||||
|
||||
This can be worked around by marking a handle as inheritable in the
|
||||
parent (or leaving it marked; this is the default in perl), obtaining it's
|
||||
Win32 handle with C<Win32API::GetOSFHandle(FH)> or
|
||||
C<Win32API::FdGetOsFHandle($fd)> and passing it to the child using the command
|
||||
line, the environment, or any other IPC mechanism (it's a plain old integer).
|
||||
The child can then use C<OsFHandleOpen()> or C<OsFHandleOpenFd()> and possibly
|
||||
C<<open FOO ">&BAR">> or C<<open FOO ">&$fd>> as need be. Ach, the pain!
|
||||
|
||||
Remember to check the Win32 handle against INVALID_HANDLE_VALUE.
|
||||
|
||||
=cut
|
||||
|
||||
sub _save {
|
||||
my ( $saved, $saved_as, $fd ) = @_;
|
||||
|
||||
## We can only save aside the original fds once.
|
||||
return if exists $saved->{$fd};
|
||||
|
||||
my $saved_fd = IPC::Run::_dup($fd);
|
||||
_dont_inherit $saved_fd;
|
||||
|
||||
$saved->{$fd} = $saved_fd;
|
||||
$saved_as->{$saved_fd} = $fd;
|
||||
|
||||
_dont_inherit $saved->{$fd};
|
||||
}
|
||||
|
||||
sub _dup2_gently {
|
||||
my ( $saved, $saved_as, $fd1, $fd2 ) = @_;
|
||||
_save $saved, $saved_as, $fd2;
|
||||
|
||||
if ( exists $saved_as->{$fd2} ) {
|
||||
## The target fd is colliding with a saved-as fd, gotta bump
|
||||
## the saved-as fd to another fd.
|
||||
my $orig_fd = delete $saved_as->{$fd2};
|
||||
my $saved_fd = IPC::Run::_dup($fd2);
|
||||
_dont_inherit $saved_fd;
|
||||
|
||||
$saved->{$orig_fd} = $saved_fd;
|
||||
$saved_as->{$saved_fd} = $orig_fd;
|
||||
}
|
||||
_debug "moving $fd1 to kid's $fd2" if _debugging_details;
|
||||
IPC::Run::_dup2_rudely( $fd1, $fd2 );
|
||||
}
|
||||
|
||||
sub win32_spawn {
|
||||
my ( $cmd, $ops ) = @_;
|
||||
|
||||
## NOTE: The debug pipe write handle is passed to pump processes as STDOUT.
|
||||
## and is not to the "real" child process, since they would not know
|
||||
## what to do with it...unlike Unix, we have no code executing in the
|
||||
## child before the "real" child is exec()ed.
|
||||
|
||||
my %saved; ## Map of parent's orig fd -> saved fd
|
||||
my %saved_as; ## Map of parent's saved fd -> orig fd, used to
|
||||
## detect collisions between a KFD and the fd a
|
||||
## parent's fd happened to be saved to.
|
||||
|
||||
for my $op (@$ops) {
|
||||
_dont_inherit $op->{FD} if defined $op->{FD};
|
||||
|
||||
if ( defined $op->{KFD} && $op->{KFD} > 2 ) {
|
||||
## TODO: Detect this in harness()
|
||||
## TODO: enable temporary redirections if ever necessary, not
|
||||
## sure why they would be...
|
||||
## 4>&1 1>/dev/null 1>&4 4>&-
|
||||
croak "Can't redirect fd #", $op->{KFD}, " on Win32";
|
||||
}
|
||||
|
||||
## This is very similar logic to IPC::Run::_do_kid_and_exit().
|
||||
if ( defined $op->{TFD} ) {
|
||||
unless ( $op->{TFD} == $op->{KFD} ) {
|
||||
_dup2_gently \%saved, \%saved_as, $op->{TFD}, $op->{KFD};
|
||||
_dont_inherit $op->{TFD};
|
||||
}
|
||||
}
|
||||
elsif ( $op->{TYPE} eq "dup" ) {
|
||||
_dup2_gently \%saved, \%saved_as, $op->{KFD1}, $op->{KFD2}
|
||||
unless $op->{KFD1} == $op->{KFD2};
|
||||
}
|
||||
elsif ( $op->{TYPE} eq "close" ) {
|
||||
_save \%saved, \%saved_as, $op->{KFD};
|
||||
IPC::Run::_close( $op->{KFD} );
|
||||
}
|
||||
elsif ( $op->{TYPE} eq "init" ) {
|
||||
## TODO: detect this in harness()
|
||||
croak "init subs not allowed on Win32";
|
||||
}
|
||||
}
|
||||
|
||||
my $process;
|
||||
my $cmd_line = join " ", map {
|
||||
( my $s = $_ ) =~ s/"/"""/g;
|
||||
$s = qq{"$s"} if /[\"\s]|^$/;
|
||||
$s;
|
||||
} @$cmd;
|
||||
|
||||
_debug "cmd line: ", $cmd_line
|
||||
if _debugging;
|
||||
|
||||
Win32::Process::Create(
|
||||
$process,
|
||||
$cmd->[0],
|
||||
$cmd_line,
|
||||
1, ## Inherit handles
|
||||
0, ## Inherit parent priortiy class. Was NORMAL_PRIORITY_CLASS
|
||||
".",
|
||||
) or croak "$!: Win32::Process::Create()";
|
||||
|
||||
for my $orig_fd ( keys %saved ) {
|
||||
IPC::Run::_dup2_rudely( $saved{$orig_fd}, $orig_fd );
|
||||
IPC::Run::_close( $saved{$orig_fd} );
|
||||
}
|
||||
|
||||
return ( $process->GetProcessID(), $process );
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2001, Barrie Slaymaker, All Rights Reserved.
|
||||
|
||||
You may use this under the terms of either the GPL 2.0 or the Artistic License.
|
||||
|
||||
=cut
|
||||
551
database/perl/vendor/lib/IPC/Run/Win32IO.pm
vendored
Normal file
551
database/perl/vendor/lib/IPC/Run/Win32IO.pm
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
package IPC::Run::Win32IO;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::Win32IO - helper routines for IPC::Run on Win32 platforms.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use IPC::Run::Win32IO; # Exports all by default
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
IPC::Run needs to use sockets to redirect subprocess I/O so that the select()
|
||||
loop will work on Win32. This seems to only work on WinNT and Win2K at this
|
||||
time, not sure if it will ever work on Win95 or Win98. If you have experience
|
||||
in this area, please contact me at barries@slaysys.com, thanks!.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
A specialized IO class used on Win32.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use IO::Handle;
|
||||
use Socket;
|
||||
require POSIX;
|
||||
|
||||
use vars qw{$VERSION};
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
}
|
||||
|
||||
use Socket qw( IPPROTO_TCP TCP_NODELAY );
|
||||
use Symbol;
|
||||
use Text::ParseWords;
|
||||
use Win32::Process;
|
||||
use IPC::Run::Debug qw( :default _debugging_level );
|
||||
use IPC::Run::Win32Helper qw( _inherit _dont_inherit );
|
||||
use Fcntl qw( O_TEXT O_RDONLY );
|
||||
|
||||
use base qw( IPC::Run::IO );
|
||||
my @cleanup_fields;
|
||||
|
||||
BEGIN {
|
||||
## These fields will be set to undef in _cleanup to close
|
||||
## the handles.
|
||||
@cleanup_fields = (
|
||||
'SEND_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
|
||||
'RECV_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
|
||||
'TEMP_FILE_NAME', ## The name of the temp file, needed for
|
||||
## error reporting / debugging only.
|
||||
|
||||
'PARENT_HANDLE', ## The handle of the socket for the parent
|
||||
'PUMP_SOCKET_HANDLE', ## The socket handle for the pump
|
||||
'PUMP_PIPE_HANDLE', ## The anon pipe handle for the pump
|
||||
'CHILD_HANDLE', ## The anon pipe handle for the child
|
||||
|
||||
'TEMP_FILE_HANDLE', ## The Win32 filehandle for the temp file
|
||||
);
|
||||
}
|
||||
|
||||
## REMOVE OSFHandleOpen
|
||||
use Win32API::File qw(
|
||||
GetOsFHandle
|
||||
OsFHandleOpenFd
|
||||
OsFHandleOpen
|
||||
FdGetOsFHandle
|
||||
SetHandleInformation
|
||||
SetFilePointer
|
||||
HANDLE_FLAG_INHERIT
|
||||
INVALID_HANDLE_VALUE
|
||||
|
||||
createFile
|
||||
WriteFile
|
||||
ReadFile
|
||||
CloseHandle
|
||||
|
||||
FILE_ATTRIBUTE_TEMPORARY
|
||||
FILE_FLAG_DELETE_ON_CLOSE
|
||||
FILE_FLAG_WRITE_THROUGH
|
||||
|
||||
FILE_BEGIN
|
||||
);
|
||||
|
||||
# FILE_ATTRIBUTE_HIDDEN
|
||||
# FILE_ATTRIBUTE_SYSTEM
|
||||
|
||||
BEGIN {
|
||||
## Force AUTOLOADED constants to be, well, constant by getting them
|
||||
## to AUTOLOAD before compilation continues. Sigh.
|
||||
() = (
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
IPPROTO_TCP,
|
||||
TCP_NODELAY,
|
||||
HANDLE_FLAG_INHERIT,
|
||||
INVALID_HANDLE_VALUE,
|
||||
);
|
||||
}
|
||||
|
||||
use constant temp_file_flags => ( FILE_ATTRIBUTE_TEMPORARY() | FILE_FLAG_DELETE_ON_CLOSE() | FILE_FLAG_WRITE_THROUGH() );
|
||||
|
||||
# FILE_ATTRIBUTE_HIDDEN() |
|
||||
# FILE_ATTRIBUTE_SYSTEM() |
|
||||
my $tmp_file_counter;
|
||||
my $tmp_dir;
|
||||
|
||||
sub _cleanup {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
my ($harness) = @_;
|
||||
|
||||
$self->_recv_through_temp_file($harness)
|
||||
if $self->{RECV_THROUGH_TEMP_FILE};
|
||||
|
||||
CloseHandle( $self->{TEMP_FILE_HANDLE} )
|
||||
if defined $self->{TEMP_FILE_HANDLE};
|
||||
|
||||
close( $self->{CHILD_HANDLE} )
|
||||
if defined $self->{CHILD_HANDLE};
|
||||
|
||||
$self->{$_} = undef for @cleanup_fields;
|
||||
}
|
||||
|
||||
sub _create_temp_file {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
## Create a hidden temp file that Win32 will delete when we close
|
||||
## it.
|
||||
unless ( defined $tmp_dir ) {
|
||||
$tmp_dir = File::Spec->catdir( File::Spec->tmpdir, "IPC-Run.tmp" );
|
||||
|
||||
## Trust in the user's umask.
|
||||
## This could possibly be a security hole, perhaps
|
||||
## we should offer an option. Hmmmm, really, people coding
|
||||
## security conscious apps should audit this code and
|
||||
## tell me how to make it better. Nice cop-out :).
|
||||
unless ( -d $tmp_dir ) {
|
||||
mkdir $tmp_dir or croak "$!: $tmp_dir";
|
||||
}
|
||||
}
|
||||
|
||||
$self->{TEMP_FILE_NAME} = File::Spec->catfile(
|
||||
## File name is designed for easy sorting and not conflicting
|
||||
## with other processes. This should allow us to use "t"runcate
|
||||
## access in CreateFile in case something left some droppings
|
||||
## around (which should never happen because we specify
|
||||
## FLAG_DELETE_ON_CLOSE.
|
||||
## heh, belt and suspenders are better than bug reports; God forbid
|
||||
## that NT should ever crash before a temp file gets deleted!
|
||||
$tmp_dir, sprintf "Win32io-%06d-%08d", $$, $tmp_file_counter++
|
||||
);
|
||||
|
||||
$self->{TEMP_FILE_HANDLE} = createFile(
|
||||
$self->{TEMP_FILE_NAME},
|
||||
"trw", ## new, truncate, read, write
|
||||
{
|
||||
Flags => temp_file_flags,
|
||||
},
|
||||
) or croak "Can't create temporary file, $self->{TEMP_FILE_NAME}: $^E";
|
||||
|
||||
$self->{TFD} = OsFHandleOpenFd $self->{TEMP_FILE_HANDLE}, 0;
|
||||
$self->{FD} = undef;
|
||||
|
||||
_debug
|
||||
"Win32 Optimizer: temp file (",
|
||||
$self->{KFD},
|
||||
$self->{TYPE},
|
||||
$self->{TFD},
|
||||
", fh ",
|
||||
$self->{TEMP_FILE_HANDLE},
|
||||
"): ",
|
||||
$self->{TEMP_FILE_NAME}
|
||||
if _debugging_details;
|
||||
}
|
||||
|
||||
sub _reset_temp_file_pointer {
|
||||
my $self = shift;
|
||||
SetFilePointer( $self->{TEMP_FILE_HANDLE}, 0, 0, FILE_BEGIN )
|
||||
or confess "$^E seeking on (fd $self->{TFD}) $self->{TEMP_FILE_NAME} for kid's fd $self->{KFD}";
|
||||
}
|
||||
|
||||
sub _send_through_temp_file {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
_debug "Win32 optimizer: optimizing " . " $self->{KFD} $self->{TYPE} temp file instead of ",
|
||||
ref $self->{SOURCE} || $self->{SOURCE}
|
||||
if _debugging_details;
|
||||
|
||||
$self->_create_temp_file;
|
||||
|
||||
if ( defined ${ $self->{SOURCE} } ) {
|
||||
my $bytes_written = 0;
|
||||
my $data_ref;
|
||||
if ( $self->binmode ) {
|
||||
$data_ref = $self->{SOURCE};
|
||||
}
|
||||
else {
|
||||
my $data = ${ $self->{SOURCE} }; # Ugh, a copy.
|
||||
$data =~ s/(?<!\r)\n/\r\n/g;
|
||||
$data_ref = \$data;
|
||||
}
|
||||
|
||||
WriteFile(
|
||||
$self->{TEMP_FILE_HANDLE},
|
||||
$$data_ref,
|
||||
0, ## Write entire buffer
|
||||
$bytes_written,
|
||||
[], ## Not overlapped.
|
||||
) or croak "$^E writing $self->{TEMP_FILE_NAME} for kid to read on fd $self->{KFD}";
|
||||
_debug "Win32 optimizer: wrote $bytes_written to temp file $self->{TEMP_FILE_NAME}"
|
||||
if _debugging_data;
|
||||
|
||||
$self->_reset_temp_file_pointer;
|
||||
|
||||
}
|
||||
|
||||
_debug "Win32 optimizer: kid to read $self->{KFD} from temp file on $self->{TFD}"
|
||||
if _debugging_details;
|
||||
}
|
||||
|
||||
sub _init_recv_through_temp_file {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
$self->_create_temp_file;
|
||||
}
|
||||
|
||||
## TODO: Use the Win32 API in the select loop to see if the file has grown
|
||||
## and read it incrementally if it has.
|
||||
sub _recv_through_temp_file {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
## This next line kicks in if the run() never got to initting things
|
||||
## and needs to clean up.
|
||||
return undef unless defined $self->{TEMP_FILE_HANDLE};
|
||||
|
||||
push @{ $self->{FILTERS} }, sub {
|
||||
my ( undef, $out_ref ) = @_;
|
||||
|
||||
return undef unless defined $self->{TEMP_FILE_HANDLE};
|
||||
|
||||
my $r;
|
||||
my $s;
|
||||
ReadFile(
|
||||
$self->{TEMP_FILE_HANDLE},
|
||||
$s,
|
||||
999_999, ## Hmmm, should read the size.
|
||||
$r,
|
||||
[]
|
||||
) or croak "$^E reading from $self->{TEMP_FILE_NAME}";
|
||||
|
||||
_debug "ReadFile( $self->{TFD} ) = $r chars '$s'" if _debugging_data;
|
||||
|
||||
return undef unless $r;
|
||||
|
||||
$s =~ s/\r\n/\n/g unless $self->binmode;
|
||||
|
||||
my $pos = pos $$out_ref;
|
||||
$$out_ref .= $s;
|
||||
pos($out_ref) = $pos;
|
||||
return 1;
|
||||
};
|
||||
|
||||
my ($harness) = @_;
|
||||
|
||||
$self->_reset_temp_file_pointer;
|
||||
|
||||
1 while $self->_do_filters($harness);
|
||||
|
||||
pop @{ $self->{FILTERS} };
|
||||
|
||||
IPC::Run::_close( $self->{TFD} );
|
||||
}
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over
|
||||
|
||||
=item poll
|
||||
|
||||
Windows version of IPC::Run::IP::poll.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub poll {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
return if $self->{SEND_THROUGH_TEMP_FILE} || $self->{RECV_THROUGH_TEMP_FILE};
|
||||
|
||||
return $self->SUPER::poll(@_);
|
||||
}
|
||||
|
||||
## When threaded Perls get good enough, we should use threads here.
|
||||
## The problem with threaded perls is that they dup() all sorts of
|
||||
## filehandles and fds and don't allow sufficient control over
|
||||
## closing off the ones we don't want.
|
||||
|
||||
sub _spawn_pumper {
|
||||
my ( $stdin, $stdout, $debug_fd, $binmode, $child_label, @opts ) = @_;
|
||||
my ( $stdin_fd, $stdout_fd ) = ( fileno $stdin, fileno $stdout );
|
||||
|
||||
_debug "pumper stdin = ", $stdin_fd if _debugging_details;
|
||||
_debug "pumper stdout = ", $stdout_fd if _debugging_details;
|
||||
_inherit $stdin_fd, $stdout_fd, $debug_fd;
|
||||
my @I_options = map qq{"-I$_"}, @INC;
|
||||
|
||||
my $cmd_line = join(
|
||||
" ",
|
||||
qq{"$^X"},
|
||||
@I_options,
|
||||
qw(-MIPC::Run::Win32Pump -e 1 ),
|
||||
## I'm using this clunky way of passing filehandles to the child process
|
||||
## in order to avoid some kind of premature closure of filehandles
|
||||
## problem I was having with VCP's test suite when passing them
|
||||
## via CreateProcess. All of the ## REMOVE code is stuff I'd like
|
||||
## to be rid of and the ## ADD code is what I'd like to use.
|
||||
FdGetOsFHandle($stdin_fd), ## REMOVE
|
||||
FdGetOsFHandle($stdout_fd), ## REMOVE
|
||||
FdGetOsFHandle($debug_fd), ## REMOVE
|
||||
$binmode ? 1 : 0,
|
||||
$$, $^T, _debugging_level, qq{"$child_label"},
|
||||
@opts
|
||||
);
|
||||
|
||||
# open SAVEIN, "<&STDIN" or croak "$! saving STDIN"; #### ADD
|
||||
# open SAVEOUT, ">&STDOUT" or croak "$! saving STDOUT"; #### ADD
|
||||
# open SAVEERR, ">&STDERR" or croak "$! saving STDERR"; #### ADD
|
||||
# _dont_inherit \*SAVEIN; #### ADD
|
||||
# _dont_inherit \*SAVEOUT; #### ADD
|
||||
# _dont_inherit \*SAVEERR; #### ADD
|
||||
# open STDIN, "<&$stdin_fd" or croak "$! dup2()ing $stdin_fd (pumper's STDIN)"; #### ADD
|
||||
# open STDOUT, ">&$stdout_fd" or croak "$! dup2()ing $stdout_fd (pumper's STDOUT)"; #### ADD
|
||||
# open STDERR, ">&$debug_fd" or croak "$! dup2()ing $debug_fd (pumper's STDERR/debug_fd)"; #### ADD
|
||||
|
||||
_debug "pump cmd line: ", $cmd_line if _debugging_details;
|
||||
|
||||
my $process;
|
||||
Win32::Process::Create(
|
||||
$process,
|
||||
$^X,
|
||||
$cmd_line,
|
||||
1, ## Inherit handles
|
||||
NORMAL_PRIORITY_CLASS,
|
||||
".",
|
||||
) or croak "$!: Win32::Process::Create()";
|
||||
|
||||
# open STDIN, "<&SAVEIN" or croak "$! restoring STDIN"; #### ADD
|
||||
# open STDOUT, ">&SAVEOUT" or croak "$! restoring STDOUT"; #### ADD
|
||||
# open STDERR, ">&SAVEERR" or croak "$! restoring STDERR"; #### ADD
|
||||
# close SAVEIN or croak "$! closing SAVEIN"; #### ADD
|
||||
# close SAVEOUT or croak "$! closing SAVEOUT"; #### ADD
|
||||
# close SAVEERR or croak "$! closing SAVEERR"; #### ADD
|
||||
|
||||
close $stdin or croak "$! closing pumper's stdin in parent";
|
||||
close $stdout or croak "$! closing pumper's stdout in parent";
|
||||
|
||||
# Don't close $debug_fd, we need it, as do other pumpers.
|
||||
|
||||
# Pause a moment to allow the child to get up and running and emit
|
||||
# debug messages. This does not always work.
|
||||
# select undef, undef, undef, 1 if _debugging_details;
|
||||
|
||||
_debug "_spawn_pumper pid = ", $process->GetProcessID
|
||||
if _debugging_data;
|
||||
}
|
||||
|
||||
my $loopback = inet_aton "127.0.0.1";
|
||||
my $tcp_proto = getprotobyname('tcp');
|
||||
croak "$!: getprotobyname('tcp')" unless defined $tcp_proto;
|
||||
|
||||
sub _socket {
|
||||
my ($server) = @_;
|
||||
$server ||= gensym;
|
||||
my $client = gensym;
|
||||
|
||||
my $listener = gensym;
|
||||
socket $listener, PF_INET, SOCK_STREAM, $tcp_proto
|
||||
or croak "$!: socket()";
|
||||
setsockopt $listener, SOL_SOCKET, SO_REUSEADDR, pack( "l", 0 )
|
||||
or croak "$!: setsockopt()";
|
||||
|
||||
unless ( bind $listener, sockaddr_in( 0, $loopback ) ) {
|
||||
croak "Error binding: $!";
|
||||
}
|
||||
|
||||
my ($port) = sockaddr_in( getsockname($listener) );
|
||||
|
||||
_debug "win32 port = $port" if _debugging_details;
|
||||
|
||||
listen $listener, my $queue_size = 1
|
||||
or croak "$!: listen()";
|
||||
|
||||
{
|
||||
socket $client, PF_INET, SOCK_STREAM, $tcp_proto
|
||||
or croak "$!: socket()";
|
||||
|
||||
my $paddr = sockaddr_in( $port, $loopback );
|
||||
|
||||
connect $client, $paddr
|
||||
or croak "$!: connect()";
|
||||
|
||||
croak "$!: accept" unless defined $paddr;
|
||||
|
||||
## The windows "default" is SO_DONTLINGER, which should make
|
||||
## sure all socket data goes through. I have my doubts based
|
||||
## on experimentation, but nothing prompts me to set SO_LINGER
|
||||
## at this time...
|
||||
setsockopt $client, IPPROTO_TCP, TCP_NODELAY, pack( "l", 0 )
|
||||
or croak "$!: setsockopt()";
|
||||
}
|
||||
|
||||
{
|
||||
_debug "accept()ing on port $port" if _debugging_details;
|
||||
my $paddr = accept( $server, $listener );
|
||||
croak "$!: accept()" unless defined $paddr;
|
||||
}
|
||||
|
||||
_debug "win32 _socket = ( ", fileno $server, ", ", fileno $client, " ) on port $port"
|
||||
if _debugging_details;
|
||||
return ( $server, $client );
|
||||
}
|
||||
|
||||
sub _open_socket_pipe {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
my ( $debug_fd, $parent_handle ) = @_;
|
||||
|
||||
my $is_send_to_child = $self->dir eq "<";
|
||||
|
||||
$self->{CHILD_HANDLE} = gensym;
|
||||
$self->{PUMP_PIPE_HANDLE} = gensym;
|
||||
|
||||
(
|
||||
$self->{PARENT_HANDLE},
|
||||
$self->{PUMP_SOCKET_HANDLE}
|
||||
) = _socket $parent_handle;
|
||||
|
||||
## These binmodes seem to have no effect on Win2K, but just to be safe
|
||||
## I do them.
|
||||
binmode $self->{PARENT_HANDLE} or die $!;
|
||||
binmode $self->{PUMP_SOCKET_HANDLE} or die $!;
|
||||
|
||||
_debug "PUMP_SOCKET_HANDLE = ", fileno $self->{PUMP_SOCKET_HANDLE}
|
||||
if _debugging_details;
|
||||
##my $buf;
|
||||
##$buf = "write on child end of " . fileno( $self->{WRITE_HANDLE} ) . "\n\n\n\n\n";
|
||||
##POSIX::write(fileno $self->{WRITE_HANDLE}, $buf, length $buf) or warn "$! in syswrite";
|
||||
##$buf = "write on parent end of " . fileno( $self->{CHILD_HANDLE} ) . "\r\n";
|
||||
##POSIX::write(fileno $self->{CHILD_HANDLE},$buf, length $buf) or warn "$! in syswrite";
|
||||
## $self->{CHILD_HANDLE}->autoflush( 1 );
|
||||
## $self->{WRITE_HANDLE}->autoflush( 1 );
|
||||
|
||||
## Now fork off a data pump and arrange to return the correct fds.
|
||||
if ($is_send_to_child) {
|
||||
pipe $self->{CHILD_HANDLE}, $self->{PUMP_PIPE_HANDLE}
|
||||
or croak "$! opening child pipe";
|
||||
_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
|
||||
if _debugging_details;
|
||||
_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
|
||||
if _debugging_details;
|
||||
}
|
||||
else {
|
||||
pipe $self->{PUMP_PIPE_HANDLE}, $self->{CHILD_HANDLE}
|
||||
or croak "$! opening child pipe";
|
||||
_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
|
||||
if _debugging_details;
|
||||
_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
|
||||
if _debugging_details;
|
||||
}
|
||||
|
||||
## These binmodes seem to have no effect on Win2K, but just to be safe
|
||||
## I do them.
|
||||
binmode $self->{CHILD_HANDLE};
|
||||
binmode $self->{PUMP_PIPE_HANDLE};
|
||||
|
||||
## No child should ever see this.
|
||||
_dont_inherit $self->{PARENT_HANDLE};
|
||||
|
||||
## We clear the inherit flag so these file descriptors are not inherited.
|
||||
## It'll be dup()ed on to STDIN/STDOUT/STDERR before CreateProcess is
|
||||
## called and *that* fd will be inheritable.
|
||||
_dont_inherit $self->{PUMP_SOCKET_HANDLE};
|
||||
_dont_inherit $self->{PUMP_PIPE_HANDLE};
|
||||
_dont_inherit $self->{CHILD_HANDLE};
|
||||
|
||||
## Need to return $self so the HANDLEs don't get freed.
|
||||
## Return $self, $parent_fd, $child_fd
|
||||
my ( $parent_fd, $child_fd ) = (
|
||||
fileno $self->{PARENT_HANDLE},
|
||||
fileno $self->{CHILD_HANDLE}
|
||||
);
|
||||
|
||||
## Both PUMP_..._HANDLEs will be closed, no need to worry about
|
||||
## inheritance.
|
||||
_debug "binmode on" if _debugging_data && $self->binmode;
|
||||
_spawn_pumper(
|
||||
$is_send_to_child
|
||||
? ( $self->{PUMP_SOCKET_HANDLE}, $self->{PUMP_PIPE_HANDLE} )
|
||||
: ( $self->{PUMP_PIPE_HANDLE}, $self->{PUMP_SOCKET_HANDLE} ),
|
||||
$debug_fd,
|
||||
$self->binmode,
|
||||
$child_fd . $self->dir . "pump" . $self->dir . $parent_fd,
|
||||
);
|
||||
|
||||
{
|
||||
my $foo;
|
||||
confess "PARENT_HANDLE no longer open"
|
||||
unless POSIX::read( $parent_fd, $foo, 0 );
|
||||
}
|
||||
|
||||
_debug "win32_fake_pipe = ( $parent_fd, $child_fd )"
|
||||
if _debugging_details;
|
||||
|
||||
$self->{FD} = $parent_fd;
|
||||
$self->{TFD} = $child_fd;
|
||||
}
|
||||
|
||||
sub _do_open {
|
||||
my IPC::Run::Win32IO $self = shift;
|
||||
|
||||
if ( $self->{SEND_THROUGH_TEMP_FILE} ) {
|
||||
return $self->_send_through_temp_file(@_);
|
||||
}
|
||||
elsif ( $self->{RECV_THROUGH_TEMP_FILE} ) {
|
||||
return $self->_init_recv_through_temp_file(@_);
|
||||
}
|
||||
else {
|
||||
return $self->_open_socket_pipe(@_);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2001, Barrie Slaymaker, All Rights Reserved.
|
||||
|
||||
You may use this under the terms of either the GPL 2.0 or the Artistic License.
|
||||
|
||||
=cut
|
||||
173
database/perl/vendor/lib/IPC/Run/Win32Pump.pm
vendored
Normal file
173
database/perl/vendor/lib/IPC/Run/Win32Pump.pm
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package IPC::Run::Win32Pump;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
IPC::Run::Win32Pump - helper processes to shovel data to/from parent, child
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Internal use only; see IPC::Run::Win32IO and best of luck to you.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
See L<IPC::Run::Win32Helper|IPC::Run::Win32Helper> for details. This
|
||||
module is used in subprocesses that are spawned to shovel data to/from
|
||||
parent processes from/to their child processes. Where possible, pumps
|
||||
are optimized away.
|
||||
|
||||
NOTE: This is not a real module: it's a script in module form, designed
|
||||
to be run like
|
||||
|
||||
$^X -MIPC::Run::Win32Pumper -e 1 ...
|
||||
|
||||
It parses a bunch of command line parameters from IPC::Run::Win32IO.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use vars qw{$VERSION};
|
||||
|
||||
BEGIN {
|
||||
$VERSION = '20200505.0';
|
||||
}
|
||||
|
||||
use Win32API::File qw(
|
||||
OsFHandleOpen
|
||||
);
|
||||
|
||||
my ( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label );
|
||||
|
||||
BEGIN {
|
||||
( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label ) = @ARGV;
|
||||
## Rather than letting IPC::Run::Debug export all-0 constants
|
||||
## when not debugging, we do it manually in order to not even
|
||||
## load IPC::Run::Debug.
|
||||
if ($debug) {
|
||||
eval "use IPC::Run::Debug qw( :default _debug_init ); 1;"
|
||||
or die $@;
|
||||
}
|
||||
else {
|
||||
eval <<STUBS_END or die $@;
|
||||
sub _debug {}
|
||||
sub _debug_init {}
|
||||
sub _debugging() { 0 }
|
||||
sub _debugging_data() { 0 }
|
||||
sub _debugging_details() { 0 }
|
||||
sub _debugging_gory_details() { 0 }
|
||||
1;
|
||||
STUBS_END
|
||||
}
|
||||
}
|
||||
|
||||
## For some reason these get created with binmode on. AAargh, gotta #### REMOVE
|
||||
## do it by hand below. #### REMOVE
|
||||
if ($debug) { #### REMOVE
|
||||
close STDERR; #### REMOVE
|
||||
OsFHandleOpen( \*STDERR, $debug_fh, "w" ) #### REMOVE
|
||||
or print "$! opening STDERR as Win32 handle $debug_fh in pumper $$"; #### REMOVE
|
||||
} #### REMOVE
|
||||
close STDIN; #### REMOVE
|
||||
OsFHandleOpen( \*STDIN, $stdin_fh, "r" ) #### REMOVE
|
||||
or die "$! opening STDIN as Win32 handle $stdin_fh in pumper $$"; #### REMOVE
|
||||
close STDOUT; #### REMOVE
|
||||
OsFHandleOpen( \*STDOUT, $stdout_fh, "w" ) #### REMOVE
|
||||
or die "$! opening STDOUT as Win32 handle $stdout_fh in pumper $$"; #### REMOVE
|
||||
|
||||
binmode STDIN;
|
||||
binmode STDOUT;
|
||||
$| = 1;
|
||||
select STDERR;
|
||||
$| = 1;
|
||||
select STDOUT;
|
||||
|
||||
$child_label ||= "pump";
|
||||
_debug_init(
|
||||
$parent_pid,
|
||||
$parent_start_time,
|
||||
$debug,
|
||||
fileno STDERR,
|
||||
$child_label,
|
||||
);
|
||||
|
||||
_debug "Entered" if _debugging_details;
|
||||
|
||||
# No need to close all fds; win32 doesn't seem to pass any on to us.
|
||||
$| = 1;
|
||||
my $buf;
|
||||
my $total_count = 0;
|
||||
while (1) {
|
||||
my $count = sysread STDIN, $buf, 10_000;
|
||||
last unless $count;
|
||||
if (_debugging_gory_details) {
|
||||
my $msg = "'$buf'";
|
||||
substr( $msg, 100, -1 ) = '...' if length $msg > 100;
|
||||
$msg =~ s/\n/\\n/g;
|
||||
$msg =~ s/\r/\\r/g;
|
||||
$msg =~ s/\t/\\t/g;
|
||||
$msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg;
|
||||
_debug sprintf( "%5d chars revc: ", $count ), $msg;
|
||||
}
|
||||
$total_count += $count;
|
||||
$buf =~ s/\r//g unless $binmode;
|
||||
if (_debugging_gory_details) {
|
||||
my $msg = "'$buf'";
|
||||
substr( $msg, 100, -1 ) = '...' if length $msg > 100;
|
||||
$msg =~ s/\n/\\n/g;
|
||||
$msg =~ s/\r/\\r/g;
|
||||
$msg =~ s/\t/\\t/g;
|
||||
$msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg;
|
||||
_debug sprintf( "%5d chars sent: ", $count ), $msg;
|
||||
}
|
||||
print $buf;
|
||||
}
|
||||
|
||||
_debug "Exiting, transferred $total_count chars" if _debugging_details;
|
||||
|
||||
## Perform a graceful socket shutdown. Windows defaults to SO_DONTLINGER,
|
||||
## which should cause a "graceful shutdown in the background" on sockets.
|
||||
## but that's only true if the process closes the socket manually, it
|
||||
## seems; if the process exits and lets the OS clean up, the OS is not
|
||||
## so kind. STDOUT is not always a socket, of course, but it won't hurt
|
||||
## to close a pipe and may even help. With a closed source OS, who
|
||||
## can tell?
|
||||
##
|
||||
## In any case, this close() is one of the main reasons we have helper
|
||||
## processes; if the OS closed socket fds gracefully when an app exits,
|
||||
## we'd just redirect the client directly to what is now the pump end
|
||||
## of the socket. As it is, however, we need to let the client play with
|
||||
## pipes, which don't have the abort-on-app-exit behavior, and then
|
||||
## adapt to the sockets in the helper processes to allow the parent to
|
||||
## select.
|
||||
##
|
||||
## Possible alternatives / improvements:
|
||||
##
|
||||
## 1) use helper threads instead of processes. I don't trust perl's threads
|
||||
## as of 5.005 or 5.6 enough (which may be myopic of me).
|
||||
##
|
||||
## 2) figure out if/how to get at WaitForMultipleObjects() with pipe
|
||||
## handles. May be able to take the Win32 handle and pass it to
|
||||
## Win32::Event::wait_any, dunno.
|
||||
##
|
||||
## 3) Use Inline::C or a hand-tooled XS module to do helper threads.
|
||||
## This would be faster than #1, but would require a ppm distro.
|
||||
##
|
||||
close STDOUT;
|
||||
close STDERR;
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2001, Barrie Slaymaker, All Rights Reserved.
|
||||
|
||||
You may use this under the terms of either the GPL 2.0 ir the Artistic License.
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user