/*************************************************************
/*
/* Program dfcd
/*
/* Author: E. Pasqualucci
/*
/* It is an example of how to modify snmptrapd to
/* receive and log snmp traps from kloe nodes. It works on
/* the KLOE_SNMP_TRAP_PORT.
/* Modified from the Carnegie Mellon University
/* snmptrapd.
/*
/* Usage:
/*
/*       dfcd [-P] [-d]
/*
/*************************************************************/

/* Standard include files */

#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
#include <syslog.h>

/* SNMP standard include files */

#include "snmp.h"
#include "asn1.h"
#include "snmp_impl.h"
#include "snmp_api.h"
#include "snmp_client.h"
#include "party.h"
#include "context.h"
#include "view.h"
#include "acl.h"

/* Specific include files */

#include "dfcd_protos.h"
#include "dfcd.h"
#include "kloe_traps.h"

/* Global variables */

extern int errno;
int snmp_dump_packet = 0;
int Print = 0;

struct snmp_session *ss;

char *dfcd_trap_description (int trap)
{
    switch(trap){
	case SNMP_TRAP_COLDSTART:
	    return "Cold Start";
	case SNMP_TRAP_WARMSTART:
	    return "Warm Start";
	case SNMP_TRAP_LINKDOWN:
	    return "Link Down";
	case SNMP_TRAP_LINKUP:
	    return "Link Up";
	case SNMP_TRAP_AUTHFAIL:
	    return "Authentication Failure";
	case SNMP_TRAP_EGPNEIGHBORLOSS:
	    return "EGP Neighbor Loss";
	case SNMP_TRAP_ENTERPRISESPECIFIC:
	    return "Enterprise Specific";
        case DFC_TRAP_KNODE_Q_AV:
	    return "Kloe Node Queue Available";
        case DFC_TRAP_KNODE_Q_UNAV:
	    return "Kloe Node Queue Unanavailable";
	default:
	    return "Unknown Type";
    }
}

char *uptime_string(register u_int timeticks, char *buf)
{
    int	seconds, minutes, hours, days;

    timeticks /= 100;
    days = timeticks / (60 * 60 * 24);
    timeticks %= (60 * 60 * 24);

    hours = timeticks / (60 * 60);
    timeticks %= (60 * 60);

    minutes = timeticks / 60;
    seconds = timeticks % 60;

    if (days == 0){
	sprintf(buf, "%d:%02d:%02d", hours, minutes, seconds);
    } else if (days == 1) {
	sprintf(buf, "%d day, %d:%02d:%02d", days, hours, minutes, seconds);
    } else {
	sprintf(buf, "%d days, %d:%02d:%02d", days, hours, minutes, seconds);
    }
    return buf;
}

int dfcd_input (int op, struct snmp_session *session, int reqid,
                struct snmp_pdu *pdu, void *magic)
{
    struct variable_list *vars;
    char buf[DFCD_MAXBUFLEN];

    if (op == RECEIVED_MESSAGE && pdu->command == TRP_REQ_MSG)
    {
	if (Print)
        {
	    printf("%s: %s Trap (%d) Uptime: %s\n",
                   inet_ntoa(pdu->agent_addr.sin_addr),
	           dfcd_trap_description(pdu->trap_type),
                   pdu->specific_type, uptime_string(pdu->time, buf));

	    for(vars = pdu->variables; vars; vars = vars->next_variable)
		print_variable(vars->name, vars->name_length, vars);
	}
#ifndef __Lynx__
        else
        {
	    syslog(LOG_WARNING, "%s: %s Trap (%d) Uptime: %s\n",
                   inet_ntoa(pdu->agent_addr.sin_addr),
		   dfcd_trap_description(pdu->trap_type),
                   pdu->specific_type, uptime_string(pdu->time, buf));
	}
#endif
    }
    else if (op == TIMED_OUT)
    {
	printf("Timeout: This shouldn't happen!\n");
    }
}

void init_syslog (void)
{
#ifndef __Lynx__
/*
 * These definitions handle 4.2 systems without additional syslog facilities.
 */
#ifndef LOG_CONS
#define LOG_CONS	0	/* Don't bother if not defined... */
#endif
#ifndef LOG_LOCAL0
#define LOG_LOCAL0	0
#endif
    /*
     * All messages will be logged to the local0 facility and will be sent to
     * the console if syslog doesn't work.
     */
    openlog("snmptrapd", LOG_CONS, LOG_LOCAL0);
    syslog(LOG_INFO, "Starting snmptrapd");
#endif
}

void init_options (int argc, char **argv)
{
    int arg;

    for(arg = 1; arg < argc; arg++)
	if (argv[arg][0] == '-')
	{
            switch(argv[arg][1])
            {
		case 'd':
		    snmp_dump_packet++;
		    break;
		case 'p':
		    Print++;
		    break;
		default:
		    printf("invalid option: -%c\n", argv[arg][1]);
		    printf("Usage: dfcd [-p] [-d]\n");
		    break;
	    }
        }
}

struct snmp_session *dfcd_init_snmp_trap_session (void)
{
    struct snmp_session session, *dfcd_ss;

    bzero((char *)&session, sizeof(struct snmp_session));

    session.peername = NULL;
    session.community = NULL;
    session.community_len = 0;
    session.retries = SNMP_DEFAULT_RETRIES;
    session.timeout = SNMP_DEFAULT_TIMEOUT;
    session.authenticator = NULL;
    session.callback = dfcd_input;
    session.callback_magic = NULL;
    session.local_port = SNMP_KLOE_TRAP_PORT;

    dfcd_ss = snmp_open(&session);

    return dfcd_ss;
}

void dfcd_init (int argc, char **argv)
{
    init_syslog();
    init_mib();
    init_options (argc, argv);

    if ((ss = dfcd_init_snmp_trap_session()) == NULL)
    {
	printf("Couldn't open snmp\n");
	exit(-1);
    }
}

int dfcd_main_loop (void)
{
    int count, numfds, block;
    fd_set fdset;
    struct timeval timeout, *tvp;

    while (1)
    {
	numfds = 0;
	FD_ZERO(&fdset);
	block = 1;
	tvp = &timeout;
	timerclear(tvp);
	snmp_select_info(&numfds, &fdset, tvp, &block);
	if (block == 1)
	    tvp = NULL;	/* block without timeout */

#if defined (_HPUX_SOURCE) && defined (V9)
	count = select (numfds, (int *) &fdset, 0, 0, tvp);
#else
	count = select (numfds, &fdset, 0, 0, tvp);
#endif

	if (count > 0)
            snmp_read(&fdset);
	else
            switch(count)
            {
                case 0:
                    snmp_timeout();
		    break;
	        case -1:
		    if (errno != EINTR)
		        perror("select");
		    return -1;
	        default:
		    printf("select returned %d\n", count);
		    return -1;
	    }
    }
}

main (int argc, char **argv)
{
    dfcd_init (argc, argv);
    return dfcd_main_loop();
}