AnyEvent-Process
view release on metacpan or search on metacpan
lib/AnyEvent/Process.pm view on Meta::CPAN
my $self = shift;
my %args = @_;
my %proc_args;
my @fh_table;
my @handles;
# Process arguments
foreach my $arg (@proc_args) {
$proc_args{$arg} = $args{$arg} // $self->{$arg};
delete $args{$arg} if defined $args{$arg};
}
if (%args) {
croak 'Unknown arguments: ' . join ', ', keys %args;
}
my ($callback_factory, $set_on_completion_args) =
_create_callback_factory($proc_args{on_completion});
# Handle fh_table
for (my $i = 0; $i < $#{$proc_args{fh_table}}; $i += 2) {
my ($handle, $args) = @{$proc_args{fh_table}}[$i, $i + 1];
unless (ref $handle eq 'GLOB' or $handle =~ /^\d{1,4}$/) {
croak "Every second element in 'fh_table' must be " .
"GLOB reference or file descriptor number";
} elsif ($args->[0] eq 'pipe') {
my ($my_fh, $child_fh);
# Create pipe or socketpair
if ($args->[1] eq '>') {
($my_fh, $child_fh) = portable_pipe;
} elsif ($args->[1] eq '<') {
($child_fh, $my_fh) = portable_pipe;
} elsif ($args->[1] eq '+>' or $args->[1] eq '+<') {
($child_fh, $my_fh) = portable_socketpair;
} else {
croak "Invalid mode '$args->[1]'";
}
unless (defined $my_fh && defined $child_fh) {
croak "Creating pipe failed: $!";
}
push @fh_table, [$handle, $child_fh];
if (ref $args->[2] eq 'GLOB') {
open $args->[2], '+>&', $my_fh;
close $my_fh;
} elsif ($args->[2] eq 'handle') {
push @handles, [$my_fh, $args->[3]];
}
} elsif ($args->[0] eq 'open') {
open my $fh, $args->[1], $args->[2];
unless (defined $fh) {
croak "Opening file failed: $!";
}
push @fh_table, [$handle, $fh];
} elsif ($args->[0] eq 'decorate') {
my $out = $args->[3];
unless (defined $out or ref $out eq 'GLOB') {
croak "Third argument of decorate must be a glob reference";
}
my ($my_fh, $child_fh) = portable_pipe;
unless (defined $my_fh && defined $child_fh) {
croak "Creating pipe failed: $!";
}
my $on_read;
my $decorator = $args->[2];
if (defined $decorator and ref $decorator eq '') {
$on_read = sub {
while ($_[0]->rbuf() =~ s/^(.*\n)//) {
print $out $decorator, $1;
}
};
} elsif (defined $decorator and ref $decorator eq 'CODE') {
$on_read = sub {
while ($_[0]->rbuf() =~ s/^(.*\n)//) {
print $out $decorator->($1);
}
};
} else {
croak "Second argument of decorate must be a string or code reference";
}
push @fh_table, [$handle, $child_fh];
push @handles, [$my_fh, [on_read => $on_read, on_eof => $callback_factory->()]];
} else {
croak "Unknown redirect type '$args->[0]'";
}
}
# Start child
my $pid = fork;
my $job;
unless (defined $pid) {
croak "Fork failed: $!";
} elsif ($pid == 0) {
# Duplicate FDs
foreach my $dup (@fh_table) {
open $dup->[0], '+>&', $dup->[1];
close $dup->[1];
}
# Close handles
foreach my $dup (@handles) {
close $dup->[0];
}
# Close other filedescriptors
if (defined $proc_args{close_all_fds_except}) {
my @not_close = map fileno($_), @{$proc_args{close_all_fds_except}};
AE::log trace => "Closing all other fds except: " . join ', ', @not_close;
push @not_close, fileno $_->[0] foreach @fh_table;
AE::log trace => "Closing all other fds except: " . join ', ', @not_close;
AnyEvent::Util::close_all_fds_except @not_close;
}
# Run the code
lib/AnyEvent/Process.pm view on Meta::CPAN
close OUTPIPE;
# Read from bc standart output
my $result = <INPIPE>;
print "BC computed $result";
=head1 DESCRIPTION
This module starts a new process. It allows connecting file descriptor in the
new process to files or handles (both to perl handles or to
L<AnyEvent:Handle|AnyEvent:Handle>).
It is possible to monitor the process execution using a watchdog callback or
kill the process after a defined time.
=head1 METHODS
=head2 new
Creates new AnyEvent::Process instance.
Arguments:
=over 4
=item fh_table (optional)
Can be used to define opened files in a created process. Syntax of this option
is the following:
[
HANDLE => [TYPE, DIRECTION, ARGS...],
HANDLE => [TYPE, DIRECTION, ARGS...],
...
]
where
=over 4
=item HANDLE
is a handle reference or a filedescriptor number, which will be opened in the
new process.
=item DIRECTION
can be C<E<gt>> if the HANDLE in the new process shall be opened for writting,
C<E<lt>> if it shall be opened for reading or C<+E<lt>> if it shall be opened
in the read-write mode.
=item TYPE
Following types are supported:
=over 4
=item pipe
Opens an unidirectional pipe or a bidirectional socket (depends on the DIRECTION)
between the current and the new process. ARGS can be a glob reference, then the
second end of the pipe or socket pair is connected to it, or C<handle =E<gt>
[handle_args...]>, where handle_args are an argument passed to the
L<AnyEvent::Handle|AnyEvent::Handle> constructor, which will be connected to the
second end of the pipe or socket. In the a case handle_args is in the form of
C<method =E<gt> [method_args...]> and method is AnyEvent::Handle method, then
this method is called with method_args, after the handle is instantiated.
Example:
\*STDOUT => ['pipe', '>', handle => [push_read => [line => \&reader]]]
=item open
Opens the specified HANDLE using open with DIRECTION and ARGS as its arguments.
Example:
0 => ['open', '<', '/dev/null']
=item decorate
Decorate every line written to the HANDLE by the child. The DIRECTION must be
C<E<gt>>. ARGS are in the form C<DECORATOR, OUTPUT>. OUTPUT is a glob reference
and specifies a file handle, into which decorated lines are written. Decorator is
a string or a code reference. If the decorator is a string, it is prepended to
every line written by the started process. If the DECORATOR is a code reference,
it is called for each line written to the HANDLE with that line as its argument
and its return value is written to the OUTPUT.
Example:
\*STDERR => ['decorate', '>', 'Child STDERR: ', \*STDERR]
=back
=back
=item code (optional, but must be specified either in new or run)
A code reference, which is executed in the newly started process.
=item args (optional)
Arguments past to a code reference specified as code argument when it is called.
=item on_completion (optional)
Callback, which is executed when the process finishes. It receives
AnyEvent::Process::Job instance as the first argument and exit code as the
second argument.
It is called after all AnyEvent::Handle callbacks specified in the fh_table.
=item watchdog_interval (in seconds, optional)
How often a watchdog shall be called. If undefined or set to 0, the watchdog
functionality is disabled.
=item on_watchdog (optional)
Watchdog callback, receives AnyEvent::Process::Job instance as its argument.
If it returns false value, the watched process is killed (see on_kill).
=item kill_interval (in seconds, optional)
Maximum time the process can run. After this time expires, the process is
killed.
=item on_kill (optional, sends SIGKILL by default)
Called, when the process shall be killed. Receives AnyEvent::Process::Job
instance as its argument.
=back
=head2 run
Run a process. Any argument specified to the constructor can be overridden here.
Returns AnyEvent::Process::Job, which represents the new process, or undef on an
error.
=over 4
=item Returned AnyEvent::Process::Job instance has following methods:
( run in 1.918 second using v1.01-cache-2.11-cpan-d7f47b0818f )