/*
 * The combined SNMP-agent/AgentX-subagent for (both Unix and Win32)
 * $Id: mod-snmpagt.c 8676 2008-01-17 23:11:17Z ispringer $
 */
/* General includes */
#include <sys/types.h>
#include <stdlib.h>
#ifndef WIN32
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#else
#include <time.h>
#include <direct.h>
#include <winsock.h>
#endif
#include <signal.h>
#include <limits.h>
 
/* Apache includes */
#include <httpd.h>
#include <http_config.h>
#include <http_core.h>
#include <http_protocol.h>
#include <http_log.h>
#include <scoreboard.h>
#include <apr_strings.h>
#include <apr_thread_proc.h>
#include <apr_optional.h>
#include <ap_mpm.h>
#include <mpm.h>
#include <mpm_common.h>

/* SNMP includes */
#include <ucd-snmp-config.h>
#include <asn1.h>
#include <snmp.h>
#include <snmp_api.h>
#include <snmp_agent.h>
#include <snmp_vars.h>
#include <snmp_alarm.h>
#include <mib.h>
#include <var_struct.h>
#include <agent_trap.h>
#include <read_config.h>
#include <agent_read_config.h>
#include <default_store.h>
#include <ds_agent.h>
#include <snmp_debug.h>
#include <callback.h>
#ifdef SNMP_AGENT
#include <mibgroup/agentx/agentx_config.h>
#include <mibgroup/mibII/system_mib.h>
#include <mibgroup/mibII/snmp_mib.h>
#include <mibgroup/mibII/vacm_vars.h>
#include <mibgroup/mibII/sysORTable.h>
#include <mibgroup/snmpv3/usmUser.h>
#include <mibgroup/snmpv3/usmStats.h>
#include <mibgroup/snmpv3/snmpMPDStats.h>
#include <mibgroup/snmpv3/snmpEngine.h>
#endif /* SNMP_AGENT */

/* includes */
#include "covalent-snmp-config.h"
#include "snmpcommon/snmpcommon.h"
#include "snmpcommon/restart-storage.h"
#include "snmpcommon/generic-logging.h"
#include "www-mib/www-mib.h"
#include "www-mib/www-mib-document.h"
#include "www-mib/www-mib-protocol.h"
#include "www-mib/www-mib-services.h"
#include "www-mib/www-resp-notify-mib.h"
#include "mibs-ietf/nsm-mib.h"
#include "rtp-mib/rtp-mib.h"
#include "apache-server-mib/apache-server-mib.h"
#include "apache-modules-mib/apache-modules-mib.h"
#include "apache-status-mib/apache-status-mib.h"
#include "apache-logging-mib/apache-logging-mib.h"
#include "www-extensions-mib/www-extensions-mib.h"
#include "apache-mpm-mib/apache-mpm-mib.h"
#ifdef SNMP_AGENT
#include "snmpagent/ucd_snmpd.h"
#else /* AGENTX_SUBAGENT */
#include "snmpcommon/dummy-config.h"
#endif /* SNMP_AGENT */
#include "snmpagent/logging.h"
#include "external-mibs/extensions.h"

#ifndef WIN32
extern int errno;
#endif /* WIN32 */

static apr_pool_t *snmp_pconf = NULL;

module AP_MODULE_DECLARE_DATA snmpagt_module;

static enum { STOP=0, CONTINUE, RECONFIG } running;
#define TIMETICK         500000L
#define ONE_SEC         1000000L

static snmpcommon_logpath_t *logpath;

int
covalent_snmp_generic_logger(snmpagent_log_record_t *data)
{
    return(snmpcommon_logpath_write(logpath, data, sizeof(snmpagent_log_record_t)));
}

int
snmpagent_logpath_is_open()
{
	if (logpath != NULL) {
        return(snmpcommon_logpath_socket(logpath, ORIGINATOR));
	}
	return(-1);
}

