Apache-Watchdog-RunAway

 view release on metacpan or  search on metacpan

RunAway.pm  view on Meta::CPAN

    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 )