/* * Template MIB group implementation - example.c * */ /* * include important headers */ #include #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #include #include /* * header_generic() comes from here */ #include "util_funcs/header_generic.h" /* * include our .h file */ #include "example.h" /* * Certain objects can be set via configuration file directives. * These variables hold the values for such objects, as they need to * be accessible to both the config handlers, and the callback routine. */ #define EXAMPLE_STR_LEN 300 #define EXAMPLE_STR_DEFAULT "life the universe and everything" int example_int = 42; char example_str[EXAMPLE_STR_LEN]; /* * Forward declarations for the config handlers */ void example_parse_config_exampleint(const char *token, char *cptr); void example_parse_config_examplestr(const char *token, char *cptr); void example_free_config_exampleint(void); void example_free_config_examplestr(void); /********************* * * Initialisation & common implementation functions * *********************/ /* * This array structure defines a representation of the * MIB being implemented. * * The type of the array is 'struct variableN', where N is * large enough to contain the longest OID sub-component * being loaded. This will normally be the maximum value * of the fifth field in each line. In this case, the second * and third entries are both of size 2, so we're using * 'struct variable2' * * The supported values for N are listed in * If the value you need is not listed there, simply use the * next largest that is. * * The format of each line is as follows * (using the first entry as an example): * 1: EXAMPLESTRING: * The magic number defined in the example header file. * This is passed to the callback routine and is used * to determine which object is being queried. * 2: ASN_OCTET_STR: * The type of the object. * Valid types are listed in * 3: NETSNMP_OLDAPI_RONLY (or NETSNMP_OLDAPI_RWRITE): * Whether this object can be SET or not. * 4: var_example: * The callback routine, used when the object is queried. * This will usually be the same for all objects in a module * and is typically defined later in this file. * 5: 1: * The length of the OID sub-component (the next field) * 6: {1}: * The OID sub-components of this entry. * In other words, the bits of the full OID that differ * between the various entries of this array. * This value is appended to the common prefix (defined later) * to obtain the full OID of each entry. */ struct variable2 example_variables[] = { {EXAMPLESTRING, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_example, 1, {1}}, {EXAMPLEINTEGER, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_example, 2, {2, 1}}, {EXAMPLEOBJECTID, ASN_OBJECT_ID, NETSNMP_OLDAPI_RONLY, var_example, 2, {2, 2}}, {EXAMPLETIMETICKS, ASN_TIMETICKS, NETSNMP_OLDAPI_RONLY, var_example, 1, {3}}, {EXAMPLEIPADDRESS, ASN_IPADDRESS, NETSNMP_OLDAPI_RONLY, var_example, 1, {4}}, {EXAMPLECOUNTER, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_example, 1, {5}}, {EXAMPLEGAUGE, ASN_GAUGE, NETSNMP_OLDAPI_RONLY, var_example, 1, {6}}, {EXAMPLETRIGGERTRAP, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_example, 1, {7}}, {EXAMPLETRIGGERTRAP2, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_example, 1, {8}} }; /* * This array defines the OID of the top of the mib tree that we're * registering underneath. * Note that this needs to be the correct size for the OID being * registered, so that the length of the OID can be calculated. * The format given here is the simplest way to achieve this. */ oid example_variables_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 254 }; /* * This function is called at the time the agent starts up * to do any initializations that might be required. * * In theory it is optional and can be omitted if no * initialization is needed. In practise, every module * will need to register itself (or the objects being * implemented will not appear in the MIB tree), and this * registration is typically done here. * * If this function is added or removed, you must re-run * the configure script, to detect this change. */ void init_example(void) { /* * Register ourselves with the agent to handle our mib tree. * The arguments are: * descr: A short description of the mib group being loaded. * var: The variable structure to load. * (the name of the variable structure defined above) * vartype: The type of this variable structure * theoid: The OID pointer this MIB is being registered underneath. */ REGISTER_MIB("example", example_variables, variable2, example_variables_oid); /* * Register config handlers for the two objects that can be set * via configuration file directive. * Also set a default value for the string object. Note that the * example integer variable was initialised above. */ strlcpy(example_str, EXAMPLE_STR_DEFAULT, sizeof(example_str)); snmpd_register_config_handler("exampleint", example_parse_config_exampleint, example_free_config_exampleint, "exampleint value"); snmpd_register_config_handler("examplestr", example_parse_config_examplestr, example_free_config_examplestr, "examplestr value"); snmpd_register_config_handler("examplestring", example_parse_config_examplestr, example_free_config_examplestr, "examplestring value"); /* * One common requirement is to read values from the kernel. * This is usually initialised here, to speed up access when the * information is read in, as a response to an incoming request. * * This module doesn't actually use this mechanism, * so this call is commented out here. */ /* * auto_nlist( "example_symbol", 0, 0 ); */ } /********************* * * Configuration file handling functions * *********************/ void example_parse_config_exampleint(const char *token, char *cptr) { example_int = atoi(cptr); } void example_parse_config_examplestr(const char *token, char *cptr) { /* * Make sure the string fits in the space allocated for it. */ if (strlen(cptr) < sizeof(example_str)) strcpy(example_str, cptr); else { /* * Truncate the string if necessary. * An alternative approach would be to log an error, * and discard this value altogether. */ sprintf(example_str, "%.*s...", (int) (sizeof(example_str) - 4), cptr); netsnmp_assert(strlen(example_str) < sizeof(example_str)); } } /* * We don't need to do anything special when closing down */ void example_free_config_exampleint(void) { } void example_free_config_examplestr(void) { } /********************* * * System specific implementation functions * *********************/ /* * Define the callback function used in the example_variables structure. * This is called whenever an incoming request refers to an object * within this sub-tree. * * Four of the parameters are used to pass information in. * These are: * vp The entry from the 'example_variables' array for the * object being queried. * name The OID from the request. * length The length of this OID. * exact A flag to indicate whether this is an 'exact' request * (GET/SET) or an 'inexact' one (GETNEXT/GETBULK). * * Four of the parameters are used to pass information back out. * These are: * name The OID being returned. * length The length of this OID. * var_len The length of the answer being returned. * write_method A pointer to the SET function for this object. * * Note that name & length serve a dual purpose in both roles. */ u_char * var_example(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { /* * The result returned from this function needs to be a pointer to * static data (so that it can be accessed from outside). * Define suitable variables for any type of data we may return. */ static char string[EXAMPLE_STR_LEN]; /* for EXAMPLESTRING */ static oid oid_ret[8]; /* for EXAMPLEOBJECTID */ static long long_ret; /* for everything else */ /* * Before returning an answer, we need to check that the request * refers to a valid instance of this object. The utility routine * 'header_generic' can be used to do this for scalar objects. * * This routine 'header_simple_table' does the same thing for "simple" * tables. (See the AGENT.txt file for the definition of a simple table). * * Both these utility routines also set up default values for the * return arguments (assuming the check succeeded). * The name and length are set suitably for the current object, * var_len assumes that the result is an integer of some form, * and write_method assumes that the object cannot be set. * * If these assumptions are correct, this callback routine simply * needs to return a pointer to the appropriate value (using 'long_ret'). * Otherwise, 'var_len' and/or 'write_method' should be set suitably. */ DEBUGMSGTL(("example", "var_example entered\n")); if (header_generic(vp, name, length, exact, var_len, write_method) == MATCH_FAILED) return NULL; /* * Many object will need to obtain data from the operating system in * order to return the appropriate value. Typically, this is done * here - immediately following the 'header' call, and before the * switch statement. This is particularly appropriate if a single * interface call can return data for all the objects supported. * * This example module does not rely on external data, so no such * calls are needed in this case. */ /* * Now use the magic number from the variable pointer 'vp' to * select the particular object being queried. * In each case, one of the static objects is set up with the * appropriate information, and returned mapped to a 'u_char *' */ switch (vp->magic) { case EXAMPLESTRING: strcpy(string, example_str); /* * Note that the assumption that the answer will be an * integer does not hold true in this case, so the length * of the answer needs to be set explicitly. */ *var_len = strlen(string); return (u_char *) string; case EXAMPLEINTEGER: /* * Here the length assumption is correct, but the * object is writeable, so we need to set the * write_method pointer as well as the current value. */ long_ret = example_int; *write_method = write_exampleint; return (u_char *) & long_ret; case EXAMPLEOBJECTID: oid_ret[0] = 1; oid_ret[1] = 3; oid_ret[2] = 6; oid_ret[3] = 1; oid_ret[4] = 4; oid_ret[5] = oid_ret[6] = oid_ret[7] = 42; /* * Again, the assumption regarding the answer length is wrong. */ *var_len = 8 * sizeof(oid); return (u_char *) oid_ret; case EXAMPLETIMETICKS: /* * Here both assumptions are correct, * so we just need to set up the answer. */ long_ret = 363136200; /* 42 days, 42 minutes and 42.0 seconds */ return (u_char *) & long_ret; case EXAMPLEIPADDRESS: /* * ipaddresses get returned as a long. ick */ /* * we're returning 127.0.0.1 */ { in_addr_t addr = ntohl(INADDR_LOOPBACK); memcpy(string, &addr, sizeof(addr)); *var_len = sizeof(addr); } return (u_char *) string; case EXAMPLECOUNTER: long_ret = 42; return (u_char *) & long_ret; case EXAMPLEGAUGE: long_ret = 42; /* Do we detect a theme running through these answers? */ return (u_char *) & long_ret; case EXAMPLETRIGGERTRAP: /* * This object is essentially "write-only". * It only exists to trigger the sending of a trap. * Reading it will always return 0. */ long_ret = 0; *write_method = write_exampletrap; return (u_char *) & long_ret; case EXAMPLETRIGGERTRAP2: /* * This object is essentially "write-only". * It only exists to trigger the sending of a v2 trap. * Reading it will always return 0. */ long_ret = 0; *write_method = write_exampletrap2; return (u_char *) & long_ret; default: /* * This will only be triggered if there's a problem with * the coding of the module. SNMP requests that reference * a non-existant OID will be directed elsewhere. * If this branch is reached, log an error, so that * the problem can be investigated. */ DEBUGMSGTL(("snmpd", "unknown sub-id %d in examples/var_example\n", vp->magic)); } /* * If we fall through to here, fail by returning NULL. * This is essentially a continuation of the 'default' case above. */ return NULL; } /********************* * * Writeable object SET handling routines * *********************/ int write_exampleint(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { /* * Define an arbitrary maximum permissible value */ #define MAX_EXAMPLE_INT 100 static long intval; static long old_intval; switch (action) { case RESERVE1: /* * Check that the value being set is acceptable */ if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("example", "%x not integer type", var_val_type)); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(long)) { DEBUGMSGTL(("example", "wrong length %" NETSNMP_PRIz "u", var_val_len)); return SNMP_ERR_WRONGLENGTH; } intval = *((long *) var_val); if (intval > MAX_EXAMPLE_INT) { DEBUGMSGTL(("example", "wrong value %lx", intval)); return SNMP_ERR_WRONGVALUE; } break; case RESERVE2: /* * This is conventially where any necesary * resources are allocated (e.g. calls to malloc) * Here, we are using static variables * so don't need to worry about this. */ break; case FREE: /* * This is where any of the above resources * are freed again (because one of the other * values being SET failed for some reason). * Again, since we are using static variables * we don't need to worry about this either. */ break; case ACTION: /* * Set the variable as requested. * Note that this may need to be reversed, * so save any information needed to do this. */ old_intval = example_int; example_int = intval; break; case UNDO: /* * Something failed, so re-set the * variable to its previous value * (and free any allocated resources). */ example_int = old_intval; break; case COMMIT: /* * Everything worked, so we can discard any * saved information, and make the change * permanent (e.g. write to the config file). * We also free any allocated resources. * * In this case, there's nothing to do. */ break; } return SNMP_ERR_NOERROR; } int write_exampletrap(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { long intval; DEBUGMSGTL(("example", "write_exampletrap entered: action=%d\n", action)); switch (action) { case RESERVE1: /* * The only acceptable value is the integer 1 */ if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("example", "%x not integer type", var_val_type)); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(long)) { DEBUGMSGTL(("example", "wrong length %" NETSNMP_PRIz "u", var_val_len)); return SNMP_ERR_WRONGLENGTH; } intval = *((long *) var_val); if (intval != 1) { DEBUGMSGTL(("example", "wrong value %lx", intval)); return SNMP_ERR_WRONGVALUE; } break; case RESERVE2: /* * No resources are required.... */ break; case FREE: /* * ... so no resources need be freed */ break; case ACTION: /* * Having triggered the sending of a trap, * it would be impossible to revoke this, * so we can't actually invoke the action here. */ break; case UNDO: /* * We haven't done anything yet, * so there's nothing to undo */ break; case COMMIT: /* * Everything else worked, so it's now safe * to trigger the trap. * Note that this is *only* acceptable since * the trap sending routines are "failsafe". * (In fact, they can fail, but they return no * indication of this, which is the next best thing!) */ DEBUGMSGTL(("example", "write_exampletrap sending the trap\n")); send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 99); DEBUGMSGTL(("example", "write_exampletrap trap sent\n")); break; } return SNMP_ERR_NOERROR; } /* * this documents how to send a SNMPv2 (and higher) trap via the * send_v2trap() API. * * Coding SNMP-v2 Trap: * * The SNMPv2-Trap PDU contains at least a pair of object names and * values: - sysUpTime.0 whose value is the time in hundredths of a * second since the netwok management portion of system was last * reinitialized. - snmpTrapOID.0 which is part of the trap group SNMPv2 * MIB whose value is the object-id of the specific trap you have defined * in your own MIB. Other variables can be added to characterize the * trap. * * The function send_v2trap adds automatically the two objects but the * value of snmpTrapOID.0 is 0.0 by default. If you want to add your trap * name, you have to reconstruct this object and to add your own * variable. * */ int write_exampletrap2(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { long intval; /* * these variables will be used when we send the trap */ oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; /* snmpTrapOID.0 */ oid demo_trap[] = { 1, 3, 6, 1, 4, 1, 2021, 13, 990 }; /*demo-trap */ oid example_string_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 254, 1, 0 }; static netsnmp_variable_list var_trap; static netsnmp_variable_list var_obj; DEBUGMSGTL(("example", "write_exampletrap2 entered: action=%d\n", action)); switch (action) { case RESERVE1: /* * The only acceptable value is the integer 1 */ if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("example", "%x not integer type", var_val_type)); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(long)) { DEBUGMSGTL(("example", "wrong length %" NETSNMP_PRIz "u", var_val_len)); return SNMP_ERR_WRONGLENGTH; } intval = *((long *) var_val); if (intval != 1) { DEBUGMSGTL(("example", "wrong value %lx", intval)); return SNMP_ERR_WRONGVALUE; } break; case RESERVE2: /* * No resources are required.... */ break; case FREE: /* * ... so no resources need be freed */ break; case ACTION: /* * Having triggered the sending of a trap, * it would be impossible to revoke this, * so we can't actually invoke the action here. */ break; case UNDO: /* * We haven't done anything yet, * so there's nothing to undo */ break; case COMMIT: /* * Everything else worked, so it's now safe * to trigger the trap. * Note that this is *only* acceptable since * the trap sending routines are "failsafe". * (In fact, they can fail, but they return no * indication of this, which is the next best thing!) */ /* * trap definition objects */ var_trap.next_variable = &var_obj; /* next variable */ var_trap.name = objid_snmptrap; /* snmpTrapOID.0 */ var_trap.name_length = sizeof(objid_snmptrap) / sizeof(oid); /* number of sub-ids */ var_trap.type = ASN_OBJECT_ID; var_trap.val.objid = demo_trap; /* demo-trap objid */ var_trap.val_len = sizeof(demo_trap); /* length in bytes (not number of subids!) */ /* * additional objects */ var_obj.next_variable = NULL; /* No more variables after this one */ var_obj.name = example_string_oid; var_obj.name_length = sizeof(example_string_oid) / sizeof(oid); /* number of sub-ids */ var_obj.type = ASN_OCTET_STR; /* type of variable */ var_obj.val.string = (u_char *)example_str; /* value */ var_obj.val_len = strlen(example_str); DEBUGMSGTL(("example", "write_exampletrap2 sending the v2 trap\n")); send_v2trap(&var_trap); DEBUGMSGTL(("example", "write_exampletrap2 v2 trap sent\n")); break; } return SNMP_ERR_NOERROR; }