#!/usr/bin/perl
#

package Apache::SendStats;

BEGIN {
    $LJ::HAVE_INLINE = eval q{
        use Inline (C => 'DATA',
                    DIRECTORY => $ENV{LJ_INLINE_DIR} ||"$ENV{'LJHOME'}/Inline",
                    );
        1;
    };
}
use strict;
use IO::Socket::INET;
use Apache::Constants qw(:common);

if ($LJ::HAVE_INLINE && $LJ::FREECHILDREN_BCAST) {
    eval {
        Inline->init();
    };
    if ($@ && ! $LJ::JUST_COMPILING) {
        print STDERR "Warning: You seem to have Inline.pm, but you haven't run \$LJHOME/bin/lj-inline.pl.  " .
            "Continuing without it, but stats won't broadcast.\n";
        $LJ::HAVE_INLINE = 0;
    }
}

use vars qw(%udp_sock);

sub handler
{
    my $r = shift;
    return OK if $r->main;
    return OK unless $LJ::HAVE_INLINE && $LJ::FREECHILDREN_BCAST;

    my $callback = $r->current_callback() if $r;
    my $cleanup = $callback eq "PerlCleanupHandler";
    my $childinit = $callback eq "PerlChildInitHandler";

    if ($LJ::TRACK_URL_ACTIVE)
    {
	my $key = "url_active:$LJ::SERVER_NAME:$$";
	if ($cleanup) {
	    LJ::MemCache::delete($key);
	} else {
	    LJ::MemCache::set($key, $r->uri . "(" . $r->method . "/" . scalar($r->args) . ")");
	  }
    }

    my ($active, $free) = count_servers();

    $free += $cleanup;
    $free += $childinit;
    $active -= $cleanup if $active;

    my $list = ref $LJ::FREECHILDREN_BCAST ?
        $LJ::FREECHILDREN_BCAST : [ $LJ::FREECHILDREN_BCAST ];

    foreach my $host (@$list) {
        next unless $host =~ /^(\S+):(\d+)$/;
        my $bcast = $1;
        my $port = $2;
        my $sock = $udp_sock{$host};
        unless ($sock) {
            $udp_sock{$host} = $sock = IO::Socket::INET->new(Proto => 'udp');
            if ($sock) {
                $sock->sockopt(SO_BROADCAST, 1);
            } else {
                $r->log_error("SendStats: couldn't create socket: $host");
                next;
            } 
        }

        my $ipaddr = inet_aton($bcast);
        my $portaddr = sockaddr_in($port, $ipaddr);
        my $message = "bcast_ver=1\nfree=$free\nactive=$active\n";
        my $res = $sock->send($message, 0, $portaddr);
        $r->log_error("SendStats: couldn't broadcast") 
            unless $res;
    }

    return OK;
}

1;

__DATA__
__C__

extern unsigned char *ap_scoreboard_image;

/* 
 * the following structure is for Linux on i32 ONLY! It makes certan
 * choices where apache's scoreboard.h has #ifdef's. See scoreboard.h
 * for real declarations, here we only name a few things we actually need.
 */

/* total length of struct should be 164 bytes */
typedef struct {
    int foo1;
    short foo2;
    unsigned char status;
    int foo3[39];
} short_score;

/* length should be 16 bytes */
typedef struct {
    int pid;
    int foo[3];
} parent_score;

static int hard_limit = 512; /* array size on debian */

/* 
 * Scoreboard is laid out like this: array of short_score structs,
 * then array of parent_score structs, then one int, the generation
 * number. Both arrays are of size HARD_SERVERS_LIMIT, 256 by default
 * on Unixes.
 */


void count_servers() {
    int i, count_free, count_active;
    short_score *ss;
    parent_score *ps;
    Inline_Stack_Vars;

    ss = (short_score *)ap_scoreboard_image;
    ps = (parent_score *) ((unsigned char *)ap_scoreboard_image + sizeof(short_score)*hard_limit);

    count_free = 0; count_active = 0;
    for (i=0; i<hard_limit; i++) {
        if(ss[i].status == 2)  /* READY */
            count_free++;
        if(ss[i].status > 2)   /* busy doing something */
            count_active++;
    }
    Inline_Stack_Reset;
    Inline_Stack_Push(newSViv(count_active));
    Inline_Stack_Push(newSViv(count_free));
    Inline_Stack_Done;
  
    return;
}


    
