/*
 * 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.
 *
 */

/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.scalar.conf,v 1.5 2002/07/18 14:18:52 dts12 Exp $
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "demo_module_7.h"

static long fileX_data = 111; // Hardcoded size of fileX context name string
static long fileY_data = 999; // Hardcoded size of fileY context name string

static long PRIORITY = 0;
static long SUB_ID = 0;
static long RANGE_UBOUND = 0;

// Gets size of file
static oid me1filesize_oid[] =      { 1,3,6,1,4,1,42,2,2,4,4,6,1,1,0 };

// Registers a context
static oid me1createContext_oid[] = { 1,3,6,1,4,1,42,2,2,4,4,6,1,2,0 };

// Unregisters a context
static oid me1removeContext_oid[] = { 1,3,6,1,4,1,42,2,2,4,4,6,1,3,0 };

// Name of file whose size should be returned
char *filename;

/*
  This example module registers context name strings that represent files.
  Get requests to these contexts will retrieve the size of the file.

  The module can be dynamically updated to register new file names, by
  issuing an snmpset command.  You do not need to edit the module in
  application passes the file name to the module by issuing an snmpset
  command, such as the following:

	snmpset -v 3 -u < user_name > -n "< file_name >"
	-l authNoPriv -A " <password >" < agent_host_name >
	< createContext OID > .

  The module registers the set_createContext handler to handle incoming
  snmp set requests for this OID. The set_createContext handler registers
  the new filename as a context string in the contextName member of the
  netsnmp_registration_handler struct for the filesize_oid.

  Subsequent snmpget requests for the size of the file will
  return its size in blocks:  For example:

	snmpget -v 3 -u < user_name > -n "< file_name >"
	-l authNoPriv -A "< password  >" < agent_host_name >
	< filesize_oid >  */


/* Initializes the filesize module */

void
init_demo_module_7(void)
{

	char *filexcon = "fileX";
	char *fileycon = "fileY";


	netsnmp_handler_registration *myreg1;
	int status;

	/*
	Create a read-only registration handler named filesize,
	which calls the get_filesize function to service snmp requests
	for the me1filesize_oid object.  The OID_LENGTH argument
	calculates the length of the me1filesize_oid. */
	DEBUGMSGTL(("demo_module_7", "Initializing\n"));
	myreg1 = netsnmp_create_handler_registration
					("filesize",
					get_filesize,
					me1filesize_oid,
					OID_LENGTH(me1filesize_oid),
					HANDLER_CAN_RONLY);

	myreg1->contextName = filexcon;
	status = netsnmp_register_read_only_instance(myreg1);
	DEBUGMSGTL(("demo_module_7", "init reg1 status %d:\n",  status));


	myreg1 = netsnmp_create_handler_registration
					("filesize",
					get_filesize,
					me1filesize_oid,
					OID_LENGTH(me1filesize_oid),
					HANDLER_CAN_RONLY);

	myreg1->contextName = fileycon;
	status = netsnmp_register_read_only_instance(myreg1);
	DEBUGMSGTL(("demo_module_7", "init reg2 status %d:\n",  status));


	/*
	Create a read-write registration handler named filesize,
	which calls the set_createContext function to service snmp requests
	for the me1createContext_oid object.  The OID_LENGTH argument
	calculates the length of the me1createContext_oid. */
	myreg1 = netsnmp_create_handler_registration
					("filesize",
					set_createContext,
					me1createContext_oid,
					OID_LENGTH(me1createContext_oid),
					HANDLER_CAN_RWRITE);

	status = netsnmp_register_instance(myreg1);
	DEBUGMSGTL(("filesize", "init reg3 status %d:\n",  status));

	/*
	Create a read-write registration handler named filesize,
	which calls the set_removeContext function to service snmp requests
	for the me1removeContext_oid object.  The OID_LENGTH argument
	calculates the length of the me1removeContext_oid. */
	myreg1 = netsnmp_create_handler_registration
					("filesize",
					set_removeContext,
					me1removeContext_oid,
					OID_LENGTH(me1removeContext_oid),
					HANDLER_CAN_RWRITE);

	status = netsnmp_register_instance(myreg1);
	DEBUGMSGTL(("demo_module_7", "init reg4 status %d:\n", status));
}

	/*
	This handler handles set requests on the m1createContext_oid
	by registering a context. The handler extracts the string from
	the snmp set request and uses it to register a new context for
	the mefilesize_oid.

	This handler handles get requests by returning the last context
	name that was registered.

	For detailed info. on net-snmp set processing,
	see "http://www.net-snmp.org/tutorial-5/toolkit/mib_module/index.html"
	net-snmp call each SNMP mode in sequence. The case statement
	transfers control to the default: case when no other condition
	is satisfied. */