static int
snmpagent_generic_logsink()
{
snmpagent_log_record_t log_record;
unsigned int datalen;

    datalen = snmpcommon_logpath_read(logpath, &log_record, sizeof(log_record));
    if (datalen == sizeof(log_record)) {
        switch (log_record.type) {
        case WWWLOGGING:
            /* For the WWW type we need to update the www-mib statistics   */
            /* and check wheter to send a notification for a www response. */
#ifdef WWW_MIB
            update_www_mib_protocol_statistics(log_record.data.www.wwwServiceIndex,
                                               log_record.data.www.wwwProtocol,
                                               log_record.data.www.wwwRequestTime,
                                               log_record.data.www.wwwRequestInType,
                                               log_record.data.www.wwwBytesIn,
                                               log_record.data.www.wwwResponseOutType,
                                               log_record.data.www.wwwBytesOut,
                                               log_record.data.www.wwwDocName,
                                               log_record.data.www.wwwStatusMsg);
#endif /* WWW_MIB */
#ifdef WWW_DOCUMENT_GROUP
            update_www_mib_document_statistics(log_record.data.www.wwwServiceIndex,
                                               log_record.data.www.wwwProtocol,
                                               log_record.data.www.wwwRequestTime,
                                               log_record.data.www.wwwRequestInType,
                                               log_record.data.www.wwwBytesIn,
                                               log_record.data.www.wwwResponseOutType,
                                               log_record.data.www.wwwBytesOut,
                                               log_record.data.www.wwwDocName,
                                               log_record.data.www.wwwStatusMsg);
#endif /* WWW_DOCUMENT_GROUP */
#ifdef COVALENT_RESP_NOTIFY_MIB
            generate_ctNotifyResponse_notification(log_record.data.www.wwwServiceIndex,
                                               log_record.data.www.wwwProtocol,
                                               log_record.data.www.wwwRequestTime,
                                               log_record.data.www.wwwRequestInType,
                                               log_record.data.www.wwwBytesIn,
                                               log_record.data.www.wwwResponseOutType,
                                               log_record.data.www.wwwBytesOut,
                                               log_record.data.www.wwwDocName,
                                               log_record.data.www.wwwStatusMsg);
#endif /* COVALENT_RESP_NOTIFY_MIB */
            break;
        case APACHEERRORLOGGING:
#ifdef COVALENT_APACHE_LOGGING_APPLICATION_GROUP
            update_apache_logging_mib_appl(log_record.data.error.www_service,
                                log_record.data.error.log_level,
                                log_record.data.error.log_message);
#endif /* COVALENT_APACHE_LOGGING_APPLICATION_GROUP */
#ifdef COVALENT_APACHE_LOGGING_WWW_SERVICES_GROUP
            update_apache_logging_mib_www_service(log_record.datasg.www_service,
                                log_record.data.error.log_level,
                                log_record.data.error.log_message);
#endif /* COVALENT_APACHE_LOGGING_WWW_SERVICES_GROUP */
        default:
            /* Here we should add the registered log-sinks from modules. */
            break;
        }
    }
    return(OK);
}

static void init_mib_modules(server_rec *s, apr_pool_t *p, const char *persdir);
static void covalent_snmp_agent_shutdown(int signal);
static int init_signals(void);

/*
 * Function: init_application_mib_modules
 * Parameters: server_rec *s,
 *             apr_pool_t *p,
 *             char *persdir
 * Returns:    void
 * Description:
 * This function embraces all application framework MIB modules
 * for Covalent SNMP.
 */
static void
init_mib_modules(server_rec *s, apr_pool_t *p, const char *persdir)
{
#ifdef SNMP_AGENT
    /* Framework MIB modules required by a master agent. */
    init_system_mib();
    init_sysORTable();
    init_snmp_mib();
    init_vacm_vars();
    init_snmpEngine();
    init_snmpMPDStats();
    init_usmStats();
    init_usmUser();
    init_agentx_config();
#else /* AGENTX_SUBAGENT */
    /* Reading the config tokens to avoid messages in the error log. */
    init_agentx_dummy_config();
#endif /* SNMP_AGENT */

    /* application MIB modules */
    init_www_mib(p, persdir);
    init_nsm_mib();
    init_rtp_mib();

    /* Covalent enterprise MIB modules. */
    init_www_resp_notify_mib();
    init_covalent_www_extensions_mib();
    init_covalent_apache_modules_mib();
    init_covalent_apache_server_table();
    init_covalent_apache_server_www_service_table();
    init_covalent_apache_mpm_mib();
    init_covalent_apache_status_process();
    init_covalent_apache_status_thread();
    init_apache_logging_mib_appl(p, s);
    init_apache_logging_mib_www_service(p, s);
}

