/* $Id: mod-covalent-snmp-logging.c 8676 2008-01-17 23:11:17Z ispringer $
 * harrie@covalent.net
 */
/* General includes */
#include <sys/types.h>
 
/* Apache includes */
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <scoreboard.h>
#include <apr_optional.h>
#include <apr_strings.h>

/* UCD-SNMP includes */
#include <ucd-snmp-config.h>
#include <asn1.h>
#include <snmp.h>
#include <snmp_api.h>
#include <snmp_vars.h>
#include <snmp_agent.h>
#include <agent_trap.h>
#include <default_store.h>
#include <ds_agent.h>

#include "covalent-snmp-config.h"
#include "monolithic/ucd_snmpd.h"
#include "snmpcommon/snmpcommon.h"
#include "apache-logging-mib/apache-logging-mib.h"

void apache_logging_subagt_shutdown(int signal);
void apache_logging_subagt_shutdown(int signal)
{
    send_easy_trap (SNMP_TRAP_ENTERPRISESPECIFIC, 3);
    snmp_shutdown("logagt");
    exit(0);  
}


static void snmp_error_logging_agent(apr_pool_t *p, server_rec *s, int phase, int nr_www_services);
static void snmp_error_logging_agent(apr_pool_t *p, server_rec *s, int phase, int nr_www_services)
{
int count;
int numfds;
fd_set fdset;
struct timeval timeout, *tvp;
int block;
unsigned int log_socket;
static apr_pool_t *p_global_snmp;
apr_pool_t *p_snmp;
apr_proc_t snmpagent_proc;
apr_status_t status;
uid_t snmpuserid;
const char *confdir;
const char *persdir;

    if ((status = apr_pool_alloc_init(p_global_snmp)) != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL,
                                "SNMP: unable to alloc global pool for agent");
        return;
    }

    if ((status = apr_pool_create(&p_snmp, p_global_snmp)) != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL,
                                "SNMP: unable to create pool for agent");
        return;
    }
    status = apr_proc_fork(&snmpagent_proc, p_snmp);
    if (status < 0) {
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, status, NULL,
                                        "SNMP: unable to fork agent");
    } else if (status == APR_INPARENT) {
        apr_pool_note_subprocess(p_snmp, &snmpagent_proc, kill_never);
        return;
    } /* APR_INCHILD just continues */

    /* SNMP uses its own (module internal) global variable for this. */
    confdir = ds_get_string(DS_LIBRARY_ID, DS_LIB_CONF_DIR);
    if (confdir == NULL) {
        confdir = ap_server_root_relative(p_snmp, COVALENT_SNMP_CONFIGURATION_DIR);
        ds_set_string(DS_LIBRARY_ID, DS_LIB_CONF_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));
    }
    setenv( "MIBS", "", 1 );

    /* do what we need to do first. */
    init_agent("logagt");
#ifdef SNMP_UCD_DEBUGGING
    snmp_set_do_debugging(1);
#endif

    /* register MIB application MIB modules */
#ifdef COVALENT_APACHE_LOGGING_APPLICATION_GROUP
    init_apache_logging_mib_appl(p, s);
#endif /* COVALENT_APACHE_LOGGING_APPLICATION_GROUP */

#ifdef COVALENT_APACHE_LOGGING_WWW_SERVICE_GROUP
    init_apache_logging_mib_www_service(p, s);
#endif /* COVALENT_APACHE_LOGGING_WWW_SERVICE_GROUP */

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

    if (init_master_agent(0, snmp_check_packet, snmp_check_parse )) {
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                    "SNMP: %s could not be started", COVALENT_SNMP_LOGGING_VERSION);
        exit(0);
    }

#ifdef SIGTERM
    signal(SIGTERM, apache_logging_subagt_shutdown);
#endif

    /* send coldstart trap via snmptrap(1) if possible */
    send_easy_trap(0, 0);

    snmpuserid = ds_get_int(DS_APPLICATION_ID, DS_AGENT_USERID);
    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);
         }
    }
    log_socket = snmp_error_logging_get_socket();
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
         "SNMP: %s started (user '%d' - address '%s' - pid '%d' snmpset '%s')",
                COVALENT_SNMP_LOGGING_VERSION,
                geteuid(), ds_get_string(DS_APPLICATION_ID, DS_AGENT_PORTS) ?
                ds_get_string(DS_APPLICATION_ID, DS_AGENT_PORTS) : "161@0.0.0.0",
                getpid(), (check_restart_capability() ? "disable" : "enabled"));

    /* Listen to the network */
    while(1){
        tvp =  &timeout;
        tvp->tv_sec = 0;
        tvp->tv_usec = 500000L;

        numfds = log_socket + 1;
        FD_ZERO(&fdset);
        block = 1;
        snmp_select_info(&numfds, &fdset, tvp, &block);
        FD_SET(log_socket, &fdset);

        if (block == 1) {
            tvp = NULL;
        }

        count = select(numfds, &fdset, 0, 0, tvp);
        if (count > 0){
            snmp_read(&fdset);
            if (FD_ISSET(log_socket, &fdset)) {
                snmp_error_logging_recv(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));
                }
            default:
                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
                                "SNMP: select returned %d\n", count);
        }
    }
}

static void
apache_logging_subagt_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
const char *foo;
int init_state;
char *persistent;

    if ((foo = getenv("SNMP_LOGGING_STARTED")) == NULL) {
        setenv("SNMP_LOGGING_STARTED", "1", 1);
        init_state = 1;
    } else {
        char newval[6];
        init_state = (atoi(foo));
        snprintf(newval, 6, "%d", ++init_state); 
        setenv("SNMP_LOGGING_STARTED", newval, 1);
    }
    foo = ds_get_string(DS_LIBRARY_ID, DS_LIB_PERSISTENT_DIR);
    persistent = apr_pstrcat(p,
                        ap_server_root_relative(p, (foo ? foo : "var")),
                        "/apache-logging-mib.shared", NULL);
    snmp_error_logging_open(init_state);
    snmp_error_logging_agent(p, s, init_state, get_www_service_total());
}

static void apache_logging_subagt_register_hooks(apr_pool_t *p)
{
    ap_hook_post_config(apache_logging_subagt_post_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_error_log(snmp_error_logging_send, NULL, NULL, APR_HOOK_MIDDLE);

}

module AP_MODULE_DECLARE_DATA apache_logging_subagt_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 */
    apache_logging_subagt_register_hooks /* register hooks */
};

