Apache-Watchdog-RunAway
view release on metacpan or search on metacpan
defined (my $watchdog_pid = fork) or die "Cannot fork: $!\n";
if ($watchdog_pid) {
warn "detached monitor pid $watchdog_pid started\n"
if $Apache::Watchdog::RunAway::DEBUG;
return $watchdog_pid;
}
else {
start_monitor();
CORE::exit();
}
}
#################
sub start_monitor {
# 0 means don't monitor
return unless $Apache::Watchdog::RunAway::TIMEOUT;
# handle the case where apache restarts itself, either on start or
# with PerlFresh ON... this is a closure to protect this variable
# from user. it's inaccessable outside of this module
return if is_running();
# The forked process is supposed to run as long as main process
# runs, so we don't care about wait()
warn "$0: spawned a monitor process $$\n"
if $Apache::Watchdog::RunAway::DEBUG;
# create a lock file
lock();
# neverending loop
while (1) {
monitor();
debug "sleeping $Apache::Watchdog::RunAway::POLLTIME";
sleep $Apache::Watchdog::RunAway::POLLTIME;
}
}
my $pool;
# the real code that does all the accounting and killings
############
sub monitor {
die "\$Apache::Watchdog::RunAway::SCOREBOARD_URL is not set"
unless $Apache::Watchdog::RunAway::SCOREBOARD_URL;
my @args = ($Apache::Watchdog::RunAway::SCOREBOARD_URL);
if (MP2) {
# mp's Apache::Scoreboard::fetch needs a pool arg
$pool = APR::Pool->new;
unshift @args, $pool;
}
my $image = Apache::Scoreboard->fetch(@args);
unless ($image){
# reset the counters and timers
%req_proc_time = ();
%req_number = ();
debug "couldn't retrieve the scoreboard image ",
"from $Apache::Watchdog::RunAway::SCOREBOARD_URL";
return;
}
for (my $i = 0; $i < $image->server_limit; $i++) {
my $parent_score = MP2 ? $image->parent_score($i) : $image->servers($i);
next unless $parent_score;
my $pid = MP2 ? $parent_score->pid : $image->parent($i)->pid;
last unless $pid;
my $worker_score = MP2 ? $parent_score->worker_score : $parent_score;
# we care only about processes that in 'W' status
# processing. (W means 'writing to a client')
next unless $worker_score->status eq 'W';
# init if it's uninitialized (to non existant -1 count)
# can't use ||= construct as a value can be 0...
$req_number{$pid} = -1
unless exists $req_number{$pid};
# make sure the proc time is initialized
$req_proc_time{$pid} ||= 0;
my $count = $worker_score->my_access_count;
debug "OK: $i $pid ",
$worker_score->status, " $count ",
$req_proc_time{$pid}, " ",
$req_number{$pid};
if ($count == $req_number{$pid}) {
# the same request is still being processed
if ($req_proc_time{$pid} > $Apache::Watchdog::RunAway::TIMEOUT) {
my $error = <<EOT;
Killing httpd process $pid which is running for $req_proc_time{$pid} secs,
which is longer than $Apache::Watchdog::RunAway::TIMEOUT secs limit.
EOT
if ($Apache::Watchdog::RunAway::VERBOSE) {
$error .= 'It was handling [' . $worker_score->request() .
'] for [' . $worker_score->client() . "]\n";
}
log_error $error;
# META: should I kill or just send a SIGPIPE to a hanging process?
kill 9, $pid;
} else {
#warn "o0o\n";
# Note: this is not true processing time, since there is a
# work done between sleeps, but it takes less than 1 second
$req_proc_time{$pid} += $Apache::Watchdog::RunAway::POLLTIME;
}
( run in 2.100 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )