/*
 * This file implements the COVALENT-APACHE-CONFIG-MIB module.
 * $Id: apache-config-mib.c 8676 2008-01-17 23:11:17Z ispringer $
 * harrie@covalent.net
 */

/* General includes */
#include <stdio.h>
#include <sys/types.h>

/* Apache includes */
#include <httpd.h>
#include <http_conf_globals.h>
#include <http_log.h>
#include <http_config.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"
#include "default_store.h"

#include "ietf-mibs/snmp-generic.h"
#include "covalent-snmp-config.h"
#include "covalent-snmp-logging.h"
#include "covalent-snmp.h"
#include "apache-restarts.h"
#include "ietf-mibs/www-mib.h"
#include "apache-config-mib.h"
#include "shared/logging.h"

/* objects of the module, instances */
#define EXTENDEDSTATUS                 1
#define MAXSERVERS                     2
#define MINSPARESERVERS                3
#define MAXSPARESERVERS                4
#define MAXREQUESTSPERCHILD            5
#define HOSTNAMELOOKUP                 6

/* objects of the module, columnar objects */
#define KEEPALIVESWITCH                1
#define KEEPALIVEREQUESTS              2
#define KEEPALIVETIMEOUT               3
#define LOGLEVEL                       4


#ifdef COVALENT_APACHE_CONFIG_MIB
static configmib_globals_t shadow;
static configmib_globals_t instances;
static server_rec *config_services;


int     write_configGlobals(int action,
        unsigned char *var_val, unsigned char var_val_type, size_t var_val_len,
        unsigned char *statP, oid *name, size_t name_len)
{
int leafObject = name[ name_len - 2];

    if (var_val_type != ASN_INTEGER) {
        return SNMP_ERR_WRONGTYPE;
    }

    if (var_val_len > sizeof(int)) {
        return(SNMP_ERR_WRONGLENGTH);
    }
    switch (leafObject) {
        case EXTENDEDSTATUS:
            shadow.extended_status = *((unsigned long *) var_val);
            if (shadow.extended_status != 1 && shadow.extended_status != 2) {
                return SNMP_ERR_WRONGVALUE;
            }
            break;
        case MAXSERVERS:
            shadow.max_servers = *((unsigned long *) var_val);
            if ((shadow.max_servers < 1) ||
                        (shadow.max_servers > derived_hard_server_limit)) {
                return SNMP_ERR_WRONGVALUE;
            }
            break;
        case MINSPARESERVERS:
            shadow.min_spare_servers = *((unsigned long *) var_val);
            if ((shadow.min_spare_servers < 1) ||
                        (derived_hard_server_limit < shadow.min_spare_servers)) {
                return SNMP_ERR_WRONGVALUE;
            }
            if (shadow.min_spare_servers > shadow.max_spare_servers) {
                return(SNMP_ERR_INCONSISTENTVALUE);
            }
            break;
        case MAXSPARESERVERS:
            shadow.max_spare_servers = *((unsigned long *) var_val);
            if ((shadow.max_spare_servers < 1) ||
                        (derived_hard_server_limit < shadow.max_spare_servers)) {
                return SNMP_ERR_WRONGVALUE;
            }
            if ((shadow.max_spare_servers < shadow.min_spare_servers) ||
                        (shadow.max_spare_servers > shadow.max_servers)) {
                return(SNMP_ERR_INCONSISTENTVALUE);
            }
            break;
        case MAXREQUESTSPERCHILD:
            shadow.max_spare_servers = *((unsigned long *) var_val);
            if (shadow.max_requests_per_child < 0) {
                return SNMP_ERR_WRONGVALUE;
            }
            break;
        case HOSTNAMELOOKUP:
            return(SNMP_ERR_NOTWRITABLE);
	default:
	    return(SNMP_ERR_NOTWRITABLE);
    }
    if (check_restart_capability()) {
        return(SNMP_ERR_NOTWRITABLE);
    }
    if (shared_store("config-globals", &shadow, sizeof(shadow))) {
        return(SNMP_ERR_NOTWRITABLE);
    }

    if (action == COMMIT) {
        switch (leafObject) {
            case EXTENDEDSTATUS:
                instances.extended_status = shadow.extended_status - 1;
                break;
            case MAXSERVERS:
                instances.max_servers = shadow.max_servers;
                break;
            case MINSPARESERVERS:
                instances.min_spare_servers = shadow.min_spare_servers;
                break;
            case MAXSPARESERVERS:
                instances.max_spare_servers = shadow.max_spare_servers;
                break;
            case MAXREQUESTSPERCHILD:
                instances.max_requests_per_child = shadow.max_requests_per_child;
                break;
            case HOSTNAMELOOKUP:
                instances.hostname_lookup = shadow.hostname_lookup;
                return(SNMP_ERR_NOTWRITABLE);
        }
        kill(getppid(), SIGUSR1);
    }
    return(SNMP_ERR_NOERROR);
}

