 
/* $Date: 2008-01-18 07:11:17 +0800 (Fri, 18 Jan 2008) $ $Id: www-mib-utils.c 8676 2008-01-17 23:11:17Z ispringer $ harrie@covalent.net
 */
/* General includes */
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
#include <limits.h>
#include <sys/socket.h>
 
/* Apache includes */
#include "httpd.h"
#include "http_conf_globals.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "scoreboard.h"

/* SNMP includes */
#include "ucd-snmp-config.h"
#include "asn1.h"
#include "snmp.h"
#include "snmp_impl.h"
#include "snmp_api.h"
#include "snmp_agent.h"
#include "snmp_vars.h"
#include "var_struct.h"
#include "ucd_snmpd.h"
#include "agent_trap.h"
#include "read_config.h"
#include "agent_read_config.h"
#include "default_store.h"
#include "snmp_debug.h"

#include "covalent-snmp-config.h"
#include "ietf-mibs/snmp-generic.h"
#include "covalent-snmp-logging.h"
#include "covalent-snmp.h"
#include "covalent-snmp-sconfig.h"

#include "www-mib.h"
#include "ietf-mibs/nsm-mib.h"

#include "status_codes.h"

#ifdef WWW_MIB
/* Is really define somewhere in a library */
extern int errno;
extern server_rec *www_services; /* www service records (covalent-snmp.c) */
extern int nr_www_services; /* total amount of www services (covalent-snmp.c */
int logpath_fd[2]; /* The 2 file descriptors for the pipe */
int logpath_socket; /* The socket descriptor for the logpath */
int logpath_address_size; /* The size of the address for the logpath */
struct sockaddr_in logpath_address; /* The address for the logpath */


int
get_responseType_index(int responseType)
{
int i;

    for (i = 0; (responseTypes[i] > 0) ; i++) {
        if(responseTypes[i] == responseType) {
            return(i);
        }
    }
    return(i); /* Use the last index as the place to dump unknown responseTypes. */
}

const char *
update_www_access_statistics(www_log_record *lastDoc)
{
wwwStatistics_t	*wwwStatistics;
#ifdef WWW_DOCUMENT_GROUP
int		lastNIndex;
wwwDocTopN_t	documentData;
wwwDocTopN_t	*ptr_documentData;
#endif /* WWW_DOCUMENT_GROUP */
int responseOutTypeArrayIndex;
int requestInTypeArrayIndex;

    wwwStatistics = get_www_statistics_record(lastDoc->wwwServiceIndex);
    if (wwwStatistics) {
	/* Fill some short cuts for the statsistics update */
	requestInTypeArrayIndex = lastDoc->wwwRequestInType;
	responseOutTypeArrayIndex = get_responseType_index(lastDoc->wwwResponseOutType);

	wwwStatistics->wwwSummaryInRequests++;
	wwwStatistics->wwwSummaryOutResponses++;
	wwwStatistics->wwwSummaryInLowBytes += lastDoc->wwwBytesIn;
	wwwStatistics->wwwSummaryOutLowBytes += lastDoc->wwwBytesOut;
#ifdef WWW_REQUEST_IN_GROUP
#ifdef SNMP_DEBUGGING
        fprintf(stderr, "Req type: %d\n", requestInTypeArrayIndex);
#endif
	if (lastDoc->wwwRequestInType >= 0){
	    wwwStatistics->wwwRequestIn[ requestInTypeArrayIndex ].requests++;
	    wwwStatistics->wwwRequestIn[ requestInTypeArrayIndex ].bytes += lastDoc->wwwBytesIn;
	    wwwStatistics->wwwRequestIn[ requestInTypeArrayIndex ].lastTime = lastDoc->wwwRequestTime;
	}
#endif /* WWW_REQUEST_IN_GROUP */
#ifdef WWW_RESPONSE_OUT_GROUP
	wwwStatistics->wwwResponseOut[ responseOutTypeArrayIndex ].responses++;
	wwwStatistics->wwwResponseOut[ responseOutTypeArrayIndex ].bytes += lastDoc->wwwBytesOut;
	wwwStatistics->wwwResponseOut[ responseOutTypeArrayIndex ].lastTime = lastDoc->wwwRequestTime;
#endif /* WWW_RESPONSE_OUT_GROUP */
#ifdef WWW_DOCUMENT_GROUP
	if (wwwStatistics->wwwDocLastNTable) {
	    wwwStatistics->wwwDocLastNIndex++;
	    lastNIndex = wwwStatistics->wwwDocLastNIndex % MAX_WWWDOCLASTNSIZE;
	    strncpy(wwwStatistics->wwwDocLastNTable[ lastNIndex ].name, lastDoc->wwwDocName, MAX_WWWDOCNAME);
	    wwwStatistics->wwwDocLastNTable[ lastNIndex ].timestamp = lastDoc->wwwRequestTime;
	    wwwStatistics->wwwDocLastNTable[ lastNIndex ].requestType = lastDoc->wwwRequestInType;
	    wwwStatistics->wwwDocLastNTable[ lastNIndex ].responseType = lastDoc->wwwResponseOutType;
	    strncpy(wwwStatistics->wwwDocLastNTable[ lastNIndex ].statusMsg, lastDoc->wwwStatusMsg, MAX_WWWDOCLASTNSTATUSMSG);
	    wwwStatistics->wwwDocLastNTable[ lastNIndex ].bytes = lastDoc->wwwBytesOut;
	}
	ptr_documentData = covalent_snmp_wwwDocBucket_fetch(wwwStatistics->wwwDocBucket, lastDoc->wwwDocName);
	if (ptr_documentData) {
#ifdef SNMP_DEBUGGING
	    fprintf(stderr, "Document found: '%s' - '%d'\n", lastDoc.wwwDocName, ptr_documentData->accesses);
#endif
	    /* found in bucket; update document document data */
	    ptr_documentData->accesses++;
	    ptr_documentData->bytes += lastDoc->wwwBytesOut;
	    ptr_documentData->lastResponseType = responseOutTypeArrayIndex;
	    if (covalent_snmp_wwwDocBucket_store(wwwStatistics->wwwDocBucket, lastDoc->wwwDocName,
	    			ptr_documentData, RECORD_REPLACE)) {
	    	return(covalent_snmp_wwwDocBucket_strerror());
	    }
	} else {
#ifdef SNMP_DEBUGGING
	    fprintf(stderr, "Document not found: '%s'\n", lastDoc.wwwDocName);
#endif
	    /* not found in bucket; insert document data */
	    strcpy(documentData.name, lastDoc->wwwDocName);
	    documentData.accesses = 1;
	    documentData.bytes = lastDoc->wwwBytesOut;
	    documentData.lastResponseType = responseOutTypeArrayIndex;
	    if (covalent_snmp_wwwDocBucket_store(wwwStatistics->wwwDocBucket, lastDoc->wwwDocName, &documentData,
	        					RECORD_INSERT)) {
	    	return(covalent_snmp_wwwDocBucket_strerror());
	    }
	}
#endif /* WWW_DOCUMENT_GROUP */
    }
    return(NULL);
}

