man POE::Wheel::Run () - event driven fork/exec with added value

NAME

POE::Wheel::Run - event driven fork/exec with added value

SYNOPSIS

  # Program may be scalar or \@array.
  $program = '/usr/bin/cat -';
  $program = [ '/usr/bin/cat', '-' ];

  $wheel = POE::Wheel::Run->new(
    Program     => $program,
    ProgramArgs => \@program_args,     # Parameters for $program.
    Priority    => +5,                 # Adjust priority.  May need to be root.
    User        => getpwnam('nobody'), # Adjust UID. May need to be root.
    Group       => getgrnam('nobody'), # Adjust GID. May need to be root.
    ErrorEvent  => 'oops',             # Event to emit on errors.
    CloseEvent  => 'child_closed',     # Child closed all output.

    StdinEvent  => 'stdin',  # Event to emit when stdin is flushed to child.
    StdoutEvent => 'stdout', # Event to emit with child stdout information.
    StderrEvent => 'stderr', # Event to emit with child stderr information.

    # Specify different I/O formats.
    StdinFilter  => POE::Filter::Line->new(),   # Child accepts input as lines.
    StdoutFilter => POE::Filter::Stream->new(), # Child output is a stream.
    StderrFilter => POE::Filter::Line->new(),   # Child errors are lines.

    # Set StdinFilter and StdoutFilter together.
    StdioFilter => POE::Filter::Line->new(),    # Or some other filter.

    # Specify different I/O methods.
    StdinDriver  => POE::Driver::SysRW->new(),  # Defaults to SysRW.
    StdoutDriver => POE::Driver::SysRW->new(),  # Same.
    StderrDriver => POE::Driver::SysRW->new(),  # Same.

    # Set StdinDriver and StdoutDriver together.
    StdioDriver  => POE::Driver::SysRW->new(),
  );

  print "Unique wheel ID is  : ", $wheel->ID;
  print "Wheel's child PID is: ", $wheel->PID;

  # Send something to the child's STDIN.
  $wheel->put( 'input for the child' );

  # Kill the child.
  $wheel->kill();  # TERM by default
  $wheel->kill(9);

DESCRIPTION

Wheel::Run spawns child processes and establishes non-blocking, event based communication with them.

Wheel::Run does not reap child processes. For that, you need to register a SIGCHLD handler:

  $kernel->sig(CHLD => "your_event");

The session will then receive your_event with details about $? when the wheel's process exits and is reaped. POE will reap child processes as a side effect.

Another way to do it is to register CW$SIG{CHLD} = IGNORE. Use sparingly and with caution: This may clobber a handler that POE has already registered for SIGCHLD. Why does IGNORE work this way? See the discussion in perldoc perlipc.

PUBLIC METHODS

new LOTS_OF_STUFF
new() creates a new Run wheel. If successful, the new wheel represents a child process and the input, output and error pipes that speak with it. new() accepts lots of stuff. Each parameter is name/value pair.
Conduit
CWConduit describes how Wheel::Run should talk with the child process. By default it will try various forms of inter-process communication to build a pipe between the parent and child processes. If a particular method is preferred, it can be set to pipe, socketpair, or inet. It may also be set to pty if the child process should have its own pseudo tty. The reasons to define this parameter would be if you want to use pty, if the default pipe type doesn't work properly on your system, or the default pipe type's performance is poor. Pty conduits require the IO::Pty module.
Winsize
CWWinsize is only valid for CWConduit = "pty" and used to set the window size of the pty device. The window size is given as an array reference. The first element is the number of lines, the second the number of columns. The third and the fourth arguments are optional and specify the X and Y dimensions in pixels.
CloseOnCall
CWCloseOnCall emulates the close-on-exec feature for child processes which are not started by exec(). When it is set to 1, all open file handles whose descriptors are greater than $^F are closed in the child process. This is only effective when POE::Wheel::Run is called with a code reference for its Program parameter.
  CloseOnCall => 1,
  Program => \&some_function,
