blob: dd97daf9a38aa981208b92fcdc71c713661a8d76 [file] [log] [blame]
/*
* 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;
}