static void
covalent_snmp_agent_shutdown(int signal)
{
    /* Switch off the running. */
    running = STOP;
}

static int
init_signals()
{
    running = CONTINUE;

#ifndef WIN32
#ifdef SIGTERM
    signal(SIGTERM, covalent_snmp_agent_shutdown);
#endif
#ifdef SIGHUP
    signal(SIGHUP, covalent_snmp_agent_shutdown);
#endif
#ifdef SIGUSR1
    signal(SIGUSR1, covalent_snmp_agent_shutdown);
#endif
#else /* WIN32 */

#endif /* !WIN32 */
    return(0);
}

static char *
get_address_value()
{
#ifdef SNMP_AGENT
    return(ds_get_string(DS_APPLICATION_ID, DS_AGENT_PORTS) ?
                ds_get_string(DS_APPLICATION_ID, DS_AGENT_PORTS) :
                "161@0.0.0.0");
#else /* AGENTX_SUBAGENT */
    return(ds_get_string(DS_APPLICATION_ID, DS_AGENT_X_SOCKET) ?
                ds_get_string(DS_APPLICATION_ID, DS_AGENT_X_SOCKET) :
                "");
#endif /* SNMP_AGENT */
}

static char*
get_agent_type()
{
#ifdef SNMP_AGENT
    return("SNMP");
#else /* AGENTX_SUBAGENT */
    return("AgentX");
#endif /* SNMP_AGENT */

}


static void
covalent_snmpagt_main(apr_pool_t *p_snmp)
{
int count;
unsigned int numfds;
fd_set fdset;
struct timeval timeout, *tvp;
int block;
uid_t snmpuserid;
const char *confdir;
const char *persdir;
unsigned int log_socket = 0;

    confdir = ds_get_string(DS_LIBRARY_ID, DS_LIB_CONFIGURATION_DIR);
    if (confdir == NULL) {
	confdir = ap_server_root_relative(p_snmp, COVALENT_SNMP_CONFIGURATION_DIR);
	ds_set_string(DS_LIBRARY_ID, DS_LIB_CONFIGURATION_DIR, strdup(confdir));
    }
    persdir = ds_get_string(DS_LIBRARY_ID, DS_LIB_PERSISTENT_DIR);
    if (persdir == NULL) {
	persdir = ap_server_root_relative(p_snmp, COVALENT_SNMP_PERSISTENT_DIR);
	ds_set_string(DS_LIBRARY_ID, DS_LIB_PERSISTENT_DIR, strdup(persdir));
    }
    /* First phase initialization of the SNMP agent. */
#ifdef AGENTX_SUBAGENT
    ds_set_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE, 1);
#endif /* AGENTX_SUBAGENT */
    if (init_agent("snmpd")) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
                "SNMP: %s (%s) could not start (address '%s')",
                COVALENT_SNMP_VERSION, get_agent_type(), get_address_value());
        return;
    }

    /* Initialize all the MIB modules. */
    init_mib_modules(get_www_service_root(), p_snmp, persdir);

    /* start library */
    init_snmp("snmpd");

#ifdef SNMP_AGENT
    if (init_master_agent(0, snmp_check_packet, snmp_check_parse )) {
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                    "SNMP: %s (%s) could not be started", COVALENT_SNMP_VERSION, get_agent_type());
        return;
    }
#endif /* SNMP_AGENT */

    /* Setup the signals/handles to terminate the SNMP agent. */
    init_signals();

#ifdef SNMP_AGENT
    /* store persistent data immediately in case we crash later */
    snmp_store("snmpd");
    /* send coldstart trap via snmptrap(1) if possible */
    send_easy_trap(0, 0);
#endif /* SNMP_AGENT */
    send_ctApacheServerColdStart_trap();

    snmpuserid = ds_get_int(DS_APPLICATION_ID, DS_AGENT_USERID);
#ifndef WIN32
    if ((snmpuserid != 0) && !geteuid()) {
         if (setuid(snmpuserid)) {
             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
                        "SNMP: Cannot change user id.");
         } else {
             set_restart_capability(OFF);
         }
    }
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
         "SNMP: %s started (user '%d' - %s address '%s' - pid '%d')",
                COVALENT_SNMP_VERSION,
                geteuid(), get_agent_type(), get_address_value(),
                getpid());
