NAME
Proc::Govern - Run child process and govern its various aspects
VERSION
This document describes version 0.214 of Proc::Govern (from Perl distribution Proc-Govern), released on 2024-07-17.
SYNOPSIS
To use as Perl module:
use Proc::Govern qw(govern_process);
my $exit_code = govern_process(
command => ['/path/to/myapp', 'some', 'args'], # required
name => 'myapp', # optional, default will be taken from command. must be alphanum only.
# options to control number of instances
single_instance => 1, # optional. if set to 1 will fail if another instance is already running.
# implemented with pid files.
pid_dir => "/var/run", # optional. defaults to /var/run. pid filename is '<name>.pid'
on_multiple_instance => "exit", # optional. can be set to 'exit' to silently exit when another instance
# is already running. otherwise prints an error msg.
# timeout options
timeout => 3600, # optional, default is no timeout
killfam => 1, # optional. can be set to 1 to kill using killfam.
# output logging options
log_stderr => { # optional, passed to File::Write::Rotate
dir => '/var/log/myapp',
size => '16M',
histories => 12,
},
log_stdout => { # optional, passed to File::Write::Rotate
dir => '/var/log/myapp.out',
size => '16M',
histories => 12,
},
log_combined => { # optional, passed to File::Write::Rotate
dir => '/var/log/myapp',
size => '16M',
histories => 12,
},
show_stdout => 0, # optional. can be set to 0 to suppress stdout output. note:
# stdout can still be logged even if not shown.
show_stderr => 0, # optional. can be set to 0 to suppress stderr output. note:
# stderr can still be logged even if not shown.
# load control options
load_watch => 1, # optional. can be set to 1 to enable load control.
load_high_limit => 5, # optional, default 1.25. at what load command should be paused? can also be set
# to a coderef that returns 1 when load is considered too high.
# note: just setting load_high_limit or load_low_limit won't automatically
# enable load control.
load_low_limit => 2, # optional, default 0.25. at what load paused command should be resumed? can also
# be set to a coderef that returns 1 when load is considered low already.
load_check_every => 20, # optional, default 10. frequency of load checking (in seconds).
# restart options
restart_if_failed => 1, # optional. if set to 1, will restart command if exit code is not zero.
restart_if_no_output_after => 60, # optional. if set to a positive number, will restart command after no
# stdout output after this many seconds
# screensaver control options
no_screensaver => 1, # optional. if set to 1, will prevent screensaver from being activated while command
# is running.
# power management options
no_sleep => 1, # optional. if set to 1, will prevent system from sleeping while command is running.
# this includes hybrid sleep, suspend, and hibernate.
# setuid options
euid => 1000, # optional. sets euid of command process. note: need to be root to be able to setuid.
egid => 1000, # optional. sets egid(s) of command process.
);
To use via command-line:
% govproc [options] <command>...
Example:
% govproc --timeout 86400 --load-watch --load-high 4 --load-low 0.75 backup-db
DESCRIPTION
Proc::Govern is a child process manager. It is meant to be a convenient bundle (a single parent/monitoring process) for functionalities commonly needed when managing a child process. It comes with a command-line interface, govproc.
Background story: I first created this module to record STDERR output of scripts that I run from cron. The scripts already log debugging information using Log::Any to an autorotated log file (using Log::Dispatch::FileRotate, via Log::Any::Adapter::Log4perl, via Log::Any::App). However, when the scripts warn/die, or when the programs that the scripts execute emit messages to STDERR, they do not get recorded. Thus, every script is then run through govproc. From there, govproc naturally gets additional features like timeout, preventing running multiple instances, and so on.
Currently the following governing functionalities are available:
logging of STDOUT & STDERR (or both) output to an autorotated file
execution time limit
set (CPU) nice level (priority)
preventing multiple instances from running simultaneously
load watch
autorestart
preventing system from sleeping while process is running
preventing screensaver from activating while process is running
In the future the following features are also planned or contemplated:
CPU time limit
memory limit
With an option to autorestart if process' memory size grow out of limit.
other resource usage limit
fork/start multiple processes
set I/O nice level (scheduling priority/class)
limit STDIN input, STDOUT/STDERR output?
trap/handle some signals for the child process?
provide daemon functionality?
provide network server functionality?
Inspiration: djb's tcpserver.
set/clean environment variables
EXIT CODES
Below is the list of exit codes that Proc::Govern uses:
124
Timeout. The exit code is also used by timeout.
202
Another instance is already running (when
single_instance
option is true).
FUNCTIONS
govern_process
Usage:
govern_process(%args) -> int
Run child process and govern its various aspects.
It basically uses IPC::Run and a loop to check various conditions during the lifetime of the child process.
TODO: restart_delay, check_alive.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
command* => array[str]
Command to run.
Passed to IPC::Run's
start()
.egid => str
Set EGID(s) of command process.
Need to be root to be able to setuid.
euid => unix::local_uid
Set EUID of command process.
Need to be root to be able to setuid.
killfam => bool
Instead of kill, use killfam (kill family of process).
This can be useful e.g. to control load more successfully, if the load-generating processes are the subchildren of the one we're governing.
This requires Proc::Killfam CPAN module, which is installed separately.
load_check_every => duration (default: 10)
Frequency of load checking (in seconds).
load_high_limit => int|code
Limit above which program should be suspended, if load watching is enabled. If integer, will be compared against Unix::Uptime
->load
's$load1
value. Alternatively, you can provide a custom routine here, code should return true if load is considered too high.Note:
load_watch
needs to be set to true first for this to be effective.load_low_limit => int|code
Limit below which program should resume, if load watching is enabled. If integer, will be compared against Unix::Uptime
->load
's$load1
value. Alternatively, you can provide a custom routine here, code should return true if load is considered low.Note:
load_watch
needs to be set to true first for this to be effective.load_watch => bool (default: 0)
If set to 1, enable load watching. Program will be suspended when system load is too high and resumed if system load returns to a lower limit.
log_combined => hash
Will be passed as arguments to `File::Write::Rotate`.
Specify logging for STDOUT and STDERR. Logging will be done using File::Write::Rotate. Known hash keys:
dir
(STR, defaults to/var/log
, directory, preferably absolute, where the log file(s) will reside, should already exist and be writable, will be passed to File::Write::Rotate's constructor),size
(int, also passed to File::Write::Rotate's constructor),histories
(int, also passed to File::Write::Rotate's constructor),period
(str, also passed to File::Write::Rotate's constructor).Instead of this option, you can also use
log_stdout
andlog_stderr
separately to log stdout and stderr to different directory.log_stderr => hash
Will be passed as arguments to `File::Write::Rotate`.
Specify logging for STDERR. Logging will be done using File::Write::Rotate. Known hash keys:
dir
(STR, defaults to/var/log
, directory, preferably absolute, where the log file(s) will reside, should already exist and be writable, will be passed to File::Write::Rotate's constructor),size
(int, also passed to File::Write::Rotate's constructor),histories
(int, also passed to File::Write::Rotate's constructor),period
(str, also passed to File::Write::Rotate's constructor).Instead of this option, you can also use
log_combined
to log both stdout and stderr to the same directory.log_stdout => hash
Will be passed as arguments to `File::Write::Rotate`.
Specify logging for STDOUT. Logging will be done using File::Write::Rotate. Known hash keys:
dir
(STR, defaults to/var/log
, directory, preferably absolute, where the log file(s) will reside, should already exist and be writable, will be passed to File::Write::Rotate's constructor),size
(int, also passed to File::Write::Rotate's constructor),histories
(int, also passed to File::Write::Rotate's constructor),period
(str, also passed to File::Write::Rotate's constructor).Instead of this option, you can also use
log_combined
to log both stdout and stderr to the same directory.name => str
Should match regex
\A\w+\z
. Used in several places, e.g. passed asprefix
in File::Write::Rotate's constructor as well as used as name of PID file.If not given, will be taken from command.
nice => int
Set nice/priority level.
no_screensaver => true
Prevent screensaver from being activated.
no_sleep => true
Prevent system from sleeping.
on_multiple_instance => str
Can be set to
exit
to silently exit when there is already a running instance. Otherwise, will print an error messageProgram <NAME> already running
.pid_dir => dirname
Directory to put PID file in.
restart_if_failed => bool
If set to true, do restart.
restart_if_no_output_after => uint
If set to positive number, restart when there is no output after this many seconds.
show_stderr => bool (default: 1)
Can be used to turn off STDERR output. If you turn this off and set
log_stderr
, STDERR output will still be logged but not displayed to screen.show_stdout => bool (default: 1)
Just like `show_stderr`, but for STDOUT.
single_instance => bool (default: 0)
If set to true, will prevent running multiple instances simultaneously. Implemented using Proc::PID::File. You will also normally have to set
pid_dir
, unless your script runs as root, in which case you can use the default/var/run
.timeout => duration
Apply execution time limit, in seconds.
After this time is reached, process (and all its descendants) are first sent the TERM signal. If after 30 seconds pass some processes still survive, they are sent the KILL signal.
The killing is implemented using IPC::Run's
kill_kill()
.Upon timeout, exit code is set to 124.
Return value: Child's exit code (int)
FAQ
Why use Proc::Govern?
The main feature this module offers is convenience: it creates a single parent process to monitor child process. This fact is more pronounced when you need to monitor lots of child processes. If you use, on the other hand, separate parent/monitoring process for timeout and then a separate one for CPU watching, and so on, there will potentially be a lot more processes running on the system. Compare for example:
% govproc --timeout 10 --load-watch CMD
which only creates one monitoring process, versus:
% timeout 10s loadwatch CMD
which will create two parent processes (three actually, loadwatch apparently forks first).
ENVIRONMENT
HOMEPAGE
Please visit the project's homepage at https://metacpan.org/release/Proc-Govern.
SOURCE
Source repository is at https://github.com/perlancar/perl-Proc-Govern.
SEE ALSO
Forks::Super (since 2009) extends the fork(), wait(), and waitpid() functions with some process/job management features, e.g. set timeout, retries, chdir, environment, umask, delay before start, control STDIN/STDOUT/STDERR, load control, priority/nice level, daemon, and a lot more. Had I known about this module, I probably wouldn't start Proc::Govern.
Proc::Govern uses IPC::Run at its core; you might want to use it directly. It already has some features, e.g. controlling STDIN/STDOUT/STDERR and timeout. But the main selling point of IPC::Run is its redirection and piping features.
IPC::Cmd is a core module, but can also use IPC::Run (as well as IPC::Open3 on systems that do not have IPC::Run installed or on some archaic systems that do not support IPC::Run). Its run_forked()
routine has some of Proc::Govern's functionalities like capturing stdout and stderr, timeout, hiding (discarding) output. If you only need those functionalities, you can use IPC::Cmd as it is a core module.
Proc::Govern attempts (or will attempt, some day) to provide the functionality (or some of the functionality) of the builtins/modules/programs listed below:
Starting/autorestarting
djb's supervise, http://cr.yp.to/daemontools/supervise.html
Pausing under high system load
run-stop-run from App::RunStopRun employs the same technique of stopping/resuming processes periodically but does not seem to monitor system load or CPU usage.
loadwatch. This program also has the ability to run N copies of program and interactively control stopping/resuming via Unix socket.
cPanel also includes a program called cpuwatch.
Preventing multiple instances of program running simultaneously
Execution time limit
timeout.
alarm() (but alarm() cannot be used to timeout external programs started by system()/backtick).
Logging
djb's multilog, http://cr.yp.to/daemontools/multilog.html
Although not really related, Perinci::Sub::Wrapper. This module also bundles functionalities like timeout, retries, argument validation, etc into a single function wrapper.
AUTHOR
perlancar
CONTRIBUTORS
perlancar <perlancar@cpan.org>
Steven Haryanto <stevenharyanto@gmail.com>
CONTRIBUTING
To contribute, you can send patches by email/via RT, or send pull requests on GitHub.
Most of the time, you don't need to build the distribution yourself. You can simply modify the code, then test via:
% prove -l
If you want to build the distribution (e.g. to try to install it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond that are considered a bug and can be reported to me.
COPYRIGHT AND LICENSE
This software is copyright (c) 2024, 2023, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
BUGS
Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Proc-Govern
When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.
CAVEATS
Not yet tested on Win32.