Safe Pipe Opens

Another interesting approach to IPC is making your single program go multiprocess and communicate between (or even amongst) yourselves. The 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, "-|"); unless (defined $pid) { warn "cannot fork: $!"; die "bailing out" if $sleep_count++ > 6; sleep 10; } } until defined $pid; if ($pid) { # parent print KID @some_data; close(KID) || 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 straigh-forward, but you can't use a pipe open or backticks 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 backtick or pipe open for read:

# add error processing as above $pid = open(KID, "-|"); if ($pid) { # parent while (<KID>) { # do something interesting } close(KID) || 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, "|-"); $SIG{ALRM} = sub { die "whoops, $program pipe broke" }; if ($pid) { # parent for (@data) { print KID; } close(KID) || 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 multithreading. If you'd like to learn more about threading, see the modules file mentioned below in the SEE ALSO section.


overview / signals / fifos / pipes / open2 / sockets / AF_INET / AF_UNIX / udp / sys5