#else
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
         "SNMP: %s started (address '%s' - pid '%d')",
                COVALENT_SNMP_VERSION, get_address_value(),
                getpid());
#endif /* !WIN32 */

    log_socket = snmpcommon_logpath_socket(logpath, RECIPIENT);

    /* Listen to packets to receive */
    while (running != STOP) {
        tvp = &timeout;
        tvp->tv_sec = 0;
        tvp->tv_usec = TIMETICK;

        /*
         * We should make the logging functionality part of the
         * session portion of the NET-SNMP package.
         * APR does not help here, since NET-SNMP is the more important part.
         */
        numfds = log_socket + 1;
        FD_ZERO(&fdset);
        snmp_select_info(&numfds, &fdset, tvp, &block);
        FD_SET(log_socket, &fdset);

        /* XXX Fatal error on Win32 when the child socket is terminated
         * with prejudice.
         */
        count = select(numfds, &fdset, 0, 0, tvp);
        if (count > 0){
            snmp_read(&fdset);
            if (FD_ISSET(log_socket, &fdset)) {
		snmpagent_generic_logsink(log_socket);
	    }
        } else switch(count){
            case 0:
                break;
            case -1:
                if (errno == EINTR) {
		    continue;
                } else {
		    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
				"SNMP: select error '%s'\n", strerror(errno));
                    running = STOP;
                }
                break;
            default:
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
                		"SNMP: select returned %d\n", count);
                running = STOP;
        }
        run_alarms();
        send_ctApacheServerWarmStart_trap();
    }
    /* Shutting down the SNMP agent/AgentX subagent */
#ifdef SNMP_AGENT
    /* Before shutdown send 'stop' trap and write persistant information. */
    send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 3);
    snmp_store("snmpd");
#endif /* SNMP_AGENT */
    send_ctApacheServerShutdown_trap();
    snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, NULL);
    snmp_close_sessions();
    shutdown_mib();
    unregister_all_config_handlers();
    ds_shutdown();
    return;
}

#ifdef WIN32
static void * APR_THREAD_FUNC
covalent_snmpagt_mainthread(apr_thread_t *thd, void *data)
{
    apr_pool_t *p_snmp = apr_thread_pool_get(thd);

    covalent_snmpagt_main(p_snmp);
    apr_thread_exit(thd, APR_SUCCESS);
    return(NULL);
}

typedef struct {
    apr_pool_t *parent_pool;
    apr_thread_t *thr;
} snmp_cleanup_info_t;

static apr_status_t covalent_snmp_cleanup(void *data_)
{
    snmp_cleanup_info_t *snmp_info = (snmp_cleanup_info_t *)data_;
    apr_status_t rv, thr_rv;

    /* Set the shutdown flag */
    running = STOP;

    /* send a 0-length datagram to the logpath socket
     * so that we can knock that thread out of select() */
    (void) sendto(logpath->socket[ORIGINATOR], (char *)NULL, 0 /* len */,
                  0 /* flags */,
                  (struct sockaddr *)&(logpath->address[RECIPIENT]),
                  (logpath->address_size[RECIPIENT]));

    /* Join the thread. Warning: If the thread doesn't die this will
     * hang forever. */
    if ((rv = apr_thread_join(&thr_rv, snmp_info->thr)) != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     rv, NULL, "SNMP: unable to join snmp thread");
        apr_pool_destroy(snmp_info->parent_pool);
        return rv;
    }
    if (thr_rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     thr_rv, NULL, "SNMP: snmp thread exited abnormally");
        apr_pool_destroy(snmp_info->parent_pool);
        return thr_rv;
    }

    apr_pool_destroy(snmp_info->parent_pool);
    return APR_SUCCESS;
}
#endif /* WIN32 */

