/*
 * This file implements the NETWORK-SERVICES-MIB module.
 * $Id: nsm-mib.c 8676 2008-01-17 23:11:17Z ispringer $
 */

#include <stdio.h>
#include <sys/types.h>
#include <string.h>

/* Apache includes */
#include <httpd.h>
#include <http_config.h>
#include <scoreboard.h>
#include <ap_mpm.h>
#include <apr_optional.h>

/* SNMP includes */
#include <ucd-snmp-config.h>
#include <asn1.h>
#include <snmp.h>
#include <snmp_api.h>
#include <snmp_impl.h>
#include <snmp_debug.h>
#include <snmp_vars.h>
#include <var_struct.h>

/* module includes */
#include "covalent-snmp-config.h"
#include "snmpcommon/snmpcommon.h"
#include "mibs-ietf/nsm-mib.h"

#define APPLNAME                         2
#define APPLDIRECTORYNAME                3
#define APPLVERSION                      4
#define APPLUPTIME                       5
#define APPLOPERSTATUS                   6
#define APPLLASTCHANGE                   7
#define APPLINBOUNDASSOCIATIONS          8
#define APPLOUTBOUNDASSOCIATIONS         9
#define APPLACCUMULATEDINBOUNDASSOCIATIONS 10
#define APPLACCUMULATEDOUTBOUNDASSOCIATIONS 11
#define APPLLASTINBOUNDACTIVITY          12
#define APPLLASTOUTBOUNDACTIVITY         13
#define APPLREJECTEDINBOUNDASSOCIATIONS  14
#define APPLFAILEDOUTBOUNDASSOCIATIONS   15
#define APPLDESCRIPTION                  16
#define APPLURL                          17

#define ASSOCREMOTEAPPLICATION           2
#define ASSOCAPPLICATIONPROTOCOL         3
#define ASSOCAPPLICATIONTYPE             4
#define ASSOCDURATION                    5

#define is_active(p) (((p == SERVER_BUSY_READ) || \
			(p == SERVER_BUSY_WRITE) || \
			(p == SERVER_BUSY_KEEPALIVE) || \
			(p == SERVER_BUSY_LOG) || \
			(p == SERVER_BUSY_DNS)) ? 1 : 0)

/* Global/static variables. */
#ifdef NSM_APPL_GROUP
static char *applName = "Apache HTTP server";
static char *applDescr = "World Wide Web application (server-side)";
static char *applUrl = "http://www.apache.org/";