unsigned char *
read_configGlobals(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
int result;

    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 = write_configGlobals;
    *var_len = sizeof(long_return);    /* default to 'long' results */

    switch(vp->magic) {
	case EXTENDEDSTATUS:
	    long_return = instances.extended_status + 1;
	    return (unsigned char *) &(long_return);
	case MAXSERVERS:
	    long_return = instances.max_servers;
	    return (unsigned char *) &(long_return);
	case MINSPARESERVERS:
	    long_return = instances.min_spare_servers;
	    return (unsigned char *) &(long_return);
	case MAXSPARESERVERS:
	    long_return = instances.max_spare_servers;
	    return (unsigned char *) &(long_return);
	case MAXREQUESTSPERCHILD:
	    long_return = instances.max_requests_per_child;
	    return (unsigned char *) &(long_return);
	case HOSTNAMELOOKUP:
	    long_return = instances.hostname_lookup;
	    return (unsigned char *) &(long_return);
    }
    return(NULL);
}

unsigned char *
read_configServiceEntry(
    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;
server_rec *service;

    service = get_admin_wwwService_row(vp, name, length,
                                newname, &newname_length,
                                exact, config_services);
    if (!service) {
        return NULL;
    }

    *length = newname_length;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    /* *write_method = write_configServerEntry; */
    *write_method = NULL;
    *var_len = sizeof(long_return);    /* default to 'long' results */
    switch(vp->magic) {
        case KEEPALIVESWITCH:
            long_return = service->keep_alive + 1;
            return (unsigned char *) &(long_return);
        case KEEPALIVEREQUESTS:
            long_return = service->keep_alive_max;
            return (unsigned char *) &(long_return);
        case KEEPALIVETIMEOUT:
            long_return = service->keep_alive_timeout;
            return (unsigned char *) &(long_return);
        case LOGLEVEL:
            long_return = service->loglevel + 1;
            return (unsigned char *) &(long_return);
    }
    return(NULL);
}


oid configGlobals_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 5, 1, 1 };
struct variable2 configGlobals_variables[] = {
    { EXTENDEDSTATUS, ASN_INTEGER, RWRITE, read_configGlobals, 2, {1, 0} },
    { MAXSERVERS, ASN_INTEGER, RWRITE, read_configGlobals, 2, {2, 0} },
    { MINSPARESERVERS, ASN_INTEGER, RWRITE, read_configGlobals, 2, {3, 0} },
    { MAXSPARESERVERS, ASN_INTEGER, RWRITE, read_configGlobals, 2, {4, 0} },
    { MAXREQUESTSPERCHILD, ASN_INTEGER, RWRITE, read_configGlobals, 2, {5, 0} },
    { HOSTNAMELOOKUP, ASN_INTEGER, RWRITE, read_configGlobals, 2, {6, 0} },
};


oid configServerEntry_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 5, 1, 2, 1 };
struct variable2 configServiceEntry_variables[] = {
    { KEEPALIVESWITCH, ASN_INTEGER, RWRITE, read_configServiceEntry, 1, {1} },
    { KEEPALIVEREQUESTS, ASN_INTEGER, RWRITE, read_configServiceEntry, 1, {2} },
    { KEEPALIVETIMEOUT, ASN_INTEGER, RWRITE, read_configServiceEntry, 1, {3} },
    { LOGLEVEL, ASN_INTEGER, RWRITE, read_configServiceEntry, 1, {4} },
};

oid configServiceCompliance_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 5, 2, 1, 2 };
oid configFullCompliance_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 5, 2, 1, 3 };


void
init_covalent_apache_config_mib_setup_shmem(server_rec *s, pool *p)
{
    /* Initialise the mib instances */
    instances.hostname_lookup = 0;
    shadow.hostname_lookup = 0;
    instances.extended_status = ap_extended_status;
    shadow.extended_status = ap_extended_status;
    instances.max_requests_per_child = ap_max_requests_per_child;
    shadow.max_requests_per_child = ap_max_requests_per_child;
    instances.max_servers = ap_daemons_limit;
    shadow.max_servers = ap_daemons_limit;
    instances.min_spare_servers = ap_daemons_min_free;
    shadow.min_spare_servers = ap_daemons_min_free;
    instances.max_spare_servers = ap_daemons_max_free;
    shadow.max_spare_servers = ap_daemons_max_free;
    /* Store initial part in DBM */
    shared_store("configmib-globals", &instances, sizeof(instances));
}

void
init_covalent_apache_config_mib_parent_copy(server_rec *s, pool *p)
{
    DEBUGMSGTL(("covalent/config-mib", "init_apache_config_mib_parent_copy"));

    if (shared_fetch("configmib-globals", &instances, sizeof(instances))) {
        ap_extended_status = instances.extended_status;
        ap_max_requests_per_child = instances.max_requests_per_child;
        ap_daemons_limit = instances.max_servers;
        ap_daemons_min_free = instances.min_spare_servers;
        ap_daemons_max_free = instances.max_spare_servers;
        ap_max_requests_per_child = instances.max_requests_per_child;
    }
}

void
init_covalent_apache_config_mib_subtrees(server_rec *s, pool *p)
{

    DEBUGMSGTL(("covalent/config-mib", "init_covalent_config_mib_subtrees\n"));

    /* Get our main service point */
    config_services = s;

    REGISTER_MIB("config-mib/MIBobjects", configGlobals_variables,
					variable2, configGlobals_oid);
    REGISTER_MIB("config-mib/MIBobjects", configServiceEntry_variables,
					variable2, configServerEntry_oid);
#ifdef USING_MIBII_SYSORTABLE_MODULE
        register_sysORTable(configFullCompliance_oid, 11,
				"The Covalent Apache Config MIB module");
#endif
}

#endif /* COVALENT_APACHE_CONFIG_MIB */
