/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 * 
 * U.S. Government Rights - Commercial software. Government users are subject to
 * the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.
 * 
 * 
 * This distribution may include materials developed by third parties. Sun, Sun
 * Microsystems, the Sun logo and Solaris are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
 * 
 */


/*
 * demo_module_4:
 * 
 * This module is based on the SDK-DEMO4-MIB.txt MIB. It implements the
 * me4LoadGroup objects.
 * 
 * This example module demonstrates following features: - Automatic Refresh of
 * data in regular intervals - Check for alarm condition in regular intervals
 * and generate trap if necessary. - Read threshold values from an external
 * configuration file called demo_module_4.conf
 */


#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "me4LoadGroup.h"
#include <sys/loadavg.h>
#include <netdb.h>


/*
 * Data for demo_module_4: loadavg1 stores data for me4SystemLoadAvg1min
 * loadavg5 stores data for me4SystemLoadAvg5min loadavg15 stores data for
 * me4SystemLoadAvg15min
 */

char           *loadavg1, *loadavg5, *loadavg15;

/* Alarm thresholds for demo_module_4 */

float           threshold_loadavg1 = 1.0, threshold_loadavg5 = 2.0, threshold_loadavg15 = 3.0;

/* Maintain previous alarm states for comparison */

int             prev_loadavg1_state = OK;
int             prev_loadavg5_state = OK;
int             prev_loadavg15_state = OK;

/* Common variables for information to be included in traps */

u_char          hostName[MAXHOSTNAMELEN], moduleName[15];


/** Initializes the demo_module_4 module
 * This is the Init function which is called when the module is loaded by the agent.
 *
 * Note: The name of this function has been changed from "init_me4LoadGroup" to
 * "init_demo_module_4" to give unique names to example modules across the SMA
 * SDK.
 */


void
init_demo_module_4(void)
{

    int             retCode;

    static oid      me4SystemLoadAvg15min_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 3, 0};
    static oid      me4SystemLoadAvg1min_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 1, 0};
    static oid      me4SystemLoadAvg5min_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 2, 0};

    DEBUGMSGTL(("me4LoadGroup", "Initializing\n"));

    netsnmp_register_read_only_instance(netsnmp_create_handler_registration
					("me4SystemLoadAvg15min",
					 get_me4SystemLoadAvg15min,
					 me4SystemLoadAvg15min_oid,
				      OID_LENGTH(me4SystemLoadAvg15min_oid),
					 HANDLER_CAN_RONLY));
    netsnmp_register_read_only_instance(netsnmp_create_handler_registration
					("me4SystemLoadAvg1min",
					 get_me4SystemLoadAvg1min,
					 me4SystemLoadAvg1min_oid,
				       OID_LENGTH(me4SystemLoadAvg1min_oid),
					 HANDLER_CAN_RONLY));
    netsnmp_register_read_only_instance(netsnmp_create_handler_registration
					("me4SystemLoadAvg5min",
					 get_me4SystemLoadAvg5min,
					 me4SystemLoadAvg5min_oid,
				       OID_LENGTH(me4SystemLoadAvg5min_oid),
					 HANDLER_CAN_RONLY));

    /* Initialize some common data */

    retCode = gethostname((char *) hostName, MAXHOSTNAMELEN);
    if (retCode != 0)
	strcpy((char *) hostName, "null\0");

    strcpy((char *) moduleName, "demo_module_4\0");


    /* Allocate memory once. These variables will hold load data */

    loadavg1 = malloc(10 * sizeof(char));
    loadavg5 = malloc(10 * sizeof(char));
    loadavg15 = malloc(10 * sizeof(char));

    /*
     * Register for thresholds. When a token (say threshold_loadavg1) is
     * found in demo_module_4.conf file, the read_load_thresholds function is
     * called by the agent
     */

    register_config_handler("demo_module_4", "threshold_loadavg1",
			    read_load_thresholds, NULL, NULL);

    register_config_handler("demo_module_4", "threshold_loadavg5",
			    read_load_thresholds, NULL, NULL);

    register_config_handler("demo_module_4", "threshold_loadavg15",
			    read_load_thresholds, NULL, NULL);


    snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_READ_CONFIG,
			   demo_4_post_read_config, NULL);


}

/**
 * The callback function that is called after the configuration files are
 * read by the agent ( the thresholds are loaded into the module )
 */

