open
function will accept a file argument of either "-|"
or "|-"
to do a very interesting thing: it forks a child connected to the filehandle you've opened. The child is running the same program as the parent. This is useful for safely opening a file when running under an assumed
UID or
GID, for example. If you open a pipe
to minus, you can write to the filehandle you opened and your kid will find it in his
STDIN. If you open a pipe
from minus, you can read from the filehandle you opened whatever your kid writes to his
STDOUT.
use English; my $sleep_count = 0;
do { $pid = open(KID_TO_WRITE, "|-"); unless (defined $pid) { warn "cannot fork: $!"; die "bailing out" if $sleep_count++ > 6; sleep 10; } } until defined $pid;
if ($pid) { # parent print KID_TO_WRITE @some_data; close(KID_TO_WRITE) || warn "kid exited $?"; } else { # child ($EUID, $EGID) = ($UID, $GID); # suid progs only open (FILE, "> /safe/file") || die "can't open /safe/file: $!"; while (<STDIN>) { print FILE; # child's STDIN is parent's KID } exit; # don't forget this }
Another common use for this construct is when you need to execute something
without the shell's interference. With system,
it's
straightforward, but you can't use a pipe open or back-ticks safely. That's
because there's no way to stop the shell from getting its hands on your
arguments. Instead, use lower-level control to call exec
directly.
Here's a safe back-tick or pipe open for read:
# add error processing as above $pid = open(KID_TO_READ, "-|");
if ($pid) { # parent while (<KID_TO_READ>) { # do something interesting } close(KID_TO_READ) || warn "kid exited $?";
} else { # child ($EUID, $EGID) = ($UID, $GID); # suid only exec($program, @options, @args) || die "can't exec program: $!"; # NOTREACHED }
And here's a safe pipe open for writing:
# add error processing as above $pid = open(KID_TO_WRITE, "|-"); $SIG{ALRM} = sub { die "whoops, $program pipe broke" };
if ($pid) { # parent for (@data) { print KID_TO_WRITE; } close(KID_TO_WRITE) || warn "kid exited $?";
} else { # child ($EUID, $EGID) = ($UID, $GID); exec($program, @options, @args) || die "can't exec program: $!"; # NOTREACHED }
Note that these operations are full Unix forks, which means they may not be correctly implemented on alien systems. Additionally, these are not true multi-threading. If you'd like to learn more about threading, see the modules file mentioned below in the SEE ALSO section.