static unsigned char *
read_applEntry(struct variable *vp,
    oid *name, size_t *length,
    int exact, size_t *var_len, WriteMethod **write_method)
{
int result;
int i;
int j;
int hard_server_limit;
int hard_thread_limit;
worker_score *worker_info;
/* The time still needs to be fixed.
#ifndef NO_GETTIMEOFDAY
unsigned long stopTime;
#endif
*/

    vp->name[ vp->namelen++ ] = (oid)get_appl_index();
    result = snmp_oid_compare(name, *length, vp->name, vp->namelen);
    if (!(exact && (result == 0)) && !(!exact && (result < 0))) {
	return(NULL);
    }
	
    memcpy((char *)name, (char *)vp->name, vp->namelen * sizeof(oid));
    *length = vp->namelen;
    write_method = 0;
    *var_len = sizeof(long_return);

    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &hard_server_limit);
    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &hard_thread_limit);

    switch (vp->magic) {
	case APPLNAME:
	    *var_len = strlen(applName);
	    return (unsigned char *) applName;
	case APPLDIRECTORYNAME:
	    strcpy(return_buf, "");
	    *var_len = strlen(return_buf);
	    return (unsigned char *) return_buf;
	case APPLVERSION:
	    strcpy(return_buf, ap_get_server_version());
	    *var_len = strlen(return_buf);
	    return (unsigned char *) return_buf;
	case APPLUPTIME:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLOPERSTATUS:
	    long_return = 1;
	    return (unsigned char *) &long_return;
	case APPLLASTCHANGE:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLINBOUNDASSOCIATIONS:
	    long_return = 0;
	    for (i = 0; i < hard_server_limit; i++) {
		for (j = 0; j < hard_thread_limit; j++) {
                    worker_info = ap_get_scoreboard_worker(i, j);
                    if (worker_info) {
		        if (is_active(worker_info->status)) {
		            long_return++;
                        }
                    }
		} 
	    }
	    return (unsigned char *) &long_return;
	case APPLOUTBOUNDASSOCIATIONS:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLACCUMULATEDINBOUNDASSOCIATIONS:
	    long_return = 0;
	    for (i = 0; i < hard_server_limit; i++) {
                for (j = 0; j < hard_thread_limit; j++) {
                    worker_info = ap_get_scoreboard_worker(i, j);
                    if (worker_info) {
                        long_return += worker_info->conn_count;
                    }
                }
	    }
	    return (unsigned char *) &long_return;
	case APPLACCUMULATEDOUTBOUNDASSOCIATIONS:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLLASTINBOUNDACTIVITY:
/* Somehow this needs to be fixed. :-))
	    long_return = 0;
#ifndef NO_GETTIMEOFDAY
	    for (i = 0; i < hard_server_limit; i++) {
		unsigned long stopTime = get_sysUpTime_timeval(
				&(ap_scoreboard_image->servers[ i ][ 0 ].stop_time));
		if (long_return < stopTime) {
		    long_return = stopTime;
		}
	    }
	    long_return = stopTime;
#endif
*/
	    return (unsigned char *) NULL;
	case APPLLASTOUTBOUNDACTIVITY:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLREJECTEDINBOUNDASSOCIATIONS:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLFAILEDOUTBOUNDASSOCIATIONS:
	    long_return = 0;
	    return (unsigned char *) &long_return;
	case APPLDESCRIPTION:
	    *var_len = strlen(applDescr);
	    return (unsigned char *) applDescr;
	case APPLURL:
	    *var_len = strlen(applUrl);
	    return (unsigned char *) applUrl;
	default:
         ERROR_MSG("");
    }
    return(NULL);
}
#endif /* NSM_APPL_GROUP */

#ifdef NSM_ASSOC_GROUP
worker_score *
create_assocIndex(oid *name, size_t *namelength,
        oid *newname, size_t *newname_length,
        int exact)
{
oid *ptr;
int result;
int i;
int j;
int hard_server_limit;
int hard_thread_limit;
worker_score *worker_info;

    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &hard_server_limit);
    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &hard_thread_limit);
    ptr = &(newname[ (*newname_length)++ ]);
    for ( i = 0 ; i < hard_server_limit; i++) {
        for (j = 0; j < hard_thread_limit; j++) {
            worker_info = ap_get_scoreboard_worker(i, j);
            if (worker_info) {
                if (is_active(worker_info->status)) {
	             *ptr = i + 1;
                     result = snmp_oid_compare(name, *namelength, newname, *newname_length);
	             if ((exact && (result == 0)) || (!exact && (result < 0))) {
		         return(worker_info);
                     }
                }
	    }
	}
    }
    return(NULL);
}	

static unsigned char *
read_assocEntry(struct variable *vp,
    oid *name, size_t *length,
    int exact, size_t *var_len, WriteMethod **write_method)
{
oid newname[ MAX_OID_LEN ];
size_t newname_length;
worker_score *worker_info;

    newname_length = vp->namelen;
    memcpy((char *)newname, (char *)vp->name, newname_length * sizeof(oid));
    newname[ newname_length++ ] = (oid)get_appl_index();
    worker_info = create_assocIndex(name, length,
					newname, &newname_length, exact);
    if (worker_info == NULL) {
	return(NULL);
    }
    memcpy((char *)name, (char *)newname, newname_length * sizeof(oid));
    *length = newname_length;

    switch (vp->magic) {
	case ASSOCREMOTEAPPLICATION:
	    *var_len = strlen(worker_info->client);
	    return (unsigned char *) worker_info->client;
	case ASSOCAPPLICATIONPROTOCOL:
	    return (unsigned char *) NULL;
	case ASSOCAPPLICATIONTYPE:
	    long_return = 1;
	    *var_len = sizeof(long_return);
	    return (unsigned char *) &long_return;
	case ASSOCDURATION:
	    return (unsigned char *) NULL;
	default:
	    ERROR_MSG("");
    }
    return NULL;
}
#endif /* NSM_ASSOC_GROUP */