int
set_createContext(netsnmp_mib_handler *handler,
	netsnmp_handler_registration *reginfo,
	netsnmp_agent_request_info *reqinfo,
	netsnmp_request_info *requests)
{

	netsnmp_handler_registration *myreg;
	char *context_names[256];
	int status;
	DEBUGMSGTL(("demo_module_7", "set_createContext CALLED\n"));
	DEBUGMSGTL(("demo_module_7", "reqinfo->mode = %d\n", reqinfo->mode));
	switch (reqinfo -> mode) {

	case MODE_SET_RESERVE1:
		break;

	case MODE_SET_RESERVE2:
		break;

	case MODE_SET_FREE:
		break;

	case MODE_SET_ACTION:

		DEBUGMSGTL(("demo_module_7", "MODE_SET_ACTION CALLED\n"));
		DEBUGMSGTL(("demo_module_7", "requests->requestvb->val = %s\n",
			(u_char *) requests->requestvb->val.string));

                // You must allocate memory for this variable because
		// the unregister_mib function frees it.
		filename = malloc(requests->requestvb->val_len + 1);
		snprintf(filename, requests->requestvb->val_len + 1, "%s",
		    (u_char *) requests->requestvb->val.string);
	        filename[requests->requestvb->val_len + 1] = '\0';
	
		DEBUGMSGTL(("demo_module_7", "filename = %s\n", filename));

		/*
		Create a registration handler for the me1filesize_oid
		object in the new context name specified by
		the snmp set on the me1createContext OID. */
		myreg = netsnmp_create_handler_registration
			("test", get_test, me1filesize_oid,
			OID_LENGTH(me1filesize_oid),
			HANDLER_CAN_RONLY);
		myreg->contextName = filename;
		status = netsnmp_register_read_only_instance(myreg);
		DEBUGMSGTL(("demo_module_7", "status %d:\n",  status));
		break;

	case MODE_SET_COMMIT:
		break;

	case MODE_SET_UNDO:
		/*
		Not handling the undo case because we don't care about
		multi-phase sets for this example. */
		break;

	case MODE_GET:
		snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
			(u_char *)filename, sizeof (filename));

	default:
		/* we should never get here, so this is a really bad error */
	DEBUGMSGTL(("demo_module_7", "default CALLED\n"));
	}

	return (SNMP_ERR_NOERROR);
}

	/*
	This handler handles set requests on the m1removeContext_oid
	for detailed info. on net-snmp set processing,
	see "http://www.net-snmp.org/tutorial-5/toolkit/mib_module/index.html"
	net-snmp call each SNMP mode in sequence. The case statement
	transfers control to the default: case when no other condition
	is satisfied. */