#ifndef WIN32
#if APR_HAS_OTHER_CHILD
/* Blatantly stolen from cgid_maint. */
static void snmpagent_maint(int reason, void *data, apr_wait_t status)
{
    pid_t *sd = data;
    int state;
#ifdef AP_MPMQ_MPM_STATE /* added in 2.0.49 */
    ap_mpm_query(AP_MPMQ_MPM_STATE, &state);

    /* workaround worker mpm oddity that 
     * leaves snmp agent process alive after apachectl stop
     */
    if ((state == AP_MPMQ_STOPPING) &&
        (reason == APR_OC_REASON_RESTART))
    {
        reason = APR_OC_REASON_DEATH;
    }
#endif
    switch (reason) {
    case APR_OC_REASON_DEATH:
        /* When the server is going down, unregister the child (which
         * means we'll send it a SIGHUP. */
        apr_proc_other_child_unregister(data);
        break;
    case APR_OC_REASON_RESTART:
        /* Do nothing on a graceful restart, we want the snmp process
         * to stick around between restarts. */
        break;
    case APR_OC_REASON_LOST:
        /* it would be better to restart just the snmp child
         * process but for now we'll gracefully restart the entire 
         * server by sending AP_SIG_GRACEFUL to ourself, the httpd 
         * parent process
         */
        kill(getpid(), AP_SIG_GRACEFUL);
        break;
    case APR_OC_REASON_UNREGISTER:
        /* we get here when p_snmp is cleaned up; p_snmp gets cleaned
         * up when pconf gets cleaned up
         */
        kill(*sd, SIGHUP); /* send signal to daemon telling it to die */
        break;
    }
}
#endif /* APR_HAS_OTHER_CHILD */
#endif /* WIN32 */

/* similar to the win32 hack that uses LoadLibrary 
 * to increment the dl refcnt.  this is required on
 * certain platforms since we register a cleanup with
 * s->process->pool, a graceful restart will run cleanups
 * for pconf, closing this shared library and taking function
 * addresses with it. 
 */
static void snmp_dso_load(void)
{
    char *fname = NULL;

#if defined(__sun)
#include <dlfcn.h>
    Dl_info dli;
    if (dladdr((void*)snmp_dso_load, &dli)) {
        fname = dli.dli_fname;
        dlopen(fname, RTLD_LAZY);
    }
#endif 

    if (fname) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                     "Incremented dl refcnt on %s", fname);
    }
}

int
covalent_snmp_pre_mpm(apr_pool_t *snmp_process_pool, ap_scoreboard_e sb_type)
{
#ifndef WIN32
apr_proc_t *snmpagent_proc;
pid_t snmpagent_pid;
apr_status_t status;

    if ((snmpagent_pid = fork()) < 0) {
        ap_log_error(APLOG_MARK, APLOG_ERR, errno,
			         NULL, "SNMP: unable to fork agent");
        return DECLINED;
    }
    else if (snmpagent_pid == 0) { /* in child */
        covalent_snmpagt_main(snmp_pconf);
        exit(-1);
    }
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                 "Starting mod_snmp child process (pid %d)", snmpagent_pid);
    /* in parent */
    snmpagent_proc = apr_palloc(snmp_pconf, sizeof(*snmpagent_proc));
    snmpagent_proc->pid = snmpagent_pid;
    snmpagent_proc->err = snmpagent_proc->in = snmpagent_proc->out = NULL;
    apr_pool_note_subprocess(snmp_pconf, snmpagent_proc, APR_KILL_NEVER);
#if APR_HAS_OTHER_CHILD
    apr_proc_other_child_register(snmpagent_proc, snmpagent_maint,
                                  &snmpagent_proc->pid, NULL, snmp_pconf);
    snmp_dso_load();