int 
demo_4_post_read_config(int a, int b, void *c, void *d)
{

    /* Refresh the load data every 60 seconds */
    snmp_alarm_register(60, SA_REPEAT, refreshLoadAvg, NULL);

    /* Acquire the data first time */
    refreshLoadAvg(0, NULL);

    return 1;

}


/*
 * This function is generated by mib2c. It is called when an SNMP "get"
 * request arrives for the node me4SystemLoadAvg15min
 * 
 */

int
get_me4SystemLoadAvg15min(netsnmp_mib_handler * handler,
			  netsnmp_handler_registration * reginfo,
			  netsnmp_agent_request_info * reqinfo,
			  netsnmp_request_info * requests)
{
    /*
     * We are never called for a GETNEXT if it's registered as a "instance",
     * as it's "magically" handled for us.
     */

    /*
     * a instance handler also only hands us one request at a time, so we
     * don't need to loop over a list of requests; we'll only get one.
     */

    /* Refresh the load data by calling the refresh function */
    refreshLoadAvg(0, NULL);

    switch (reqinfo->mode) {

    case MODE_GET:

	refreshLoadAvg(0, NULL);

	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) loadavg15, strlen(loadavg15));
	break;


    default:
	/* we should never get here, so this is a really bad error */
	return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

/*
 * This function is generated by mib2c. It is called when an SNMP "get"
 * request arrives for the node me4SystemLoadAvg1min
 * 
 */


int
get_me4SystemLoadAvg1min(netsnmp_mib_handler * handler,
			 netsnmp_handler_registration * reginfo,
			 netsnmp_agent_request_info * reqinfo,
			 netsnmp_request_info * requests)
{
    /*
     * We are never called for a GETNEXT if it's registered as a "instance",
     * as it's "magically" handled for us.
     */

    /*
     * a instance handler also only hands us one request at a time, so we
     * don't need to loop over a list of requests; we'll only get one.
     */

    switch (reqinfo->mode) {

	case MODE_GET:

	refreshLoadAvg(0, NULL);
	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) loadavg1, strlen(loadavg1));
	break;


    default:
	/* we should never get here, so this is a really bad error */
	return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

/*
 * This function is generated by mib2c. It is called when an SNMP "get"
 * request arrives for the node me4SystemLoadAvg5min
 * 
 */


int
get_me4SystemLoadAvg5min(netsnmp_mib_handler * handler,
			 netsnmp_handler_registration * reginfo,
			 netsnmp_agent_request_info * reqinfo,
			 netsnmp_request_info * requests)
{
    /*
     * We are never called for a GETNEXT if it's registered as a "instance",
     * as it's "magically" handled for us.
     */

    /*
     * a instance handler also only hands us one request at a time, so we
     * don't need to loop over a list of requests; we'll only get one.
     */

    refreshLoadAvg(0, NULL);

    switch (reqinfo->mode) {

    case MODE_GET:

	refreshLoadAvg(0, NULL);

	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *) loadavg5, strlen(loadavg5));
	break;

    default:
	/* we should never get here, so this is a really bad error */
	return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}


/*
 * refreshLoadAvg: This is the function which "refreshes" the load data using
 * the system call "getloadavg". The data is stored in the 3 variables
 * loadavg1, loadavg5, loadavg15. Once the data is refreshed, the "check"
 * functions are called to check for alarm conditions
 */

void 
refreshLoadAvg(unsigned int clientreg, void *clientarg)
{

    double          loadavg[3];
    int             numOfSamples = getloadavg(loadavg, 3);
    sprintf(loadavg1, "%.3f\0", loadavg[LOADAVG_1MIN]);
    sprintf(loadavg5, "%.3f\0", loadavg[LOADAVG_5MIN]);
    sprintf(loadavg15, "%.3f\0", loadavg[LOADAVG_15MIN]);

    check_loadavg1_state();
    check_loadavg5_state();
    check_loadavg15_state();

}


/*
 * Function: conv_alarm_state : This function returns appropriate charecter
 * string for each integer alarm type
 */

char           *
conv_alarm_state(int state)
{
    switch (state) {
	case 0:
	return "OK\0";
    case 1:
	return "ERROR\0";
    default:
	return "NULL\0";
    }
}



/*
 * read_load_thresholds: This function is called when a registered token (see
 * Init_demo_module_4 function) is found in the appropriate configuration
 * file. The token's values (thresholds) are then stored in some variables.
 */