int
set_removeContext(netsnmp_mib_handler *handler,
	netsnmp_handler_registration *reginfo,
	netsnmp_agent_request_info *reqinfo,
	netsnmp_request_info *requests)
{
	DEBUGMSGTL(("demo_module_7", "remove_filesize CALLED\n"));
	switch (reqinfo->mode) {
		case MODE_SET_RESERVE1:
			break;
		case MODE_SET_RESERVE2:
			break;
		case MODE_SET_ACTION:
			DEBUGMSGTL(("demo_module_7",
				"remove_filesize MODE_SET_ACTION CALLED\n"));
			DEBUGMSGTL(("demo_module_7",
			    "remove_filesize MODE_SET_ACTION CALLED\n"));
			snprintf(filename, requests->requestvb->val_len + 1,
			    "%s",  (u_char *) requests->requestvb->val.string);
			filename[requests->requestvb->val_len + 1] = '\0';
			DEBUGMSGTL(("demo_module_7",
				"filename after snmpset = %s:\n",
				filename));
			unregister_mib_context(me1filesize_oid,
				OID_LENGTH(me1filesize_oid),
				PRIORITY, SUB_ID, RANGE_UBOUND,
				filename);
			break;

		case MODE_SET_COMMIT:
			break;

		case MODE_SET_FREE:
			break;

		case MODE_SET_UNDO:
			/*
			Not handling the undo case because we don't care about
			multi-phase sets for this example. */
			break;

		default:
			/*
			we should never get here, so this
			is a really bad error */
			DEBUGMSGTL(("demo_module_7", "set_removeContext CALLED\n"));
	}
	return (SNMP_ERR_NOERROR);
}



int
get_filesize(netsnmp_mib_handler *handler,
	netsnmp_handler_registration *reginfo,
	netsnmp_agent_request_info *reqinfo,
	netsnmp_request_info *requests)
{
	/*
	This handler is never called for a getnext
	if it is registered as an instance. An instance
	handler only delivers one request at a time, so
	we do not need to loop over a list of requests. */

	DEBUGMSGTL(("demo_module_7", "get_filesize CALLED\n"));
	DEBUGMSGTL(("demo_module_7", "INCOMING CONTEXT NAME = %s:\n",
		reginfo->contextName));

	switch (reqinfo->mode) {

		case MODE_GET:

		if (strcmp(reginfo->contextName, "fileX") == 0)
			snmp_set_var_typed_value(requests->requestvb,
				ASN_INTEGER, (u_char *) &fileX_data
					/* pointer to the scalar's data */,
				sizeof (fileX_data)
					/* the length of the data in bytes */);

		else if (strcmp(reginfo->contextName, "fileY") == 0)
			snmp_set_var_typed_value(requests->requestvb,
				ASN_INTEGER, (u_char *)	&fileY_data
					/* Pointer to the scalar's data */,
				sizeof (fileY_data)
					/* Length of the data in bytes */);
	break;

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

    return (SNMP_ERR_NOERROR);
}

int
get_test(netsnmp_mib_handler *handler,
	netsnmp_handler_registration *reginfo,
	netsnmp_agent_request_info *reqinfo,
	netsnmp_request_info *requests)
{
	/*
	This handler is called to handle snmpset requests for a new
	context name in the me1filesize_oid. If it is called to
	handle snmp get requests, the handler does not need to
	handle a GETNEXT if it is registered as an
	instance handler. Instance handlers only deliver one request
	at a time, so we do not need to loop over a list of requests. */

	struct stat buf;
	static int fd = 0;
	DEBUGMSGTL(("demo_module_7", "get_test CALLED\n"));
	DEBUGMSGTL(("demo_module_7", "INCOMING CONTEXT NAME = %s:\n",
		reginfo->contextName));
	switch (reqinfo->mode) {
	case MODE_GET:
		if (strcmp(reginfo->contextName, filename) == 0)
		// An open() for reading only returns without delay.
			if ((fd = open(filename, O_NONBLOCK |
				O_RDONLY)) == -1)
				DEBUGMSGTL(("demo_module_7", "ERROR\n"));

			if (fstat(fd, &buf) == -1)
				DEBUGMSGTL(("demo_module_7", "ERROR\n"));
			else
				DEBUGMSGTL(("demo_module_7",
				"FILE SIZE IN BYTES = %d:\n",
					buf.st_size));

				snmp_set_var_typed_value(requests->requestvb,
					ASN_INTEGER, (u_char *) &buf.st_size
					/* Pointer to the scalar's data */,
					sizeof (buf.st_size)
					/* The length of the data in bytes*/);
		break;

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