| /* |
| * Template MIB group implementation - example.c |
| * |
| */ |
| |
| /* |
| * include important headers |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| /* |
| * 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 <agent/var_struct.h> |
| * 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 <snmp_impl.h> |
| * 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 variales 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 = (unsigned 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; |
| } |