#endif
#else /* WIN32 */
    apr_status_t status;
    snmp_cleanup_info_t *snmp_info;

    snmp_info = apr_palloc(snmp_process_pool, sizeof(*snmp_info));

    /* Create a global pool to hold the SNMP resources */
    apr_pool_create(&snmp_info->parent_pool, NULL);

    /* Load the DLL for our module again to increase the ref count so
     * we don't get unloaded when pconf gets cleared. */
    if (snmpagt_module.dynamic_load_handle == NULL) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     0, NULL, "SNMP: snmpagt must be loaded as a DSO on Win32");
        return HTTP_INTERNAL_SERVER_ERROR;
    }
    else {
        /* reload our module and attach it to the global pool.
         * Win32 will keep a reference count for us, how kind! */
        char fspec[APR_PATH_MAX];
        apr_os_dso_handle_t dso_handle;
        apr_os_dso_handle_get(&dso_handle,
                              snmpagt_module.dynamic_load_handle);

        if (GetModuleFileName((HMODULE)dso_handle,
                              fspec, APR_PATH_MAX) == 0) {
            apr_status_t rv;
            rv = apr_get_os_error();
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
                         "SNMP: unable to retrieve DSO filename");
            return rv;
        }

        /* Increase the reference count on this DLL, let it stick around
         * until the process dies. */
        (void) LoadLibrary(fspec);
    }

    /* Open the parent's logpipe */
    logpath = snmpcommon_logpath_open(RECIPIENT, "COVALENT_SNMPAGT",
                                      snmp_process_pool);
    if (logpath == NULL) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     0, NULL, "SNMP: unable to open logpath");
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    status = apr_thread_create(&snmp_info->thr, NULL,
                               covalent_snmpagt_mainthread, NULL,
                               snmp_info->parent_pool);
    if (status != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     status, NULL, "SNMP: unable to start agent");
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    /* register a cleanup handler for when we want to shutdown that thread
     * we just created. This cleanup will get called only when the process
     * is being shut down. */
    apr_pool_cleanup_register(snmp_process_pool, (void *)snmp_info,
                              covalent_snmp_cleanup, apr_pool_cleanup_null);

#endif /* WIN32 */
    return OK;
}

 
static int
covalent_snmp_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
#ifdef WWW_MIB
    /* It is ok to call this multiple times in one process/thread
     * (eg. during restart). */
    if (covalent_snmp_www_protocol_init()) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL,
                 "SNMP: Could not initialise the protocol registration");
        exit(1);
    }
#endif /* WWW_MIB */
    return OK;
}

static int
covalent_snmp_post_config(apr_pool_t *p, apr_pool_t *ptemp,
                         apr_pool_t *plog, server_rec *s);
/*
 * Function:
 *	static int covalent_snmp_post_config(apr_pool_t *p, apr_pool_t *ptemp,
 *                       apr_pool_t *plog, server_rec *s)
 * Description:
 *	Initialisation for 'apache-servers' to use the SNMP-module. This does
 *	NOT intialise the complete SNMP-AGENT. The SNMP-AGENT allocates some
 *	memory and it is not required that the rest of the http-server this
 *	knows.
 */
static int
covalent_snmp_post_config(apr_pool_t *pconf, apr_pool_t *ptemp,
                          apr_pool_t *plog, server_rec *s)
{
#if 0
    /* FIXME: This isn't necessary, but it's useful and I'm
     * leaving it in here so it can be added later */
void *data;
const char *userdata_key = "covalent_snmp_pre_mpm";

    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
    if (!data) {
        apr_pool_userdata_set((const void *)1, userdata_key,
                              apr_pool_cleanup_null, s->process->pool);
        return OK;
    }
#endif

    /* Generic parts are always required. */
    ap_add_version_component(pconf, COVALENT_SNMP_VERSION);

    /* save pconf so we can use it in the pre_mpm */
    /* we actually need to use s->process->pool to survive graceful restarts */
    snmp_pconf = s->process->pool; /*pconf;*/

#ifndef WIN32
    logpath = snmpcommon_logpath_open(RECIPIENT, "COVALENT_SNMPAGT", s->process->pool);
    if (logpath == NULL) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
                     0, NULL, "SNMP: unable to open logpath");
        return HTTP_INTERNAL_SERVER_ERROR;
    }
#endif
    return OK;
}

/*
 * Function:
 *      int covalent_snmp_logger(request_rec *r)
 * Description:
 * This function fills the document log structure at the http-side
 * of the 'logpath' and sends (datagram) it to the snmp-side.
 */