CloseOnCall defaults to 0 (off) to remain compatible with existing programs. For more details, please the discussion of $^F in perlvar.
StdioDriver
StdinDriver
StdoutDriver
StderrDriver
These parameters change the drivers for Wheel::Run. The default drivers are created internally with CW<POE::Driver::SysRW-new()>>. CWStdioDriver changes both CWStdinDriver and CWStdoutDriver at the same time.
CloseEvent
ErrorEvent
StdinEvent
StdoutEvent
StderrEvent
CWCloseEvent contains the name of an event to emit when the child process closes all its output handles. This is a consistent notification that the child will not be sending any more output. It does not, however, signal that the client process has stopped accepting input. CWErrorEvent contains the name of an event to emit if something fails. It is optional and if omitted, the wheel will not notify its session if any errors occur. The event receives 5 parameters as follows: ARG0 = the return value of syscall(), ARG1 = errno() - the numeric value of the error generated, ARG2 = error() - a descriptive for the given error, ARG3 = the wheel id, and ARG4 = the handle on which the error occurred (stdout, stderr, etc.) Wheel::Run requires at least one of the following three events: CWStdinEvent contains the name of an event that Wheel::Run emits whenever all its output has been flushed to the child process' STDIN handle. CWStdoutEvent and CWStderrEvent contain names of events that Wheel::Run emits whenever the child process writes something to its STDOUT or STDERR handles, respectively.
StdioFilter
StdinFilter
StdoutFilter
StderrFilter
CWStdioFilter contains an instance of a POE::Filter subclass. The filter describes how the child process performs input and output. CWFilter will be used to describe the child's stdin and stdout methods. If stderr is also to be used, StderrFilter will need to be specified separately. CWFilter is optional. If left blank, it will default to an instance of CWPOE::Filter::Line-new(Literal => \n);> CWStdinFilter and CWStdoutFilter can be used instead of or in addition to CWStdioFilter. They will override the default filter's selection in situations where a process' input and output are in different formats.
Group
CWGroup contains a numerical group ID that the child process should run at. This may not be meaningful on systems that have no concept of group IDs. The current process may need to run as root in order to change group IDs. Mileage varies considerably.
NoSetSid
When true, CWNoSetSid disables setsid() in the child process. By default, setsid() is called to execute the child process in a separate Unix session.
Priority
CWPriority contains an offset from the current process's priority. The child will be executed at the current priority plus the offset. The priority offset may be negative, but the current process may need to be running as root for that to work.
Program
CWProgram is the program to exec() once pipes and fork have been set up. CWProgram's type determines how the program will be run. If CWProgram holds a scalar, it will be executed as exec($scalar). Shell metacharacters will be expanded in this form. If CWProgram holds an array reference, it will executed as exec(@$array). This form of exec() doesn't expand shell metacharacters. If CWProgram holds a code reference, it will be called in the forked child process, and then the child will exit. This allows Wheel::Run to fork off bits of long-running code which can accept STDIN input and pass responses to STDOUT and/or STDERR. Note, however, that POE's services are effectively disabled in the child process. perlfunc has more information about exec() and the different ways to call it. Note: Do not call exit() explicitly when executing a subroutine. POE::Wheel::Run takes special care to avoid object destructors and END blocks in the child process, and calling exit() will thwart that. You may see POE::Kernel's run() method was never called. or worse.
ProgramArgs => ARRAY
If specified, CWProgramArgs should refer to a list of parameters for the program being run.
  my @parameters = qw(foo bar baz);  # will be passed to Program
  ProgramArgs => \@parameters;
event EVENT_TYPE => EVENT_NAME, ...
event() changes the event that Wheel::Run emits when a certain type of event occurs. CWEVENT_TYPE may be one of the event parameters in Wheel::Run's constructor.
  $wheel->event( StdinEvent  => 'new-stdin-event',
                 StdoutEvent => 'new-stdout-event',
               );
put LIST
put() queues a LIST of different inputs for the child process. They will be flushed asynchronously once the current state returns. Each item in the LIST is processed according to the CWStdinFilter.
get_stdin_filter
get_stdout_filter
get_stderr_filter
Get CWStdinFilter, CWStdoutFilter, or CWStderrFilter respectively.
set_stdio_filter FILTER_REFERENCE
Set CWStdinFilter and CWStdoutFilter at once.
set_stdin_filter FILTER_REFERENCE
set_stdout_filter FILTER_REFERENCE
set_stderr_filter FILTER_REFERENCE
Set CWStdinFilter, CWStdoutFilter, or CWStderrFilter respectively.
pause_stdout
pause_stderr
resume_stdout
resume_stderr
Pause or resume CWStdoutEvent or CWStderrEvent events. By using these methods a session can control the flow of Stdout and Stderr events coming in from this child process.
shutdown_stdin
Closes the child process' STDIN and stops the wheel from reporting StdinEvent. It is extremely useful for running utilities that expect to receive EOF on their standard inputs before they respond.
ID
Returns the wheel's unique ID, which is not the same as the child process' ID. Every event generated by Wheel::Run includes a wheel ID so that it can be matched up with its generator. This lets a single session manage several wheels without becoming confused about which one generated what event.
PID
Returns the child process' ID. It's useful for matching up to SIGCHLD events, which include child process IDs as well, so that wheels can be destroyed properly when children exit.
kill SIGNAL
Sends a signal to the child process. It's useful for processes which tend to be reluctant to exit when their terminals are closed. The kill() method will send SIGTERM if SIGNAL is undef or omitted.

