POE::Wheel::Run - event driven fork/exec with added value |
POE::Wheel::Run - event driven fork/exec with added value
# Program may be scalar or \@array. $program = '/usr/bin/cat -'; $program = [ '/usr/bin/cat', '-' ];
$wheel = POE::Wheel::Run->new( # Set the program to execute, and optionally some parameters. Program => $program, ProgramArgs => \@program_args,
# Define I/O events to emit. Most are optional. StdinEvent => 'stdin', # Flushed all data to the child's STDIN. StdoutEvent => 'stdout', # Received data from the child's STDOUT. StderrEvent => 'stderr', # Received data from the child's STDERR. ErrorEvent => 'oops', # An I/O error occurred. CloseEvent => 'child_closed', # Child closed all output handles.
# Optionally adjust the child process priority, user ID, and/or # group ID. You may need to be root to do this. Priority => +5, User => scalar(getpwnam 'nobody'), Group => getgrnam('nobody'),
# Optionally 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.
# Shorthand to set StdinFilter and StdoutFilter together. StdioFilter => POE::Filter::Line->new(), # Or some other filter. );
# Information about the wheel and its process. 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(9); # TERM by default.
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 $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.
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
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. Setting it to ``pty-pipe''
gives the child process a stdin and stdout pseudo-tty, but keeps
stderr as a pipe, rather than merging stdout and stderr as with ``pty''.
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
is only valid for Conduit = "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
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 the perlvar manpage.
<POE::Driver::SysRW-
new()>>.
StdioDriver
changes both StdinDriver
and StdoutDriver
at the
same time.
CloseEvent
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.
ErrorEvent
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.
Wheel::Run requires at least one of the following three events:
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.
StdoutEvent
and StderrEvent
contain names of events that
Wheel::Run emits whenever the child process writes something to its
STDOUT or STDERR handles, respectively.
StdioFilter
contains an instance of a POE::Filter subclass. The
filter describes how the child process performs input and output.
Filter
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.
Filter
is optional. If left blank, it will default to an
instance of POE::Filter::Line-
new(Literal => ``\n'');>
StdinFilter
and StdoutFilter
can be used instead of or in
addition to StdioFilter
. They will override the default filter's
selection in situations where a process' input and output are in
different formats.
Group
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
disables setsid()
in the child process. By
default, setsid()
is called to execute the child process in a separate
Unix session.
Priority
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
is the program to exec()
once pipes and fork have been set
up. Program
's type determines how the program will be run.
If Program
holds a scalar, it will be executed as exec($scalar).
Shell metacharacters will be expanded in this form.
If Program
holds an array reference, it will executed as
exec(@$array). This form of exec()
doesn't expand shell
metacharacters.
If Program
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.
the perlfunc manpage 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
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()
changes the event that Wheel::Run emits when a certain type of
event occurs. EVENT_TYPE
may be one of the event parameters in
Wheel::Run's constructor.
$wheel->event( StdinEvent => 'new-stdin-event', StdoutEvent => 'new-stdout-event', );
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 StdinFilter
.
StdinFilter
, StdoutFilter
, or StderrFilter
respectively.
StdinFilter
and StdoutFilter
at once.
StdinFilter
, StdoutFilter
, or StderrFilter
respectively.
StdoutEvent
or StderrEvent
events. By using
these methods a session can control the flow of Stdout and Stderr
events coming in from this child process.
The kill()
method will send SIGTERM if SIGNAL is undef or omitted.
ARG0
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"; }
ARG0
contains the name of the operation that failed. It may be
'read', 'write', 'fork', 'exec' or the name of some other function or
task. The actual values aren't yet defined. Note: This is not
necessarily a function name.
ARG1
and ARG2
hold numeric and string values for $!
,
respectively. "$!"
will eq ""
for read error 0 (child process
closed STDOUT or STDERR).
ARG3
contains the wheel's unique ID.
ARG4
contains the name of the child filehandle that has the error.
It may be ``STDIN'', ``STDOUT'', or ``STDERR''. The sense of ARG0
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]; $errstr = "remote end closed" if $operation eq "read" and !$errnum; warn "Wheel $wheel_id generated $operation error $errnum: $errstr\n"; }
put()
method has been flushed to the
child's STDIN handle.
StdinEvent's ARG0
parameter contains its wheel's unique ID.
Both of these events come with two parameters. ARG0
contains the
information that the child wrote. ARG1
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"; }
One common task is scrubbing a child process' environment. This amounts to clearing the contents of %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.
POE::Wheel.
The SEE ALSO section in POE contains a table of contents covering the entire POE distribution.
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.
Please see POE for more information about authors and contributors.
POE::Wheel::Run - event driven fork/exec with added value |