NAME
Fugu::Process - child process management
SYNOPSIS
use Fugu::Process;
my $result = Fugu::Process->spawn_command(
cmd => [ '/usr/local/bin/mydaemon', '-f' ],
daemonize => 1,
stdout => '/var/log/mydaemon.log',
);
Fugu::Process->terminate($result->{pid}) if $result->{success};
my $r = Fugu::Process->run(cmd => [ 'rcctl', 'check', 'mydaemon' ]);
print $r->{stdout} if $r->{success};
DESCRIPTION
Fugu::Process does the parts of child-process control that are easy to get wrong. It shows the difference between a child that runs and a child that exits immediately. It sends SIGTERM first and sends SIGKILL after a delay. It reaps zombies and does not block.
Two methods start a child. spawn_command() leaves it running. run() waits for it and captures what it wrote. Both report a failed execve(2) exactly, over a close-on-exec pipe, and never by a wait-and-guess sleep(3). Neither one runs a shell: the command is a list, so no argument needs quoting and no argument can become a shell operator.
The module keeps no state. Every method is a class method.
spawn_command
spawn_command(%args) forks, redirects the standard descriptors, and runs a command.
These are the arguments:
cmd-
An array reference that holds the command and its arguments. This argument is necessary and must not be empty.
daemonize-
If this argument is true, the child calls setsid(2) before exec. The default is false.
stdin,stdout,stderr-
The paths for the child's standard descriptors. The default for each path is /dev/null.
The parent always waits for the execve(2) to resolve. The child holds the write end of a close-on-exec pipe. A successful execve(2) closes that end, and the parent reads end-of-file. A failure leaves a message in the pipe, and the parent returns it in error. Thus a command that does not exist reports its own reason at once.
run
run(%args) runs a command to completion and captures what it wrote.
These are the arguments:
cmd-
An array reference that holds the command and its arguments. This argument is necessary and must not be empty.
timeout-
The number of seconds to wait before the method stops the child. The default is no limit.
stdin-
A string to feed to the child on its standard input.
cwd-
A directory to run the child in. The child calls chdir(2) after the fork and before the execve(2), so the working directory of the caller does not change. A chdir(2) in the caller would change the meaning of every other relative path in the program, and a second call that ran at the same time would race it.
A directory that the child cannot enter is a startup failure with the reason in
error, not a silent run in the wrong place. passthrough-
Let the child write straight to the caller's terminal.
stdoutandstderrthen come back empty.
The method reads standard output and standard error at the same time. A reader that took them in sequence would deadlock: a child that fills one pipe blocks until someone drains it.
exit_code
exit_code($status) maps a raw waitpid(2) status, or the return value of system, to an exit code between 0 and 255. The low byte holds the terminating signal. The high byte holds the exit code. A value of -1 means the child never started.
A caller that gives a raw status to exit turns a remote exit code of 1 into exit(256), which the kernel truncates to 0. That silently reports a failed command as a success.
is_alive
is_alive($pid) reports if a process exists and is not a zombie. The check reaps a zombie child as a side effect and then reports it as not alive. A caller that needs the exit status uses run(), or waits itself.
terminate
terminate($pid, %args) sends SIGTERM, waits, and sends SIGKILL if the process continues to run.
These are the arguments:
grace_period-
The number of seconds to wait between the two signals. The default is 5.
on_kill-
A code reference that the method calls when the process is gone.
The wait polls with sub-second granularity. Thus a child that stops at once does not cost a whole second.
wait_exit
wait_exit($pid, $timeout) polls until the process exits or until the timeout ends. The default for $timeout is 30 seconds.
spawn_perl
spawn_perl(%args) runs Perl code in a child process. It gives the child the parent's -I paths. The parent gets these paths from -I, use lib or PERL5LIB. Thus the child sees the same modules.
code is the program text. args is an array reference of arguments for the program. The method gives all other arguments to spawn_command().
RETURN VALUES
spawn_command() and spawn_perl() return a hash reference. On success, the hash holds success set to 1 and pid. On failure, success is 0 and error gives the cause.
run() returns a hash reference that holds success, stdout, stderr, exit_code and timed_out. On a failure to start the child, it also holds error. success is 1 only when the child exited with code 0 and did not time out.
exit_code() returns a number between 0 and 255.
is_alive() returns 1 or 0.
terminate() returns 1 if the process is gone. It returns 0 if the process continues after SIGKILL.
wait_exit() returns 1 if the process exits in the timeout period. If not, it returns 0.
EXAMPLES
This example runs a helper and then stops it:
my $r = Fugu::Process->spawn_command(
cmd => [ 'mdnsctl', 'publish', $name, '_hap', 'tcp', $port ],
);
Fugu::Process->terminate($r->{pid}, grace_period => 10)
if $r->{success};
ERRORS
No method dies. The methods report failures through the hash reference or the boolean value that they return.
SEE ALSO
execve(2), kill(2), setsid(2), waitpid(2), Fugu::Log, Fugu::Pidfile
AUTHORS
Dick Olsson <hi@senzilla.io>
CAVEATS
run() holds the whole output of the child in memory. Do not use it for a command that writes without a bound.
is_alive() calls waitpid(2). That call reaps only the children of the caller. For all other processes, it uses kill(2) with signal 0. This signal cannot show the difference between a live process and a zombie.
The operating system uses process IDs again for new processes. The module cannot show the difference between the initial process and a later process with the same number.