#ifdef WWW_DOCUMENT_GROUP
void
insert_in_accessTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *accessTopNTable, int size)
{
int	i;

#ifdef SNMP_DEBUGGING
    fprintf(stderr, "doc accesses %d\n", doc->accesses);
#endif
    size--;
    for ( i=0 ; i<size ; i++) {
#ifdef SNMP_DEBUGGING
    fprintf(stderr, "topN(%d) accesses %d\n", i, accessTopNTable[i].accesses);
#endif
	if (accessTopNTable[i].accesses <= doc->accesses) {
#ifdef SNMP_DEBUGGING
	    fprintf(stderr, "Moving %d records index %d size %d\n", (size - i), i, size);
	    fprintf(stderr, "record %d to %d\n", i, i + 1);
#endif
	    memmove(&(accessTopNTable[i+1]), &(accessTopNTable[i]), sizeof(wwwDocTopN_t) * (size - i)); 
	    memcpy(&(accessTopNTable[i]), doc, sizeof(wwwDocTopN_t));
	    return;
	}
    }
    if (accessTopNTable[i].accesses <= doc->accesses) {
#ifdef SNMP_DEBUGGING
	fprintf(stderr, "Filling record %d\n", i);
#endif
	memcpy(&(accessTopNTable[i]), doc, sizeof(wwwDocTopN_t));
    }
}

void
insert_in_bytesTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *bytesTopNTable, int size)
{
int     i;

#ifdef SNMP_DEBUGGING
    fprintf(stderr, "doc bytes %d\n", doc->bytes);
#endif
    size--;
    for ( i=0 ; i<size ; i++) {
#ifdef SNMP_DEBUGGING
    fprintf(stderr, "topN(%d) bytes %d\n", i, bytesTopNTable[i].bytes);
#endif
	if (bytesTopNTable[i].bytes <= doc->bytes) {
#ifdef SNMP_DEBUGGING
	    fprintf(stderr, "Moving %d records index %d size %d\n", (size - i), i, size);
	    fprintf(stderr, "record %d to %d\n", i, i + 1);
#endif
	    memmove(&(bytesTopNTable[i+1]), &(bytesTopNTable[i]), sizeof(wwwDocTopN_t) * (size - i)); 
	    memcpy(&(bytesTopNTable[i]), doc, sizeof(wwwDocTopN_t));
	    return;
	}
    }
    if (bytesTopNTable[i].bytes <= doc->bytes) {
#ifdef SNMP_DEBUGGING
	fprintf(stderr, "Filling record %d\n", i);
#endif
	memcpy(&(bytesTopNTable[i]), doc, sizeof(wwwDocTopN_t));
    }
}

