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

NAME

IPC::ChildSafe, ChildSafe - control a child process without blocking

SYNOPSIS

   use IPC::ChildSafe;

   # Start a shell process (create a new shell object).
   $SH = IPC::ChildSafe->new('sh', 'echo ++EOT++', '++EOT++');

   # If the ls command succeeds, read lines from its stdout one at a time.
   if ($SH->cmd('ls') == 0) {
      print "Found ", scalar($SH->stdout), " files in current dir ...\n";

      # Another ls cmd - results added to the object's internal stack
      $SH->cmd('ls /tmp');

      # Since we're stuck in this dumb example, let's get the date too.
      $SH->cmd('date');

      # Now dump results to stdout - show how to get 1 line at a time
      for my $line ($SH->stdout) {
         print $line;
      }

      # You could also print the output this way:
      # print $SH->stdout;

      # Or even just:
      # $SH->stdout;

   }

   # Send it a command, read back the stdout/stderr/return code
   # into a hash array.
   my(%results) = $SH->cmd('id');               # Send an 'id' cmd
   die if $results{status};                     # Expect no errors
   die if @{$results{stdout}} != 1;             # Should be just 1 line
   die if $results{stdout}[0] !~ /^uid=/;       # Check output line

   # (lather, rinse, repeat)

   # Finishing up.
   die if $SH->finish;                          # Returns final status

DESCRIPTION

   This was written to address the "blocking problem" inherent in
most coprocessing designs such as IPC::Open3.pm, which has warnings
such as this in its documentation:

    ... additionally, this is very dangerous as you may block forever.  It
   assumes it's going to talk to something like bc, both writing to it and
   reading from it.  This is presumably safe because you "know" that
   commands like bc will read a line at a time and output a line at a
   time ...

or IPC::Open2 which has this warning from its author (Tom Christansen):

   ... I strongly advise against using open2 for almost anything, even
   though I'm its author. UNIX buffering will just drive you up the wall.
   You'll end up quite disappointed ...

The blocking problem is: once you've sent a command to your coprocess, how do you know when the output resulting from this command has finished? If you guess wrong and issue one read too many you can deadlock forever. This implementation solves the problem, at least for a subset of possible child programs, by using a little trick: it sends a 2nd (trivial) command down the pipe right in back of every real command. When we see the the output of this special command in the return pipe, we know the real command is done.

This module also returns an "exit status" for each command, which is really a count of the error messages produced by it. The programmer can optionally register his/her own discriminator function for determining which output to stderr constitutes an error message.

CONSTRUCTOR

The constructor takes 3 arguments plus an optional 4th and 5th: the 1st is the program to run, the 2nd is a command to that program which produces a unique one-line output, and the 3rd is that unique output. If a 4th arg is supplied it becomes the mode in which this object will run (default: NOTIFY, see below), and if a 5th is given it must be a code ref, which will be registered as the error discriminator. If no discriminator is supplied then a standard internal one is used.

The 2nd arg is called the "tag command". Preferably this would be something lightweight, e.g. a shell builtin. Unfortunately the current version has no support for a multi-line return value since it would require some fairly complex buffering.

DISCRIMINATOR

The discriminator function is invoked after each command completes, and is passed a reference to an array containing the stderr generated by that command in its first parameter. A pointer to the stdout is similarly supplied in the second param. Normally this function would just apply a regular expression to one or both of these and indicate by its return status whether it considers this to constitute an error condition. E.g. the version provided internally is:

    sub errors {
        my($r_stderr, $r_stdout) = @_;
        grep(!/^\+\s|warning:/i, @$r_stderr);
    }

which treats ANY output to stderr as indicative of an error, with the exception of lines beginning with "+ " (shell verbosity) or containing the string "warning:".

METHODS

  • notify/store/print/ignore

    Sets the output-handling mode. The meanings of these are described below.

  • cmd

    Send specified command to child process. Return behavior varies with context:

    array context

    returns a hash containing an array of stdout results (key: 'stdout'), an array of stderr messages ('stderr), and the "return code" of the command ('status').

    scalar context

    returns command's "return code". In the default mode (NOTIFY), sends stderr results directly to parent's stderr while storing stdout in the object for later retrieval via stdout method. In PRINT mode both stdout and stderr are sent directly to the "real" (parent's) stdout/stderr. STORE mode causes both stdout and stderr to be stored for later use, while IGNORE mode throws away both.

    void context

    similar to scalar mode but exits on nonzero return code unless in IGNORE mode.

    void context and no args

    clears the stdout and stderr buffers

  • stdout

    Return stored output from previous command(s). Behavior varies with context:

    array context

    shifts all stored lines off the stdout stack and returns them in a list.

    scalar context

    returns the number of lines currently stored in the stdout stack.

    void context

    prints the current stdout stack to actual stdout.

  • stderr

    Similar to stdout method above. Note that by default stderr does not go to the accumulator, but rather to the parent's stderr. Set the STORE attribute to leave stderr in the accumulator instead where this method can operate on it.

  • status

    Pass the current stdout and stderr buffers to the currently-registered error discriminator and return its results (aka the error count).

  • kill

    Sends an interrupt signal to the child process. If you've installed a signal handler in the parent using %SIG, you can use ->kill to stop the command currently running in the child from within the handler, then continue to use the child for potential cleanup operations before shutting down.

    A signal other than SIGINT (aka Ctrl-C) may be sent by specifing its name, e.g.

        $child->kill('HUP');

    However, note that signal handling is very complex. The only code path tested is that of SIGINT and other signals may have unpredictable results. Even with SIGINT, a great deal depends on how the tool running as the child process handles it.

  • finish

    Ends the child process and returns its final exit status.

  • dbglevel

    Sets a debugging (verbosity) level. Current defined levels are 1-4. Verbosity lines are printed with a leading '+'.

  • noexec

    Sets the 'noexec' attribute, which causes commands to not be run but to be printed with a leading '-'.

AUTHOR

David Boyce dsbperl@cleartool.com

Copyright (c) 1997-2001 David Boyce. All rights reserved. This perl program is free software; you may redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

perl(1), "perldoc IPC::Open3", _Advanced Programming in the Unix Environment_ by W. R. Stevens