| 1 | package IPC::Open3; |
| 2 | |
| 3 | use strict; |
| 4 | no strict 'refs'; # because users pass me bareword filehandles |
| 5 | use vars qw($VERSION @ISA @EXPORT $Fh $Me); |
| 6 | |
| 7 | require 5.001; |
| 8 | require Exporter; |
| 9 | |
| 10 | use Carp; |
| 11 | use Symbol 'qualify'; |
| 12 | |
| 13 | $VERSION = 1.01; |
| 14 | @ISA = qw(Exporter); |
| 15 | @EXPORT = qw(open3); |
| 16 | |
| 17 | =head1 NAME |
| 18 | |
| 19 | IPC::Open3, open3 - open a process for reading, writing, and error handling |
| 20 | |
| 21 | =head1 SYNOPSIS |
| 22 | |
| 23 | $pid = open3(\*WTRFH, \*RDRFH, \*ERRFH |
| 24 | 'some cmd and args', 'optarg', ...); |
| 25 | |
| 26 | =head1 DESCRIPTION |
| 27 | |
| 28 | Extremely similar to open2(), open3() spawns the given $cmd and |
| 29 | connects RDRFH for reading, WTRFH for writing, and ERRFH for errors. If |
| 30 | ERRFH is '', or the same as RDRFH, then STDOUT and STDERR of the child are |
| 31 | on the same file handle. |
| 32 | |
| 33 | If WTRFH begins with "E<lt>&", then WTRFH will be closed in the parent, and |
| 34 | the child will read from it directly. If RDRFH or ERRFH begins with |
| 35 | "E<gt>&", then the child will send output directly to that file handle. In both |
| 36 | cases, there will be a dup(2) instead of a pipe(2) made. |
| 37 | |
| 38 | If you try to read from the child's stdout writer and their stderr |
| 39 | writer, you'll have problems with blocking, which means you'll |
| 40 | want to use select(), which means you'll have to use sysread() instead |
| 41 | of normal stuff. |
| 42 | |
| 43 | open3() returns the process ID of the child process. It doesn't return on |
| 44 | failure: it just raises an exception matching C</^open3:/>. |
| 45 | |
| 46 | =head1 WARNING |
| 47 | |
| 48 | It will not create these file handles for you. You have to do this |
| 49 | yourself. So don't pass it empty variables expecting them to get filled |
| 50 | in for you. |
| 51 | |
| 52 | Additionally, this is very dangerous as you may block forever. It |
| 53 | assumes it's going to talk to something like B<bc>, both writing to it |
| 54 | and reading from it. This is presumably safe because you "know" that |
| 55 | commands like B<bc> will read a line at a time and output a line at a |
| 56 | time. Programs like B<sort> that read their entire input stream first, |
| 57 | however, are quite apt to cause deadlock. |
| 58 | |
| 59 | The big problem with this approach is that if you don't have control |
| 60 | over source code being run in the the child process, you can't control |
| 61 | what it does with pipe buffering. Thus you can't just open a pipe to |
| 62 | C<cat -v> and continually read and write a line from it. |
| 63 | |
| 64 | =cut |
| 65 | |
| 66 | # &open3: Marc Horowitz <marc@mit.edu> |
| 67 | # derived mostly from &open2 by tom christiansen, <tchrist@convex.com> |
| 68 | # fixed for 5.001 by Ulrich Kunitz <kunitz@mai-koeln.com> |
| 69 | # |
| 70 | # $Id: open3.pl,v 1.1 1993/11/23 06:26:15 marc Exp $ |
| 71 | # |
| 72 | # usage: $pid = open3('wtr', 'rdr', 'err' 'some cmd and args', 'optarg', ...); |
| 73 | # |
| 74 | # spawn the given $cmd and connect rdr for |
| 75 | # reading, wtr for writing, and err for errors. |
| 76 | # if err is '', or the same as rdr, then stdout and |
| 77 | # stderr of the child are on the same fh. returns pid |
| 78 | # of child (or dies on failure). |
| 79 | |
| 80 | |
| 81 | # if wtr begins with '<&', then wtr will be closed in the parent, and |
| 82 | # the child will read from it directly. if rdr or err begins with |
| 83 | # '>&', then the child will send output directly to that fd. In both |
| 84 | # cases, there will be a dup() instead of a pipe() made. |
| 85 | |
| 86 | |
| 87 | # WARNING: this is dangerous, as you may block forever |
| 88 | # unless you are very careful. |
| 89 | # |
| 90 | # $wtr is left unbuffered. |
| 91 | # |
| 92 | # abort program if |
| 93 | # rdr or wtr are null |
| 94 | # a system call fails |
| 95 | |
| 96 | $Fh = 'FHOPEN000'; # package static in case called more than once |
| 97 | $Me = 'open3 (bug)'; # you should never see this, it's always localized |
| 98 | |
| 99 | # Fatal.pm needs to be fixed WRT prototypes. |
| 100 | |
| 101 | sub xfork { |
| 102 | my $pid = fork; |
| 103 | defined $pid or croak "$Me: fork failed: $!"; |
| 104 | return $pid; |
| 105 | } |
| 106 | |
| 107 | sub xpipe { |
| 108 | pipe $_[0], $_[1] or croak "$Me: pipe($_[0], $_[1]) failed: $!"; |
| 109 | } |
| 110 | |
| 111 | # I tried using a * prototype character for the filehandle but it still |
| 112 | # disallows a bearword while compiling under strict subs. |
| 113 | |
| 114 | sub xopen { |
| 115 | open $_[0], $_[1] or croak "$Me: open($_[0], $_[1]) failed: $!"; |
| 116 | } |
| 117 | |
| 118 | sub xclose { |
| 119 | close $_[0] or croak "$Me: close($_[0]) failed: $!"; |
| 120 | } |
| 121 | |
| 122 | sub _open3 { |
| 123 | local $Me = shift; |
| 124 | my($package, $dad_wtr, $dad_rdr, $dad_err, @cmd) = @_; |
| 125 | my($dup_wtr, $dup_rdr, $dup_err, $kidpid); |
| 126 | |
| 127 | $dad_wtr or croak "$Me: wtr should not be null"; |
| 128 | $dad_rdr or croak "$Me: rdr should not be null"; |
| 129 | $dad_err = $dad_rdr if ($dad_err eq ''); |
| 130 | |
| 131 | $dup_wtr = ($dad_wtr =~ s/^[<>]&//); |
| 132 | $dup_rdr = ($dad_rdr =~ s/^[<>]&//); |
| 133 | $dup_err = ($dad_err =~ s/^[<>]&//); |
| 134 | |
| 135 | # force unqualified filehandles into callers' package |
| 136 | $dad_wtr = qualify $dad_wtr, $package; |
| 137 | $dad_rdr = qualify $dad_rdr, $package; |
| 138 | $dad_err = qualify $dad_err, $package; |
| 139 | |
| 140 | my $kid_rdr = ++$Fh; |
| 141 | my $kid_wtr = ++$Fh; |
| 142 | my $kid_err = ++$Fh; |
| 143 | |
| 144 | xpipe $kid_rdr, $dad_wtr if !$dup_wtr; |
| 145 | xpipe $dad_rdr, $kid_wtr if !$dup_rdr; |
| 146 | xpipe $dad_err, $kid_err if !$dup_err && $dad_err ne $dad_rdr; |
| 147 | |
| 148 | $kidpid = xfork; |
| 149 | if ($kidpid == 0) { |
| 150 | # If she wants to dup the kid's stderr onto her stdout I need to |
| 151 | # save a copy of her stdout before I put something else there. |
| 152 | if ($dad_rdr ne $dad_err && $dup_err |
| 153 | && fileno($dad_err) == fileno(STDOUT)) { |
| 154 | my $tmp = ++$Fh; |
| 155 | xopen($tmp, ">&$dad_err"); |
| 156 | $dad_err = $tmp; |
| 157 | } |
| 158 | |
| 159 | if ($dup_wtr) { |
| 160 | xopen \*STDIN, "<&$dad_wtr" if fileno(STDIN) != fileno($dad_wtr); |
| 161 | } else { |
| 162 | xclose $dad_wtr; |
| 163 | xopen \*STDIN, "<&$kid_rdr"; |
| 164 | xclose $kid_rdr; |
| 165 | } |
| 166 | if ($dup_rdr) { |
| 167 | xopen \*STDOUT, ">&$dad_rdr" if fileno(STDOUT) != fileno($dad_rdr); |
| 168 | } else { |
| 169 | xclose $dad_rdr; |
| 170 | xopen \*STDOUT, ">&$kid_wtr"; |
| 171 | xclose $kid_wtr; |
| 172 | } |
| 173 | if ($dad_rdr ne $dad_err) { |
| 174 | if ($dup_err) { |
| 175 | xopen \*STDERR, ">&$dad_err" |
| 176 | if fileno(STDERR) != fileno($dad_err); |
| 177 | } else { |
| 178 | xclose $dad_err; |
| 179 | xopen \*STDERR, ">&$kid_err"; |
| 180 | xclose $kid_err; |
| 181 | } |
| 182 | } else { |
| 183 | xopen \*STDERR, ">&STDOUT" if fileno(STDERR) != fileno(STDOUT); |
| 184 | } |
| 185 | local($")=(" "); |
| 186 | exec @cmd |
| 187 | or croak "open3: exec of @cmd failed"; |
| 188 | } |
| 189 | |
| 190 | xclose $kid_rdr if !$dup_wtr; |
| 191 | xclose $kid_wtr if !$dup_rdr; |
| 192 | xclose $kid_err if !$dup_err && $dad_rdr ne $dad_err; |
| 193 | # If the write handle is a dup give it away entirely, close my copy |
| 194 | # of it. |
| 195 | xclose $dad_wtr if $dup_wtr; |
| 196 | |
| 197 | select((select($dad_wtr), $| = 1)[0]); # unbuffer pipe |
| 198 | $kidpid; |
| 199 | } |
| 200 | |
| 201 | sub open3 { |
| 202 | return _open3 'open3', scalar caller, @_ |
| 203 | } |
| 204 | 1; # so require is happy |
| 205 | |