The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Proc::SafeExec - Convenient utility for executing external commands in various ways.

SYNOPSIS

        use Proc::SafeExec;
        $SIG{"CHLD"} = "DEFAULT";  # Not IGNORE, so we can collect exit status.
        my $command = Proc::SafeExec->new({
                # Choose just one of these.
                "exec" => ["ls", "-l", "myfile"],  # exec() after forking.
                "fork" => 1,                       # Return undef in the child after forking.

                # Specify whether to capture each. Specify a file handle ref to dup an existing
                # one. Specify "new" to create a new file handle, "default" or undef to keep
                # the parent's descriptor, or "close" to close it.
                "stdin" => \*INPUT_PIPE,
                "stdout" => \*OUTPUT_PIPE,
                "stderr" => "new",

                # Miscellaneous options.
                "child_callback" => \&fref,  # Specify a function to call in the child after fork(), for example, to drop privileges.
                "debug" => 1,  # Emit some information via warnings, such as the command to execute.
                "no_autowait" => 1,  # Don't automatically call $command->wait() when $command is destroyed.
                "real_arg0" => "/bin/ls",  # Specify the actual file to execute.
                "untaint_args" => 1,  # Untaint the arguments before exec'ing.
        });
        printf "Child's PID is %s\n", $command->child_pid() if $command->child_pid();

The wait method waits for the child to exit or checks whether it already exited:

        $command->wait({
                # Optional hash of options.
                "no_close" => 1,  # Don't close "new" file handles.
                "nonblock" => 1,  # Don't wait if the child hasn't exited (implies no_close).
        });

To communicate with the child:

        # Perl doesn't understand <$command->stdout()>.
        my $command_stdout = $command->stdout();
        my $command_stderr = $command->stderr();

        $line = <$command_stdout>;
        $line = <$command_stderr>;
        print {$command->stdin()} "mumble\n";

To check whether the child exited yet:

        print "Exit status: ", $command->exit_status(), "\n" if $command->wait({"nonblock" => 1});

To wait until it exits:

        $command->wait();
        print "Exit status: ", $command->exit_status(), "\n";

A convenient quick tool for an alternative to $output = `@exec`:

        ($output, $?) = Proc::SafeExec::backtick(@exec);

DESCRIPTION

Proc::SafeExec provides an easy, safe way to execute external programs. It replaces all of Perl's questionable ways of accomodating this, including system(), open() with a pipe, exec(), back-ticks, etc. This module will never automatically invoke /bin/sh. This module is easy enough to use that /bin/sh should be unnecessary, even for complex pipelines.

For all errors, this module dies setting $@.

Errors from exec() in the child are reported gracefully to the parent. This means that if anything fails in the child, the error is reported through $@ with die just like any other error. This also reports $@ if child_callback dies when it is called between fork() and exec(). This is accomplished by passing $@ through an extra pipe that's closed when exec succeeds. Note: A side-effect of this is $@ is stringified if it isn't a string.

CAVEATS

When using an existing file handle by passing a reference for stdin, stdout, or stderr, new() closes the previously open file descriptor. This is to make sure, for example, that when setting up a pipeline the child process notices EOF on its stdin. If you need this file handle to stay open, dup it first. For example:

        open my $tmp_fh, "<&", $original_fh or die "dup: $!";
        my $ls = new Proc::SafeExec({"exec" => ["ls"], "stdout" => $tmp_fh});
        # $tmp_fh is now closed.

By default, $command->wait() closes any new pipes opened in the constructor. This is to prevent a deadlock where the child is waiting to read or write and the parent is waiting for the child to exit. Pass no_close to $command->wait() to prevent this (see above). Also, by default the destructor calls $command->wait() if child hasn't finished. This is to prevent zombie processes from inadvertently accumulating. To prevent this, pass no_autowait to the constructor. The easiest way to wait for the child is to call the wait method, but if you need more control, set no_autowait, then call child_pid to get the PID and do the work yourself.

This will emit a warning if the child exits with a non-zero status, and the caller didn't inspect the exit status, and the caller didn't specify no_autowait (which may imply the exit status might not be meaningful). It's bad practice not to inspect the exit status, and it's easy enough to quiet this warning if you really don't want it by calling $command->exit_status() and discarding the result.

EXAMPLES

It's easy to execute several programs to form a pipeline. For the first program, specify "new" for stdout. Then execute the second one, and specify stdout from the first one for the stdin of the second one. For example, here's how to write the equivalent of system("ls | sort > output.txt"):

        open my $output_fh, ">", "output.txt" or die "output.txt: $!\n";
        my $ls = new Proc::SafeExec({"exec" => ["ls"], "stdout" => "new"});
        my $sort = new Proc::SafeExec({"exec" => ["sort"], "stdin" => $ls->stdout(), "stdout" => $output_fh});
        $ls->wait();
        $sort->wait();
        printf "ls exited with status %i\n", ($ls->exit_status() >> 8);
        printf "sort exited with status %i\n", ($sort->exit_status() >> 8);

INSTALLATION

This module has no dependencies besides Perl itself. Follow your favorite standard installation procedure.

To test the module, run the following command line:

        $ perl -e 'use Proc::SafeExec; print Proc::SafeExec::test();'

VERSION AND HISTORY

  • Version 1.5, released 2013-06-14. Fixed bug: Open /dev/null for STDIN STDOUT STDERR instead of leaving closed when "close" is specified. Also, recommend in doc to set $SIG{"CHLD"} = "DEFAULT".

  • Version 1.4, released 2008-05-30. Added Proc::SafeExec::backtick() function for convenience. Fixed a couple minor bugs in error handling (not security related). Invalidate $? after reading it so callers must fetch the exit status through $self->exit_status().

  • Version 1.3, released 2008-03-31. Added Proc::SafeExec::Queue. Emit a warning when non-zero exit status, and the caller didn't inspect the exit status, and the caller didn't specify no_autowait (which may imply the exit status might not be meaningful).

  • Version 1.2, released 2008-01-22. Tweaked test() to handle temp files correctly, addressing https://rt.cpan.org/Ticket/Display.html?id=32458 .

  • Version 1.1, released 2008-01-09. Fixed obvious bug.

  • Version 1.0, released 2007-05-23.

SEE ALSO

The source repository is at git://git.devpit.org/Proc-SafeExec/

See also Proc::SafeExec::Queue.

MAINTAINER

Leif Pedersen, <bilbo@hobbiton.org>

COPYRIGHT AND LICENSE

 This may be distributed under the terms below (BSD'ish) or under the GPL.
 
 Copyright (c) 2007
 All Rights Reserved
 Meridian Environmental Technology, Inc.
 4324 University Avenue, Grand Forks, ND 58203
 http://meridian-enviro.com
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
 
  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
 
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the
     distribution.
 
 THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHORS OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.