int
covalent_snmp_logger(request_rec *r)
{
request_rec *last;
www_protocol_t *protocol_definition;
const char *statusMsg;
snmpagent_log_record_t logMsg;

    if (r == NULL) {
        return(!OK);
    }
    last = r;
    while (last->next) {
        last = last->next;
    }
    protocol_definition = get_www_protocol_definition_by_name(r->protocol);
    logMsg.type = WWWLOGGING;
    logMsg.data.www.wwwServiceIndex = get_www_service_index(r->server);
    logMsg.data.www.wwwProtocol = protocol_definition->number;
    logMsg.data.www.wwwRequestTime = r->request_time;
    logMsg.data.www.wwwBytesIn = (unsigned long)r->read_length;
    if (r->bytes_sent) {
        logMsg.data.www.wwwBytesOut = (unsigned long)r->bytes_sent;
    } else {
        logMsg.data.www.wwwBytesOut = (unsigned long)last->bytes_sent;
    }
    if (r->uri) {
        strncpy(logMsg.data.www.wwwDocName, r->uri, sizeof(logMsg.data.www.wwwDocName));
    } else {
        strcpy(logMsg.data.www.wwwDocName, "");
    }
    logMsg.data.www.wwwResponseOutType = last->status;
    logMsg.data.www.wwwRequestInType = get_request_type_index(protocol_definition, r->method);
    if (logMsg.data.www.wwwRequestInType == -1) {
        snprintf(logMsg.data.www.wwwStatusMsg, MAX_WWWDOCLASTNSTATUSMSG,
                "Not supported method %s", r->method);
    } else {
        statusMsg = apr_table_get(r->notes, "error-notes");
        if (statusMsg) {
            strncpy(logMsg.data.www.wwwStatusMsg, statusMsg, (MAX_WWWDOCLASTNSTATUSMSG - 1));
        }
    }

    if (0 >= covalent_snmp_generic_logger(&logMsg)) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, r->server,
                    "SNMP: generic logger: Could not log www-info\n");
    }
    return(OK);
}
const char *
set_snmp_user(cmd_parms *cmd, void *dummy, char *arg)
{
#ifndef WIN32
    ds_set_int(DS_APPLICATION_ID, DS_AGENT_USERID, ap_uname2id(arg));
#endif
    return NULL;
}

#ifdef WIN32
static void
covalent_snmp_child_init(apr_pool_t *pchild, server_rec *s)
{
    logpath = snmpcommon_logpath_open(ORIGINATOR, "COVALENT_SNMPAGT", s->process->pool);
}
#endif /* WIN32 */

static void covalent_snmp_register_hooks(apr_pool_t *p)
{
#ifndef WIN32
    /* XXX: This seems wrong to call every time through register_hooks. */
    memset(&logpath, 0, sizeof(logpath));
#endif /* WIN32 */

    ap_hook_pre_config(covalent_snmp_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(covalent_snmp_post_config, NULL, NULL, APR_HOOK_MIDDLE);
#ifdef WWW_MIB
    ap_hook_log_transaction(covalent_snmp_logger, NULL, NULL, APR_HOOK_MIDDLE);
#endif /* WWW_MIB */
#ifdef COVALENT_APACHE_LOGGING_MIB
    ap_hook_error_log(snmp_error_logging_send, NULL, NULL, APR_HOOK_MIDDLE);
#endif /* COVALENT_APACHE_LOGGING_MIB */
    ap_hook_pre_mpm(covalent_snmp_pre_mpm, NULL, NULL, APR_HOOK_LAST);
#ifdef WIN32
	ap_hook_child_init(covalent_snmp_child_init, NULL, NULL, APR_HOOK_MIDDLE);
#endif /* WIN32 */

    /* Register some optional functions required for other modules. */
    /* The function that can be used to send the SNMP agent         */
    /* an activity and requires MIB variable updates.               */
    APR_REGISTER_OPTIONAL_FN(covalent_snmp_generic_logger);
    /* Function that can be used to send a trap from a module.      */
    APR_REGISTER_OPTIONAL_FN(covalent_snmp_notification);
    /* Register a MIB portion.                                      */
    /* the result will be a unique loggin id or zero (on failure).  */
    APR_REGISTER_OPTIONAL_FN(covalent_snmp_register_mibtree);
#ifdef WWW_MIB
    /* Register other protocols besides HTTP.                       */
    APR_REGISTER_OPTIONAL_FN(covalent_snmp_www_protocol_register);
#endif /* WWW_MIB */
}

module AP_MODULE_DECLARE_DATA snmpagt_module = {
    STANDARD20_MODULE_STUFF,
    NULL,			/* dir config creater */
    NULL,			/* dir merger --- default is to override */
    NULL,                       /* server config */
    NULL,			/* merge server config */
    NULL,			/* command ap_table_t */
    covalent_snmp_register_hooks	/* register hooks */
};