#ifdef NSM_APPL_GROUP
static oid applEntry_oid[] = {1, 3, 6, 1, 2, 1, 27, 1, 1};
struct variable2 applEntry_variables[] = {
    { APPLNAME, ASN_OCTET_STR, RONLY, read_applEntry, 1, {2} },
    { APPLDIRECTORYNAME, ASN_OCTET_STR, RONLY, read_applEntry, 1, {3} },
    { APPLVERSION, ASN_OCTET_STR, RONLY, read_applEntry, 1, {4} },
    { APPLUPTIME, ASN_TIMETICKS, RONLY, read_applEntry, 1, {5} },
    { APPLOPERSTATUS, ASN_INTEGER, RONLY, read_applEntry, 1, {6} },
    { APPLLASTCHANGE, ASN_TIMETICKS, RONLY, read_applEntry, 1, {7} },
    { APPLINBOUNDASSOCIATIONS, ASN_GAUGE, RONLY, read_applEntry, 1, {8} },
    { APPLOUTBOUNDASSOCIATIONS, ASN_GAUGE, RONLY, read_applEntry, 1, {9} },
    { APPLACCUMULATEDINBOUNDASSOCIATIONS, ASN_COUNTER, RONLY, read_applEntry, 1, {10} },
    { APPLACCUMULATEDOUTBOUNDASSOCIATIONS, ASN_COUNTER, RONLY, read_applEntry, 1, {11} },
    { APPLLASTINBOUNDACTIVITY, ASN_TIMETICKS, RONLY, read_applEntry, 1, {12} },
    { APPLLASTOUTBOUNDACTIVITY, ASN_TIMETICKS, RONLY, read_applEntry, 1, {13} },
    { APPLREJECTEDINBOUNDASSOCIATIONS, ASN_COUNTER, RONLY, read_applEntry, 1, {14} },
    { APPLFAILEDOUTBOUNDASSOCIATIONS, ASN_COUNTER, RONLY, read_applEntry, 1, {15} },
    { APPLDESCRIPTION, ASN_OCTET_STR, RONLY, read_applEntry, 1, {16} },
    { APPLURL, ASN_OCTET_STR, RONLY, read_applEntry, 1, {17} },
};
#endif /* NSM_APPL_GROUP */

#ifdef NSM_ASSOC_GROUP
static oid assocEntry_oid[] = {1, 3, 6, 1, 2, 1, 27, 2, 1};
struct variable2 assocEntry_variables[] = {
    { ASSOCREMOTEAPPLICATION, ASN_OCTET_STR, RONLY, read_assocEntry, 1, {2} },
    { ASSOCAPPLICATIONPROTOCOL, ASN_OBJECT_ID, RONLY, read_assocEntry, 1, {3} },
    { ASSOCAPPLICATIONTYPE, ASN_INTEGER, RONLY, read_assocEntry, 1, {4} },
    { ASSOCDURATION, ASN_INTEGER, RONLY, read_assocEntry, 1, {5} },
};
#endif /* NSM_ASSOC_GROUP */

#ifdef NSM_MIB
void
init_nsm_mib()
{
#ifdef NSM_APPL_GROUP
    REGISTER_MIB("nsm-mib/applTable", applEntry_variables, variable2, applEntry_oid);
#endif /* NSM_APPL_GROUP */
#ifdef NSM_ASSOC_GROUP
    REGISTER_MIB("nsm-mib/assocTable", assocEntry_variables, variable2, assocEntry_oid);
#endif /* NSM_ASSOC_GROUP */
}
#endif /* NSM_MIB */
