=head1 NAME AnyEvent::Watchdog::Util - watchdog control and process management =head1 SYNOPSIS use AnyEvent::Watchdog::Util; =head1 DESCRIPTION This module can control the watchdog started by using L in your main program, but it has useful functionality even when not running under the watchdog at all, such as program exit hooks. =head1 VARIABLES/FUNCTIONS The module supports the following variables and functions: =over 4 =cut package AnyEvent::Watchdog::Util; # load modules we will use later anyways use common::sense; use AnyEvent (); use Carp (); our $VERSION = '1.0'; our $C; BEGIN { *C = \$AnyEvent::Watchdog::C; } our $AUTORESTART; our $HEARTBEAT_W; =item AnyEvent::Watchdog::Util::enabled Return true when the program is running under the regime of AnyEvent::Watchdog, false otherwise. AnyEvent::Watchdog::Util::enabled or die "watchdog not enabled..."; AnyEvent::Watchdog::Util::restart; Note that if it returns defined, but false, then AnyEvent::Watchdog is running, but you are in the watchdog process - you probably did something very wrong in this case. =cut sub enabled() { $AnyEvent::Watchdog::ENABLED } =item AnyEvent::Watchdog::Util::restart_in [$timeout] Tells the supervisor to restart the process when it exits (enable autorestart), or forcefully after C<$timeout> seconds (minimum 1, maximum 255, default 60). This function disables the heartbeat, if it was enabled. Also, after calling this function the watchdog will ignore any further requests until the program has restarted. Good to call before you intend to exit, in case your clean-up handling gets stuck. =cut sub restart_in(;$) { my ($timeout) = @_; return unless $C; undef $HEARTBEAT_W; $timeout = 60 unless defined $timeout; $timeout = 1 if $timeout < 1; $timeout = 255 if $timeout > 255; syswrite $C, "\x01\x02" . chr $timeout; # now make sure we dont' send it any further requests our $OLD_C = $C; undef $C; } =item AnyEvent::Watchdog::Util::restart [$timeout] Just like C, but also calls C. This means that this is the ideal method to force a restart. =cut sub restart(;$) { &restart_in; exit 0; } =item AnyEvent::Watchdog::Util::autorestart [$boolean] =item use AnyEvent::Watchdog autorestart => $boolean Enables or disables autorestart (initially disabled, default for C<$boolean> is to enable): By default, the supervisor will exit if the program exits or dies in any way. When enabling autorestart behaviour, then the supervisor will try to restart the program after it dies. Note that the supervisor will never autorestart when the child died with SIGINT or SIGTERM. =cut sub autorestart(;$) { my $AUTORESTART = !@_ || $_[0]; return unless $C; unless (enabled) { warn "AnyEvent::Watchdog: watchdog not running, cannot enable autorestart, ignoring.\n" if $AUTORESTART; $AUTORESTART = 0; return; } syswrite $C, $AUTORESTART ? "\x01" : "\x00"; } =item AnyEvent::Watchdog::Util::heartbeat [$interval] =item use AnyEvent::Watchdog heartbeat => $interval Tells the supervisor to automatically kill the program if it doesn't react for C<$interval> seconds (minium 1, maximum 255, default 60) , then installs an AnyEvent timer the sends a regular heartbeat to the supervisor twice as often. Exit behaviour isn't changed, so if you want a restart instead of an exit, you have to call C. The heartbeat frequency can be changed as often as you want, an interval of C<0> disables the heartbeat check again. =cut sub heartbeat(;$) { my ($interval) = @_; unless (enabled) { warn "AnyEvent::Watchdog: watchdog not running, cannot enable heartbeat, ignoring.\n"; return; } $interval = 60 unless defined $interval; $interval = 0 if $interval < 0; $interval = 255 if $interval > 255; $interval = int $interval; syswrite $C, "\x03" . chr $interval if $C; $HEARTBEAT_W = AE::timer 0, $interval * 0.5, sub { syswrite $C, "\x04" if $C; }; } =item AnyEvent::Watchdog::Util::on_exit { BLOCK; shift->() } Installs an exit hook that is executed when the program is about to exit, while event processing is still active to some extent. The hook should do whatever it needs to do (close active connections, disable listeners, write state, free resources etc.). When it is done, it should call the code reference that has been passed to it. This means you can install event handlers and return from the block, and the program will not exit until the callback is invoked. Exiting "the right way" is surprisingly difficult. This is what C does: It installs watchers for C, C, C and C, and well as an C block (the END block is actually registered in L, if possible, so it executes as late as possible). The signal handlers remember the signal and then call C, invoking the C callback. The END block then checks for an exit code of C<255>, in which case nothing happens (C<255> is the exit code that results from a program error), otherwise it runs all C hooks and waits for their completion using the event loop. After all C hooks have finished, the program will either be Ced with the relevant status code (if C was the cause for the program exit), or it will reset the signal handler, unblock the signal and kill itself with the signal, to ensure that the exit status is correct. If the program is running under the watchdog, and autorestart is enabled, then the heartbeat is disabled and the watchdog is told that the program wishes to exit within C<60> seconds, after which it will be forcefully killed. All of this should ensure that C hooks are only executed when the program is in a sane state and data structures are still intact. This only works when the program does not install its own TERM (etc.) watchers, of course, as there is no control over them. There is currently no way to unregister C hooks. =cut our @ON_EXIT; our %SIG_W; our $EXIT_STATUS; # >= 0 exit status; arrayref => signal, undef if exit was just called # in case AnyEvent::Watchdog is not loaded, use our own END block END { $AnyEvent::Watchdog::end && &$AnyEvent::Watchdog::end } sub _exit { $EXIT_STATUS = $? unless defined $EXIT_STATUS; # we might have two END blocks trying to call us. undef $AnyEvent::Watchdog::end; if (enabled) { undef $HEARTBEAT_W; restart_in 60; } my $cv = AE::cv; my $cb = sub { $cv->end }; $cv->begin; while (@ON_EXIT) { $cv->begin; (pop @ON_EXIT)->($cb); } $cv->end; $cv->recv; if (ref $EXIT_STATUS) { # signal # reset to default, hopefully this overrides any C-level handlers $SIG{$EXIT_STATUS->[0]} = 'DEFAULT'; eval { # try to unblock require POSIX; my $set = POSIX::SigSet->new; $set->addset ($EXIT_STATUS->[1]); POSIX::sigprocmask (POSIX::SIG_UNBLOCK (), $set); }; # now raise the signal kill $EXIT_STATUS->[1], $$; # well, if we can't force it even now, try exit 255 $? = 255; } else { # exit status $? = $EXIT_STATUS; } } sub on_exit(&) { unless ($AnyEvent::Watchdog::end) { $AnyEvent::Watchdog::end = \&_exit; push @ON_EXIT, $_[0]; for my $signal (qw(TERM INT XFSZ XCPU)) { my $signum = AnyEvent::Base::sig2num $signal or next; $SIG_W{$signum} = AE::signal $signal => sub { $EXIT_STATUS = [$signal => $signum]; exit 124; }; } } } =back =head1 SEE ALSO L. =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ =cut 1