void
set_alarm_bucketsTimeInterval(int index)
{   
wwwStatistics_t *wwwStatistics = get_www_statistics_record(index);

    if (wwwStatistics) {
	if (wwwStatistics->wwwDocCtrlBucketTimeInterval > 0) {
	    alarm(wwwStatistics->wwwDocCtrlBucketTimeInterval);
	}       
    }           
}               

void
compute_wwwDocTopNTable(wwwStatistics_t *wwwStatistics)
{
wwwDocBucket_t	*wwwDocBucket;
wwwDocTopN_t	*documentData;

    wwwDocBucket = &(wwwStatistics->wwwDocBucketTable[++wwwStatistics->wwwDocBucketIndex % 
						MAX_WWWDOCBUCKETS ]);;
    if (wwwDocBucket) {
	wwwDocBucket->timestamp = time(NULL);
	wwwDocBucket->accesses = 0;
	wwwDocBucket->documents = 0;
	wwwDocBucket->bytes = 0;
	if (wwwDocBucket->accessTopNTable) {
	    memset(wwwDocBucket->accessTopNTable, 0,
		(sizeof(wwwDocTopN_t) * MAX_WWWDOCLASTNSIZE));
	}
	if (wwwDocBucket->bytesTopNTable) {
	    memset(wwwDocBucket->bytesTopNTable, 0,
		(sizeof(wwwDocTopN_t) * MAX_WWWDOCLASTNSIZE));
	}
	/* We now go through the complete bucket and sort the documentData */
	documentData = covalent_snmp_wwwDocBucket_seqfetch(wwwStatistics->wwwDocBucket, FIRST_RECORD);
	while (documentData) {
	    insert_in_accessTopNTable(documentData, wwwDocBucket->accessTopNTable,
					wwwStatistics->wwwDocCtrlTopNSize);
	    insert_in_bytesTopNTable(documentData, wwwDocBucket->bytesTopNTable,
					wwwStatistics->wwwDocCtrlTopNSize);
	    wwwDocBucket->accesses += documentData->accesses;
	    wwwDocBucket->documents++;
	    wwwDocBucket->bytes += documentData->bytes;
	    documentData = covalent_snmp_wwwDocBucket_seqfetch(wwwStatistics->wwwDocBucket, NEXT_RECORD);
	}
#ifdef SNMP_DEBUGGING
	fprintf(stderr, "Nr of docs %d\n", wwwDocBucket->documents);
#endif
    }
}

/* This function walks through to compute all bucket related tables.
 * It does them always together, but it should be possible to do this
 * independent.
 */
void
compute_wwwDocTopNTables(int signal)
{
int i;
wwwStatistics_t *www_statistics;

    set_alarm_bucketsTimeInterval(0);
    for(i = 0; i < nr_www_services; i++) {
        www_statistics = get_www_statistics_record(0);
	compute_wwwDocTopNTable(www_statistics);
	covalent_snmp_wwwDocBucket_close(www_statistics->wwwDocBucket);
	www_statistics->wwwDocBucket = covalent_snmp_wwwDocBucket_open(www_statistics->wwwDocBucketName);
	if (!www_statistics->wwwDocBucket) {
	    ap_log_unixerr("SNMP: covalent_snmp_wwwDocBucket_open", NULL, covalent_snmp_wwwDocBucket_strerror(), www_services);
	}
    }
}

void
init_alarm_bucketsTimeInterval()
{
#ifndef NO_USE_SIGACTION
struct sigaction snmp_sig;

    sigemptyset(&snmp_sig.sa_mask);
    snmp_sig.sa_flags = 0;
    snmp_sig.sa_handler = (void (*)())compute_wwwDocTopNTables;
    if (sigaction (SIGALRM, &snmp_sig, NULL) < 0)
	ap_log_error (APLOG_MARK, (APLOG_ERR|APLOG_NOERRNO), www_services,
                        "sigaction(SIGALRM)");
    sigaddset (&snmp_sig.sa_mask, SIGALRM);
#else
    signal (SIGALRM, (void (*)())compute_wwwDocTopNTables);
#endif
}
#endif /* WWW_DOCUMENT_GROUP */
#endif /* WWW_MIB */