EVENTS AND PARAMETERS

CloseEvent
CloseEvent contains the name of the event Wheel::Run emits whenever a child process has closed all its output handles. It signifies that the child will not be sending more information. In addition to the usual POE parameters, each CloseEvent comes with one of its own: CWARG0 contains the wheel's unique ID. This can be used to keep several child processes separate when they're managed by the same session. A sample close event handler:
  sub close_state {
    my ($heap, $wheel_id) = @_[HEAP, ARG0];
    my $child = delete $heap->{child}->{$wheel_id};
    print "Child ", $child->PID, " has finished.\n";
  }
ErrorEvent
ErrorEvent contains the name of an event that Wheel::Run emits whenever an error occurs. Every error event comes with four parameters: CWARG0 contains the name of the operation that failed. It may be 'read' or 'write' or 'fork' or 'exec' or something. The actual values aren't yet defined. Note: This is not necessarily a function name. CWARG1 and CWARG2 hold numeric and string values for CW$!, respectively. CWARG3 contains the wheel's unique ID. CWARG4 contains the name of the child filehandle that has the error. It may be STDIN, STDOUT, or STDERR. The sense of CWARG0 will be the opposite of what you might normally expect for these handles. For example, Wheel::Run will report a read error on STDOUT because it tried to read data from that handle. A sample error event handler:
  sub error_state {
    my ($operation, $errnum, $errstr, $wheel_id) = @_[ARG0..ARG3];
    warn "Wheel $wheel_id generated $operation error $errnum: $errstr\n";
  }
StdinEvent
StdinEvent contains the name of an event that Wheel::Run emits whenever everything queued by its put() method has been flushed to the child's STDIN handle. StdinEvent's CWARG0 parameter contains its wheel's unique ID.
StdoutEvent
StderrEvent
StdoutEvent and StderrEvent contain names for events that Wheel::Run emits whenever the child process makes output. StdoutEvent contains information the child wrote to its STDOUT handle, and StderrEvent includes whatever arrived from the child's STDERR handle. Both of these events come with two parameters. CWARG0 contains the information that the child wrote. CWARG1 holds the wheel's unique ID.
  sub stdout_state {
    my ($heap, $input, $wheel_id) = @_[HEAP, ARG0, ARG1];
    print "Child process in wheel $wheel_id wrote to STDOUT: $input\n";
  }
  sub stderr_state {
    my ($heap, $input, $wheel_id) = @_[HEAP, ARG0, ARG1];
    print "Child process in wheel $wheel_id wrote to STDERR: $input\n";
  }

TIPS AND TRICKS

One common task is scrubbing a child process' environment. This amounts to clearing the contents of CW%ENV and setting it up with some known, secure values.

Environment scrubbing is easy when the child process is running a subroutine, but it's not so easy---or at least not as intuitive---when executing external programs.

The way we do it is to run a small subroutine in the child process that performs the exec() call for us.

  Program => \&exec_with_scrubbed_env,

  sub exec_with_scrubbed_env {
    delete @ENV{keys @ENV};
    $ENV{PATH} = "/bin";
    exec(@program_and_args);
  }

That deletes everything from the environment, sets a simple, secure PATH, and executes a program with its arguments.

SEE ALSO

POE::Wheel.

The SEE ALSO section in POE contains a table of contents covering the entire POE distribution.

BUGS

Wheel::Run's constructor doesn't emit proper events when it fails. Instead, it just dies, carps or croaks.

Filter changing hasn't been implemented yet. Let the author know if it's needed. Better yet, patch the file based on the code in Wheel::ReadWrite.

Priority is a delta; there's no way to set it directly to some value.

User must be specified by UID. It would be nice to support login names.

Group must be specified by GID. It would be nice to support group names.

AUTHORS & COPYRIGHTS

Please see POE for more information about authors and contributors.