NAME
Mojo::IOLoop::ReadWriteFork - Fork a process and read/write from it
VERSION
2.02
SYNOPSIS
use Mojo::Base -strict, -signatures;
my $rwf = Mojo::IOLoop::ReadWriteFork->new;
# Emitted if something terrible happens
$rwf->on(error => sub ($rwf, $error) { warn $error });
# Emitted when the child completes
$rwf->on(finish => sub ($rwf, $exit_value, $signal) { Mojo::IOLoop->stop; });
# Emitted when the child prints to STDOUT or STDERR
$rwf->on(read => sub ($rwf, $buf) { print qq(Child process sent us "$buf") });
# Need to set "conduit" for bash, ssh, and other programs that require a pty
$rwf->conduit({type => 'pty'});
# Start the application
$rwf->run('bash', -c => q(echo $YIKES foo bar baz));
# Using promises
$rwf->on(read => sub ($rwf, $buf) { ... });
$rwf->run_p('bash', -c => q(echo $YIKES foo bar baz))->wait;
See also https://github.com/jhthorsen/mojo-ioloop-readwritefork/tree/master/examples/tail.pl for an example usage from a Mojo::Controller.
DESCRIPTION
Mojo::IOLoop::ReadWriteFork enable you to fork a child process and "read" and "write" data to. You can also send signals to the child and see when the process ends. The child process can be an external program (bash, telnet, ffmpeg, ...) or a CODE block running perl.
Conduits
Mojo::IOLoop::ReadWriteFork can write to STDIN or a IO::Pty object, and read from STDOUT or STDERR, depending on the "type" given to "conduit".
Here is an overview of the different conduits:
pipe
The "pipe" type will create a STDIN and a STDOUT conduit using a plain pipe. Passing in
stderr
will also create a seperate pipe for STDERR.$rwf->conduit({type => 'pipe'}); $rwf->conduit({type => 'pipe', stderr => 1}); $rwf->write('some data'); # write to STDIN $rwf->on(read => sub { ... }); # STDOUT and STDERR $rwf->on(stdout => sub { ... }); # STDOUT $rwf->on(stderr => sub { ... }); # STDERR
This is useful if you want to run a program like "cat" that simply read/write from STDIN, STDERR or STDOUT.
pty
The "pty" type will create a STDIN and a STDOUT conduit using IO::Pty. Passing in "stderr" will also create a seperate pipe for STDERR.
$rwf->conduit({type => 'pty'}); $rwf->conduit({type => 'pty', stderr => 1}); $rwf->write('some data'); # write to STDIN $rwf->on(read => sub { ... }); # STDOUT and STDERR $rwf->on(stdout => sub { ... }); # STDOUT $rwf->on(stderr => sub { ... }); # STDERR
The difference between "pipe" and "pty" is that a IO::Pty object will be used for STDIN and STDOUT instead of a plain pipe. In addition, it is possible to pass in
clone_winsize_from
andraw
:$rwf->conduit({type => 'pty', clone_winsize_from => \*STDOUT, raw => 1});
This is useful if you want to run "bash" or another program that requires a pseudo terminal.
pty3
The "pty3" type will create a STDIN, a STDOUT, a STDERR and a PTY conduit.
$rwf->conduit({type => 'pty3'}); $rwf->write('some data'); # write to STDIN/PTY $rwf->on(pty => sub { ... }); # PTY $rwf->on(stdout => sub { ... }); # STDOUT $rwf->on(stderr => sub { ... }); # STDERR
The difference between "pty" and "pty3" is that there will be a different "read" event for bytes coming from the pseudo PTY. This type also supports "clone_winsize_from" and "raw".
$rwf->conduit({type => 'pty3', clone_winsize_from => \*STDOUT, raw => 1});
This is useful if you want to run "ssh" or another program that sends password prompts (or other output) on the PTY channel. See https://github.com/jhthorsen/mojo-ioloop-readwritefork/tree/master/examples/sshpass for an example application.
EVENTS
asset
$rwf->on(asset => sub ($rwf, $asset) { ... });
Emitted at least once when calling "run_and_capture_p". $asset
can be either a Mojo::Asset::Memory or Mojo::Asset::File object.
$rwf->on(asset => sub ($rwf, $asset) {
# $asset->auto_upgrade(1) is set by default
$asset->max_memory_size(1) if $asset->can('max_memory_size');
});
drain
$rwf->on(drain => sub ($rwf) { ... });
Emitted when the buffer has been written to the sub process.
error
$rwf->on(error => sub ($rwf, $str) { ... });
Emitted when when the there is an issue with creating, writing or reading from the child process.
finish
$rwf->on(finish => sub ($rwf, $exit_value, $signal) { ... });
Emitted when the child process exit.
pty
$rwf->on(pty => sub ($rwf, $buf) { ... });
Emitted when the child has written a chunk of data to a pty and "conduit" has "type" set to "pty3".
prepare
$rwf->on(prepare => sub ($rwf, $fh) { ... });
Emitted right before the child process is forked. $fh
can contain the example hash below or a subset:
$fh = {
stderr_read => $pipe_fh_w_or_pty_object,
stderr_read => $stderr_fh_r,
stdin_read => $pipe_fh_r,
stdin_write => $pipe_fh_r_or_pty_object,
stdin_write => $stderr_fh_w,
stdout_read => $pipe_fh_w_or_pty_object,
stdout_read => $stderr_fh_r,
stdout_write => $pipe_fh_w,
};
read
$rwf->on(read => sub ($rwf, $buf) { ... });
Emitted when the child has written a chunk of data to STDOUT or STDERR, and neither "stderr" nor "stdout" is set in the "conduit".
spawn
$rwf->on(spawn => sub ($rwf) { ... });
Emitted after fork()
has been called. Note that the child process might not yet have been started. The order of things is impossible to say, but it's something like this:
.------.
| fork |
'------'
|
___/ \_______________
| |
| (parent) | (child)
.--------------. |
| emit "spawn" | .--------------------.
'--------------' | set up filehandles |
'--------------------'
|
.---------------.
| exec $program |
'---------------'
See also "pid" for example usage of this event.
stderr
$rwf->on(stderr => sub ($rwf, $buf) { ... });
Emitted when the child has written a chunk of data to STDERR and "conduit" has the "stderr" key set to a true value or "type" is set to "pty3".
stdout
$rwf->on(stdout => sub ($rwf, $buf) { ... });
Emitted when the child has written a chunk of data to STDOUT and "conduit" has the "stdout" key set to a true value or "type" is set to "pty3".
ATTRIBUTES
conduit
$hash = $rwf->conduit;
$rwf = $rwf->conduit(\%options);
Used to set the conduit options. Possible values are:
clone_winsize_from
See "clone_winsize_from" in IO::Pty. This only makes sense if "conduit" is set to "pty". This can also be specified by using the "conduit" attribute.
raw
See "set_raw" in IO::Pty. This only makes sense if "conduit" is set to "pty". This can also be specified by using the "conduit" attribute.
stderr
This will make Mojo::IOLoop::ReadWriteFork emit "stderr" events, instead of "read" events. Setting this to "0" will close STDERR in the child.
stdout
This will make Mojo::IOLoop::ReadWriteFork emit "stdout" events, instead of "read" events. Setting this to "0" will close STDOUT in the child.
type
"type" can be either "pipe", "pty" or "pty3". Default value is "pipe".
See also "Conduits"
ioloop
$ioloop = $rwf->ioloop;
$rwf = $rwf->ioloop(Mojo::IOLoop->singleton);
Holds a Mojo::IOLoop object.
pid
$int = $rwf->pid;
Holds the child process ID. Note that "start" will start the process after the IO loop is started. This means that the code below will not work:
$rwf->run("bash", -c => q(echo $YIKES foo bar baz));
warn $rwf->pid; # pid() is not yet set
This will work though:
$rwf->on(fork => sub ($rwf) { warn $rwf->pid });
$rwf->run('bash', -c => q(echo $YIKES foo bar baz));
METHODS
close
$rwf = $rwf->close('stdin');
Close STDIN stream to the child process immediately.
run
$rwf = $rwf->run($program, @program_args);
$rwf = $rwf->run(\&Some::Perl::function, @function_args);
Simpler version of "start". Can either start an application or run a perl function.
run_and_capture_p
$p = $rwf->run_and_capture_p(...)->then(sub { my $asset = shift });
"run_and_capture_p" takes the same arguments as "run_p", but the fullfillment callback will receive a Mojo::Asset object that holds the output from the command.
See also the "asset" event.
run_p
$p = $rwf->run_p($program, @program_args);
$p = $rwf->run_p(\&Some::Perl::function, @function_args);
Promise based version of "run". The Mojo::Promise will be resolved on "finish" and rejected on "error".
start
$rwf = $rwf->start(\%args);
Used to fork and exec a child process. %args
can have:
program
Either an application or a CODE ref.
program_args
A list of options passed on to "program" or as input to the CODE ref.
Note that this module will start "program" with this code:
exec $program, @$program_args;
This means that the code is subject for shell injection unless invoked with more than one argument. This is considered a feature, but something you should be avare of. See also "exec" in perlfunc for more details.
env
Passing in
env
will override the default set of environment variables, stored in%ENV
.
write
$rwf = $rwf->write($chunk);
$rwf = $rwf->write($chunk, $cb);
Used to write data to the child process STDIN. An optional callback will be called once the $chunk
is written.
Example:
$rwf->write("some data\n", sub ($rwf) { $rwf->close });
kill
$bool = $rwf->kill;
$bool = $rwf->kill(15); # default
Used to signal the child.
SEE ALSO
https://github.com/jhthorsen/mojo-ioloop-readwritefork/tree/master/examples/tail.pl
COPYRIGHT AND LICENSE
Copyright (C) 2013-2016, Jan Henning Thorsen
This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.
AUTHOR
Jan Henning Thorsen - jhthorsen@cpan.org