void
read_load_thresholds(const char *token, char *cptr)
{

    if (strcmp(token, "threshold_loadavg1") == 0) {
	threshold_loadavg1 = atof(cptr);
    } else if (strcmp(token, "threshold_loadavg5") == 0) {
	threshold_loadavg5 = atof(cptr);
    } else if (strcmp(token, "threshold_loadavg15") == 0) {
	threshold_loadavg15 = atof(cptr);
    } else {
	/* Do nothing */
    }

    return;

}



/* send_trap: This function generates an snmpv2 trap */

void
send_trap(u_char * hostname, u_char * modulename, oid * trapoid, int size, u_char * status, u_char * description)
{

    /* This is the notification type itself. This is statusChange trap */

    oid             notification_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 2, 1};

    size_t          notification_oid_len = OID_LENGTH(notification_oid);

    /*
     * In the notification, we have to assign our notification OID to the
     * snmpTrapOID.0 object. Here is it's definition.
     */

    oid             objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
    size_t          objid_snmptrap_len = OID_LENGTH(objid_snmptrap);

    /*
     * here is where we store the variables to be sent in the trap
     */

    netsnmp_variable_list *notification_vars = NULL;

    oid             hostname_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 3, 1, 0};

    size_t          hostname_oid_len = OID_LENGTH(hostname_oid);


    oid             modulename_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 3, 2, 0};

    size_t          modulename_oid_len = OID_LENGTH(modulename_oid);


    oid             nodeoid_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 3, 3, 0};

    size_t          nodeoid_oid_len = OID_LENGTH(nodeoid_oid);


    oid             status_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 3, 4, 0};

    size_t          status_oid_len = OID_LENGTH(status_oid);


    oid             description_oid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 3, 5, 0};

    size_t          description_oid_len = OID_LENGTH(description_oid);


    /*
     * add in the trap definition object
     */

    snmp_varlist_add_variable(&notification_vars,
    /*
     * the snmpTrapOID.0 variable
     */
			      objid_snmptrap, objid_snmptrap_len,
    /*
     * value type is an OID
     */
			      ASN_OBJECT_ID,
    /*
     * value contents is our notification OID
     */
			      (u_char *) notification_oid,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      notification_oid_len * sizeof(oid));


    /*
     * if we wanted to insert additional objects, we'd do it here
     */


    snmp_varlist_add_variable(&notification_vars,
			      hostname_oid, hostname_oid_len,
    /*
     * value type is an OID
     */
			      ASN_OCTET_STR,
    /*
     * value contents is our notification OID
     */
			      (u_char *) hostname,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      strlen((char *) hostname));


    snmp_varlist_add_variable(&notification_vars,
			      modulename_oid, modulename_oid_len,
    /*
     * value type is an OID
     */
			      ASN_OCTET_STR,
    /*
     * value contents is our notification OID
     */
			      (u_char *) modulename,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      strlen((char *) modulename));


    snmp_varlist_add_variable(&notification_vars,
			      nodeoid_oid, nodeoid_oid_len,
    /*
     * value type is an OID
     */
			      ASN_OBJECT_ID,
    /*
     * value contents is our notification OID
     */
			      (u_char *) trapoid,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      size * sizeof(oid));


    snmp_varlist_add_variable(&notification_vars,
			      status_oid, status_oid_len,
    /*
     * value type is an OID
     */
			      ASN_OCTET_STR,
    /*
     * value contents is our notification OID
     */
			      (u_char *) status,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      strlen((char *) status));


    snmp_varlist_add_variable(&notification_vars,
			      description_oid, description_oid_len,
    /*
     * value type is an OID
     */
			      ASN_OCTET_STR,
    /*
     * value contents is our notification OID
     */
			      (u_char *) description,
    /*
     * size in bytes = oid length * sizeof(oid)
     */
			      strlen((char *) description));


    /* SEND THE TRAP !!!! */

    send_v2trap(notification_vars);

    /*
     * free the created notification variable list
     */

    DEBUGMSGTL(("example_notification", "cleaning up\n"));
    snmp_free_varbind(notification_vars);

    return;

}



/*
 * check_loadavg1_state: This function does 2 things: step-1) Determine the
 * current alarm state of the node by comparing it's value with threshold.
 * step-2) Depending on the new state of the node, generate a trap
 */

