diff options
author | 2013-03-25 20:40:40 +0000 | |
---|---|---|
committer | 2013-03-25 20:40:40 +0000 | |
commit | 48950c12d106c85f315112191a0228d7b83b9510 (patch) | |
tree | 54e43d54484c1bfe9bb06a10ede0ba3e2fa52c08 /gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm | |
parent | avoid null dereference affecting mod_perl, Perl RT bug 116441 (diff) | |
download | wireguard-openbsd-48950c12d106c85f315112191a0228d7b83b9510.tar.xz wireguard-openbsd-48950c12d106c85f315112191a0228d7b83b9510.zip |
merge/resolve conflicts
(some more to do after this one)
Diffstat (limited to 'gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm')
-rw-r--r-- | gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm | 305 |
1 files changed, 173 insertions, 132 deletions
diff --git a/gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm b/gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm index 82c20ae8287..31c68afc5ab 100644 --- a/gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm +++ b/gnu/usr.bin/perl/ext/IPC-Open3/lib/IPC/Open3.pm @@ -9,7 +9,7 @@ require Exporter; use Carp; use Symbol qw(gensym qualify); -$VERSION = 1.05; +$VERSION = '1.12'; @ISA = qw(Exporter); @EXPORT = qw(open3); @@ -48,7 +48,7 @@ instead of a pipe(2) made. If either reader or writer is the null string, this will be replaced by an autogenerated filehandle. If so, you must pass a valid lvalue -in the parameter slot so it can be overwritten in the caller, or +in the parameter slot so it can be overwritten in the caller, or an exception will be raised. The filehandles may also be integers, in which case they are understood @@ -68,9 +68,9 @@ C<open(FOO, "-|")> the child process will just be the forked Perl process rather than an external command. This feature isn't yet supported on Win32 platforms. -open3() does not wait for and reap the child process after it exits. +open3() does not wait for and reap the child process after it exits. Except for short programs where it's acceptable to let the operating system -take care of this, you need to do this yourself. This is normally as +take care of this, you need to do this yourself. This is normally as simple as calling C<waitpid $pid, 0> when you're done with the process. Failing to do this can result in an accumulation of defunct or "zombie" processes. See L<perlfunc/waitpid> for more information. @@ -121,8 +121,6 @@ The order of arguments differs from that of open2(). # allow fd numbers to be used, by Frank Tobin # allow '-' as command (c.f. open "-|"), by Adam Spiers <perl@adamspiers.org> # -# $Id: open3.pl,v 1.1 1993/11/23 06:26:15 marc Exp $ -# # usage: $pid = open3('wtr', 'rdr', 'err' 'some cmd and args', 'optarg', ...); # # spawn the given $cmd and connect rdr for @@ -151,29 +149,22 @@ our $Me = 'open3 (bug)'; # you should never see this, it's always localized # Fatal.pm needs to be fixed WRT prototypes. -sub xfork { - my $pid = fork; - defined $pid or croak "$Me: fork failed: $!"; - return $pid; -} - sub xpipe { pipe $_[0], $_[1] or croak "$Me: pipe($_[0], $_[1]) failed: $!"; } # I tried using a * prototype character for the filehandle but it still -# disallows a bearword while compiling under strict subs. +# disallows a bareword while compiling under strict subs. sub xopen { - open $_[0], $_[1] or croak "$Me: open($_[0], $_[1]) failed: $!"; + open $_[0], $_[1], @_[2..$#_] and return; + local $" = ', '; + carp "$Me: open(@_) failed: $!"; } sub xclose { - close $_[0] or croak "$Me: close($_[0]) failed: $!"; -} - -sub fh_is_fd { - return $_[0] =~ /\A=?(\d+)\z/; + $_[0] =~ /\A=?(\d+)\z/ ? eval { require POSIX; POSIX::close($1); } : close $_[0] + or croak "$Me: close($_[0]) failed: $!"; } sub xfileno { @@ -181,148 +172,173 @@ sub xfileno { return fileno $_[0]; } -use constant DO_SPAWN => $^O eq 'os2' || $^O eq 'MSWin32'; +use constant FORCE_DEBUG_SPAWN => 0; +use constant DO_SPAWN => $^O eq 'os2' || $^O eq 'MSWin32' || FORCE_DEBUG_SPAWN; sub _open3 { local $Me = shift; - my($package, $dad_wtr, $dad_rdr, $dad_err, @cmd) = @_; - my($dup_wtr, $dup_rdr, $dup_err, $kidpid); - - if (@cmd > 1 and $cmd[0] eq '-') { - croak "Arguments don't make sense when the command is '-'" - } # simulate autovivification of filehandles because # it's too ugly to use @_ throughout to make perl do it for us # tchrist 5-Mar-00 unless (eval { - $dad_wtr = $_[1] = gensym unless defined $dad_wtr && length $dad_wtr; - $dad_rdr = $_[2] = gensym unless defined $dad_rdr && length $dad_rdr; - 1; }) + $_[0] = gensym unless defined $_[0] && length $_[0]; + $_[1] = gensym unless defined $_[1] && length $_[1]; + 1; }) { # must strip crud for croak to add back, or looks ugly $@ =~ s/(?<=value attempted) at .*//s; croak "$Me: $@"; - } - - $dad_err ||= $dad_rdr; - - $dup_wtr = ($dad_wtr =~ s/^[<>]&//); - $dup_rdr = ($dad_rdr =~ s/^[<>]&//); - $dup_err = ($dad_err =~ s/^[<>]&//); - - # force unqualified filehandles into caller's package - $dad_wtr = qualify $dad_wtr, $package unless fh_is_fd($dad_wtr); - $dad_rdr = qualify $dad_rdr, $package unless fh_is_fd($dad_rdr); - $dad_err = qualify $dad_err, $package unless fh_is_fd($dad_err); - - my $kid_rdr = gensym; - my $kid_wtr = gensym; - my $kid_err = gensym; - - xpipe $kid_rdr, $dad_wtr if !$dup_wtr; - xpipe $dad_rdr, $kid_wtr if !$dup_rdr; - xpipe $dad_err, $kid_err if !$dup_err && $dad_err ne $dad_rdr; - - $kidpid = DO_SPAWN ? -1 : xfork; - if ($kidpid == 0) { # Kid - # A tie in the parent should not be allowed to cause problems. - untie *STDIN; - untie *STDOUT; - # If she wants to dup the kid's stderr onto her stdout I need to - # save a copy of her stdout before I put something else there. - if ($dad_rdr ne $dad_err && $dup_err - && xfileno($dad_err) == fileno(STDOUT)) { - my $tmp = gensym; - xopen($tmp, ">&$dad_err"); - $dad_err = $tmp; + } + + my @handles = ({ mode => '<', handle => \*STDIN }, + { mode => '>', handle => \*STDOUT }, + { mode => '>', handle => \*STDERR }, + ); + + foreach (@handles) { + $_->{parent} = shift; + $_->{open_as} = gensym; + } + + if (@_ > 1 and $_[0] eq '-') { + croak "Arguments don't make sense when the command is '-'" + } + + $handles[2]{parent} ||= $handles[1]{parent}; + $handles[2]{dup_of_out} = $handles[1]{parent} eq $handles[2]{parent}; + + my $package; + foreach (@handles) { + $_->{dup} = ($_->{parent} =~ s/^[<>]&//); + + if ($_->{parent} !~ /\A=?(\d+)\z/) { + # force unqualified filehandles into caller's package + $package //= caller 1; + $_->{parent} = qualify $_->{parent}, $package; } - if ($dup_wtr) { - xopen \*STDIN, "<&$dad_wtr" if fileno(STDIN) != xfileno($dad_wtr); + next if $_->{dup} or $_->{dup_of_out}; + if ($_->{mode} eq '<') { + xpipe $_->{open_as}, $_->{parent}; } else { - xclose $dad_wtr; - xopen \*STDIN, "<&=" . fileno $kid_rdr; + xpipe $_->{parent}, $_->{open_as}; } - if ($dup_rdr) { - xopen \*STDOUT, ">&$dad_rdr" if fileno(STDOUT) != xfileno($dad_rdr); - } else { - xclose $dad_rdr; - xopen \*STDOUT, ">&=" . fileno $kid_wtr; + } + + my $kidpid; + if (!DO_SPAWN) { + # Used to communicate exec failures. + xpipe my $stat_r, my $stat_w; + + $kidpid = fork; + croak "$Me: fork failed: $!" unless defined $kidpid; + if ($kidpid == 0) { # Kid + eval { + # A tie in the parent should not be allowed to cause problems. + untie *STDIN; + untie *STDOUT; + + close $stat_r; + require Fcntl; + my $flags = fcntl $stat_w, &Fcntl::F_GETFD, 0; + croak "$Me: fcntl failed: $!" unless $flags; + fcntl $stat_w, &Fcntl::F_SETFD, $flags|&Fcntl::FD_CLOEXEC + or croak "$Me: fcntl failed: $!"; + + # If she wants to dup the kid's stderr onto her stdout I need to + # save a copy of her stdout before I put something else there. + if (!$handles[2]{dup_of_out} && $handles[2]{dup} + && xfileno($handles[2]{parent}) == fileno \*STDOUT) { + my $tmp = gensym; + xopen($tmp, '>&', $handles[2]{parent}); + $handles[2]{parent} = $tmp; + } + + foreach (@handles) { + if ($_->{dup_of_out}) { + xopen \*STDERR, ">&STDOUT" + if defined fileno STDERR && fileno STDERR != fileno STDOUT; + } elsif ($_->{dup}) { + xopen $_->{handle}, $_->{mode} . '&', $_->{parent} + if fileno $_->{handle} != xfileno($_->{parent}); + } else { + xclose $_->{parent}; + xopen $_->{handle}, $_->{mode} . '&=', + fileno $_->{open_as}; + } + } + return 1 if ($_[0] eq '-'); + exec @_ or do { + local($")=(" "); + croak "$Me: exec of @_ failed"; + }; + } and do { + close $stat_w; + return 0; + }; + + my $bang = 0+$!; + my $err = $@; + utf8::encode $err if $] >= 5.008; + print $stat_w pack('IIa*', $bang, length($err), $err); + close $stat_w; + + eval { require POSIX; POSIX::_exit(255); }; + exit 255; } - if ($dad_rdr ne $dad_err) { - if ($dup_err) { - # I have to use a fileno here because in this one case - # I'm doing a dup but the filehandle might be a reference - # (from the special case above). - xopen \*STDERR, ">&" . xfileno($dad_err) - if fileno(STDERR) != xfileno($dad_err); - } else { - xclose $dad_err; - xopen \*STDERR, ">&=" . fileno $kid_err; + else { # Parent + close $stat_w; + my $to_read = length(pack('I', 0)) * 2; + my $bytes_read = read($stat_r, my $buf = '', $to_read); + if ($bytes_read) { + (my $bang, $to_read) = unpack('II', $buf); + read($stat_r, my $err = '', $to_read); + if ($err) { + utf8::decode $err if $] >= 5.008; + } else { + $err = "$Me: " . ($! = $bang); + } + $! = $bang; + die($err); } - } else { - xopen \*STDERR, ">&STDOUT" if fileno(STDERR) != fileno(STDOUT); } - return 0 if ($cmd[0] eq '-'); - local($")=(" "); - exec @cmd or do { - carp "$Me: exec of @cmd failed"; - eval { require POSIX; POSIX::_exit(255); }; - exit 255; - }; - } elsif (DO_SPAWN) { + } + else { # DO_SPAWN # All the bookkeeping of coincidence between handles is # handled in spawn_with_handles. my @close; - if ($dup_wtr) { - $kid_rdr = \*{$dad_wtr}; - push @close, $kid_rdr; - } else { - push @close, \*{$dad_wtr}, $kid_rdr; - } - if ($dup_rdr) { - $kid_wtr = \*{$dad_rdr}; - push @close, $kid_wtr; - } else { - push @close, \*{$dad_rdr}, $kid_wtr; - } - if ($dad_rdr ne $dad_err) { - if ($dup_err) { - $kid_err = \*{$dad_err}; - push @close, $kid_err; + + foreach (@handles) { + if ($_->{dup_of_out}) { + $_->{open_as} = $handles[1]{open_as}; + } elsif ($_->{dup}) { + $_->{open_as} = $_->{parent} =~ /\A[0-9]+\z/ + ? $_->{parent} : \*{$_->{parent}}; + push @close, $_->{open_as}; } else { - push @close, \*{$dad_err}, $kid_err; + push @close, \*{$_->{parent}}, $_->{open_as}; } - } else { - $kid_err = $kid_wtr; } require IO::Pipe; $kidpid = eval { - spawn_with_handles( [ { mode => 'r', - open_as => $kid_rdr, - handle => \*STDIN }, - { mode => 'w', - open_as => $kid_wtr, - handle => \*STDOUT }, - { mode => 'w', - open_as => $kid_err, - handle => \*STDERR }, - ], \@close, @cmd); + spawn_with_handles(\@handles, \@close, @_); }; die "$Me: $@" if $@; } - xclose $kid_rdr if !$dup_wtr; - xclose $kid_wtr if !$dup_rdr; - xclose $kid_err if !$dup_err && $dad_rdr ne $dad_err; + foreach (@handles) { + next if $_->{dup} or $_->{dup_of_out}; + xclose $_->{open_as}; + } + # If the write handle is a dup give it away entirely, close my copy # of it. - xclose $dad_wtr if $dup_wtr; + xclose $handles[0]{parent} if $handles[0]{dup}; - select((select($dad_wtr), $| = 1)[0]); # unbuffer pipe + select((select($handles[0]{parent}), $| = 1)[0]); # unbuffer pipe $kidpid; } @@ -331,43 +347,68 @@ sub open3 { local $" = ', '; croak "open3(@_): not enough arguments"; } - return _open3 'open3', scalar caller, @_ + return _open3 'open3', @_ } sub spawn_with_handles { my $fds = shift; # Fields: handle, mode, open_as my $close_in_child = shift; my ($fd, $pid, @saved_fh, $saved, %saved, @errs); - require Fcntl; foreach $fd (@$fds) { $fd->{tmp_copy} = IO::Handle->new_from_fd($fd->{handle}, $fd->{mode}); - $saved{fileno $fd->{handle}} = $fd->{tmp_copy}; + $saved{fileno $fd->{handle}} = $fd->{tmp_copy} if $fd->{tmp_copy}; } foreach $fd (@$fds) { bless $fd->{handle}, 'IO::Handle' unless eval { $fd->{handle}->isa('IO::Handle') } ; # If some of handles to redirect-to coincide with handles to # redirect, we need to use saved variants: - $fd->{handle}->fdopen($saved{fileno $fd->{open_as}} || $fd->{open_as}, + $fd->{handle}->fdopen(defined fileno $fd->{open_as} + ? $saved{fileno $fd->{open_as}} || $fd->{open_as} + : $fd->{open_as}, $fd->{mode}); } unless ($^O eq 'MSWin32') { + require Fcntl; # Stderr may be redirected below, so we save the err text: foreach $fd (@$close_in_child) { + next unless fileno $fd; fcntl($fd, Fcntl::F_SETFD(), 1) or push @errs, "fcntl $fd: $!" unless $saved{fileno $fd}; # Do not close what we redirect! } } unless (@errs) { - $pid = eval { system 1, @_ }; # 1 == P_NOWAIT + if (FORCE_DEBUG_SPAWN) { + pipe my $r, my $w or die "Pipe failed: $!"; + $pid = fork; + die "Fork failed: $!" unless defined $pid; + if (!$pid) { + { no warnings; exec @_ } + print $w 0 + $!; + close $w; + require POSIX; + POSIX::_exit(255); + } + close $w; + my $bad = <$r>; + if (defined $bad) { + $! = $bad; + undef $pid; + } + } else { + $pid = eval { system 1, @_ }; # 1 == P_NOWAIT + } push @errs, "IO::Pipe: Can't spawn-NOWAIT: $!" if !$pid || $pid < 0; } - foreach $fd (@$fds) { + # Do this in reverse, so that STDERR is restored first: + foreach $fd (reverse @$fds) { $fd->{handle}->fdopen($fd->{tmp_copy}, $fd->{mode}); - $fd->{tmp_copy}->close or croak "Can't close: $!"; + } + foreach (values %saved) { + $_->close or croak "Can't close: $!"; } croak join "\n", @errs if @errs; return $pid; |