Proc::tored - Service management using a pid file and touch files
version 0.17
use Proc::tored; use Getopt::Long; my %opt = ( pause => 0, resume => 0, stop => 0, zap => 0, start => 0, run => 0, ); GetOptions( 'pause' => \$opt{pause}, 'resume' => \$opt{resume}, 'stop' => \$opt{stop}, 'zap' => \$opt{zap}, 'start' => \$opt{start}, 'run' => \$opt{run}, ); my $service = service 'stuff-doer', in '/var/run'; my $pid = running $service; print "Running service found with pid $pid\n" if $pid; if ($opt{pause}) { # Set the paused flag, causing a running service to block until unset pause $service; } elsif ($opt{resume}) { # Unset the paused flag, unblocking any running service resume $service; } elsif ($opt{stop}) { # Set the stopped state, preventing new processes from starting the service # and causing running processes to self-terminate stop $service; } elsif ($opt{zap}) { # Terminate a running process, timing out after 15s zap $service, 15 or die "stuff_doer $pid is being stubborn"; } elsif ($opt{start}) { # Allow the service to start running again start $service; } if ($opt{run}) { # Run service (if not stopped) run { do_stuff() } $service; }
A Proc::tored service is voluntarily managed by a pid file and touch files.
Proc::tored
Proc::tored services are specified with a name and a path. Any services created using the same name and path are considered the same service and will be aware of other processes via their "PID FILE" and respect service control "FLAGS".
All routines are exported by default.
A proctored service is defined using the service function. The name given to the service is used in the naming of various files used to control the service (e.g., pid file and touch files). The in function is used to specify the local directory where these files will be created and looked for. Signals may be trapped using trap on non-MSWin32 systems.
service
in
trap
MSWin32
my $service = service 'name-of-service', in '/var/run', trap ['TERM', 'INT'];
Reads and returns the contents of the pid file. Does not check to determine whether the pid is valid. Returns 0 if the pid file is not found or is empty.
printf "service may be running under pid %d", pid $service;
Reads and returns the contents of the pid file after checking that the process identified still exists. Essentially the same as kill(0, pid $service). Returns 0 if the pid is not found or cannot be signalled.
kill(0, pid $service)
if (my $pid = running $service) { warn "service is already running under pid $pid"; }
Begins the service in the current process. The service, specified as a code block, will be called until it returns false or the "stopped" flag is set.
If the "paused" flag is set, the loop will continue to run without executing the code block until it has been "resume"d. If the "paused" flag is set at the time run is called, the loop will start but will not begin executing the code block until the flag is cleared.
run
If the "stopped" flag is set, the loop will terminate at the completion of the current iteration. If the "stopped" flag is set at the time run is called, run will return false immediately. The behavior under "stopped" takes priority over that of "paused".
my $started = time; my $max_run_time = 300; run { if (time - $started > $max_run_time) { warn "Max run time ($max_run_time seconds) exceeded\n"; warn " -shutting down\n"; return 0; } else { do_some_work(); } return 1; } $service;
Sets the "stopped" flag (see "stop"), then blocks until a running service exits. Returns immediately (after setting the "stopped" flag) if the "running" service is the current process.
sub stop_service { if (my $pid = running $service) { print "Attempting to stop running service running under process $pid\n"; if (zap $pid, 30) { print " -Service shut down\n"; return 1; } else { print " -Timed out before service shut down\n"; return 0; } } }
Controls and inspects the "stopped" flag for the service.
# Stop a running service if (!stopped $service && running $service) { stop $service; } do_work_while_stopped(); # Allow service to start # Note that this does not launch the service process. It simply clears the # "stopped" flag that would have prevented it from running again. start $service;
Controls and inspects the "paused" flag for the service. In general, this should never be done inside the "run" loop (see the warning in "Pause and resume").
# Pause a running service # Note that the running service will not exit. Instead, it will stop # executing its main loop until the "paused" flag is cleared. if (!paused $service && running $service) { pause $service; } do_work_while_paused(); # Allow service to resume execution resume $service;
A pid file is used to identify a running service. While the service is running, barring any outside interference, the pid will contain the pid of the running process and a newline. After the service process stops, the pid file will be truncated. The file will be located in the directory specified by "in". Its name is the concatenation of the service name and ".pid".
Service control flags are persistent until unset. Their status is determined by the existence of a touch file.
A touch file indicating that a running service should self-terminate and that new processes should not start is created with "stop" and removed with "start". It is located in the directory specified by "in". Its name is the concatenation of the service name and ".stopped".
A touch file indicating that a running service should temporarily stop executing and that new processes should start but not yet execute any service code is created with "pause" and removed with "resume". It is located in the directory specified by "in". Its name is the concatenation of the service name and ".paused".
When a service is "paused", the code block passed to "run" is no longer executed until something calls "resume". This can lead to deadlock if there is no external actor willing to "resume" the service.
For example, this service will never resume:
run { my $empty = out_of_tasks(); if ($empty) { pause $service; } elsif (paused $service && !$empty) { # This line is never reached because this code block is no longer # executed after being paused above. resume $service; } do_next_task(); return 1; } $service;
In most cases, pausing and resuming a service should be handled from outside of "run".
Jeff Ober <jeffober@gmail.com>
This software is copyright (c) 2017 by Jeff Ober.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install Proc::tored, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Proc::tored
CPAN shell
perl -MCPAN -e shell install Proc::tored
For more information on module installation, please visit the detailed CPAN module installation guide.