void 
check_loadavg1_state()
{

    /* Trap stuff */

    oid             trapoid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 1, 0};
    u_char          status[8];
    u_char          description[] = "Load Average over last 1 minute crossed the threshold \0";
    int             size;
    int             new_loadavg1_state = OK;
    float           currentLoad = atof(loadavg1);

    /* Step-1 */

    /* If threshold is crossed, set state to ERROR */
    if (currentLoad > threshold_loadavg1) {
	new_loadavg1_state = ERROR;
    }
    /* Step-2 */

    /* Depending on the new state, send trap if necessary */

    size = sizeof(trapoid) / sizeof(oid);
    strcpy((char *) status, conv_alarm_state(new_loadavg1_state));

    if (new_loadavg1_state > prev_loadavg1_state) {
	/* Send trap */
	send_trap(hostName, moduleName, trapoid, size, status, description);
	prev_loadavg1_state = new_loadavg1_state;
    } else if (new_loadavg1_state == prev_loadavg1_state) {
	/* No Change in state .. Do nothing */
    } else if (new_loadavg1_state < prev_loadavg1_state) {
	if (new_loadavg1_state == OK) {
	    /* Send OK trap */
	    prev_loadavg1_state = OK;
	    send_trap(hostName, moduleName, trapoid, size, status, description);
	}
    }
}

/*
 * check_loadavg5_state: This function does 2 things: step-1) Determine the
 * current alarm state of the node by comparing it's value with threshold.
 * step-2) Depending on the new state of the node, generate a trap
 */


void 
check_loadavg5_state()
{

    /* Trap stuff */

    oid             trapoid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 2, 0};
    u_char          status[8];
    u_char          description[] = "Load Average over last 5 minute crossed the threshold \0";
    int             size;

    int             new_loadavg5_state = OK;
    double          currentLoad = atof(loadavg5);

    /* If threshold is crossed, set state to ERROR */
    if (currentLoad > threshold_loadavg5)
	new_loadavg5_state = ERROR;

    /* Depending on the new state, send trap if necessary */

    size = sizeof(trapoid) / sizeof(oid);
    strcpy((char *) status, conv_alarm_state(new_loadavg5_state));

    if (new_loadavg5_state > prev_loadavg5_state) {
	/* Send trap */
	send_trap(hostName, moduleName, trapoid, size, status, description);
	prev_loadavg5_state = new_loadavg5_state;
    } else if (new_loadavg5_state == prev_loadavg5_state) {
	/* No Change in state .. Do nothing */
    } else if (new_loadavg5_state < prev_loadavg5_state) {
	if (new_loadavg5_state == OK) {
	    /* Send OK trap */
	    prev_loadavg5_state = OK;
	    send_trap(hostName, moduleName, trapoid, size, status, description);
	}
    }
}

/*
 * check_loadavg15_state: This function does 2 things: step-1) Determine the
 * current alarm state of the node by comparing it's value with threshold.
 * step-2) Depending on the new state of the node, generate a trap
 */


void 
check_loadavg15_state()
{

    /* Trap stuff */

    oid             trapoid[] = {1, 3, 6, 1, 4, 1, 42, 2, 2, 4, 4, 4, 1, 3, 0};
    u_char          status[8];
    u_char          description[] = "Load Average over last 15 minute crossed the threshold \0";
    int             size;

    int             new_loadavg15_state = OK;
    double          currentLoad = atof(loadavg15);

    /* If threshold is crossed, set state to ERROR */
    if (currentLoad > threshold_loadavg15)
	new_loadavg15_state = ERROR;

    /* Depending on the new state, send trap if necessary */

    size = sizeof(trapoid) / sizeof(oid);
    strcpy((char *) status, conv_alarm_state(new_loadavg15_state));

    if (new_loadavg15_state > prev_loadavg15_state) {
	/* Send trap */
	prev_loadavg15_state = new_loadavg15_state;
	send_trap(hostName, moduleName, trapoid, size, status, description);
    } else if (new_loadavg15_state == prev_loadavg15_state) {
	/* No Change in state .. Do nothing */
    } else if (new_loadavg15_state < prev_loadavg15_state) {
	if (new_loadavg15_state == OK) {
	    /* Send OK trap */
	    prev_loadavg15_state = OK;
	    send_trap(hostName, moduleName, trapoid, size, status, description);
	}
    }
}
