Safe Pipe Opens

- Safe Pipe Opens

## 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 '-no_match_vars';
        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 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_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{PIPE} = 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
        }

Since Perl 5.8.0, you can also use the list form of `open` for pipes : the syntax

        open KID_PS, "-|", "ps", "aux" or die $!;

forks the `ps(1)` command (without spawning a shell, as there are more than three arguments to open()), and reads its standard output via the `KID_PS` filehandle. The corresponding syntax to write to command pipes (with `"|-"` in place of `"-|"`) is also implemented.

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.

- Back to Complete Dissociation of Child from Parent in the perlipc manpage

- Forward to Bidirectional Communication with Another Process in the perlipc manpage

- Up to the perlipc manpage

Safe Pipe Opens