mib2c
.
#include <config.h> // local SNMP configuration details #include "mib_module_config.h" // list of which modules are supported #if HAVE_STDLIB_H #include <stdlib.h> #endif #if HAVE_STRING_H #include <string.h> #else #include <strings.h> #endif #include <sys/types.h>All of these will usually be the first files to be included.
#include "mibincl.h" // Standard set of SNMP includes #include "util_funcs.h" // utility function declarations #include "read_config.h" // if the module uses run-time // configuration controls #include "auto_nlist.h" // structures for a BSD-based // kernel using nlist #include "system.h" #include "name.h" // the module-specific headerThese conventionally come at the end of the list of includes. In between will come all the standard system-provided header files required for the library functions used in the file.
mib2c
struct variableN
(where N is the length of the longest suffix in the table). Thus
struct variable2 example_variables[] = { < individual entries go here > };Each entry corresponds to one object in the MIB tree (or one column in the case of table entries), and these should be listed in increasing OID order. A single entry consists of six fields:
RWRITE
or RONLY
)
{ EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}}If the magic numbers have not been defined in the header file, then they should be defined here, usually comming immediately before the corresponding variable entry. This is the technique used by
mib2c
.
<agent/var_struct.h>
),
being sufficient to meet the common requirements. If your particular module
needs a non-supported value, the easiest thing is simply to use the next
largest value that is supported.
The module also needs to declare the location within the MIB tree where it should be registered. This is done using a declaration of the form
oid example_variables_oid[] = { 1,3,6,1,4,1,2021,254 }where the contents of the array give the object identifier of the root of the module.
init_
{name}
(where {name} is the name of the module).
This is done by using the REGISTER_MIB
macro,
as follows:
REGISTER_MIB( "example", example_variables, variable2, example_variables_oid );where
"example"
is used for identification purposed
(and is usually the name being used for the module),
example_variables
is the structure defining the variables being implemented,
variable2
is the type used for this structure,
and example_variables_oid
is the location of the root.
In fact, this macro is simply a wrapper round the routine
register_mib()
, but the details of this can safely be
ignored, unless more control over the registration is required.
One common requirement, particularly on older operating systems or for the more obscure areas of the system, is to be able to read data directly from kernel memory. The preparation for this is typically done here by one or more statements of the form
#ifdef {NAME}_SYMBOL auto_nlist( {NAME}_SYMBOL, 0, 0); #endifwhere {NAME}_SYMBOL is defined as part of the system-specific configuration, to be the name of the appropriate kernel variable or data structure. (The two 0 values are because the kernel information is simply being primed at this point - this call will be reused later when the actual values are required). Note that this is probably the first thing described so far which isn't provided by
mib2c
!
Other possibilities for initialisation may include registering config file
directive handlers (which are documented in the read_config(5)
man page), and registering the MIB module (either in whole or in part)
in the sysOR table.
The first of these is covered in the example module, and the
second in many of the other modules within the main UCD distribution.
Four of these parameters are used for passing in information about the request, these being:
struct variable *vp; // The entry in theFour of the parameters are used to return information about the answer. The function also returns a pointer to the actual data for the variable requested (or NULL if this data is not available for any reason). The other result parameters are:variable
N array from the // header file, for the object under consideration. // Note that thename
field of this structure has been // completed into a fully qualified OID, by prepending // the prefix common to the whole array. oid *name; // The OID from the request int *length; // The length of this OID int exact; // A flag to indicate whether this is an exact // request (GET/SET) or an 'inexact' one (GETNEXT)
oid *name; // The OID being returned int *length; // The length of this OID int *var_len; // The length of the answer being returned WriteMethod **write_method; // A pointer to the SET function for this variableNote that two of the parameters (
name
and length
)
serve a dual purpose, being used for both input and output.
The first thing that this routine needs to do is to validate the request,
to ensure that it does indeed lie in the range implemented by this
particular module. This is done in slightly different ways, depending on
the style of the module, so this will be discussed in more detail later.
At the same time, it is common to retrieve some of the information needed
for answering the query.
Then the routine uses the Magic Number field from the vp
parameter to determine which of the possible variables being implemented
is being requested.
This is done using a switch
statement, which should have as many cases as
there are entries in the variableN array (or more precisely, as many as specify
this routine as their handler), plus an additional default
case to
handle an erroneous call.
Each branch of the switch statement needs to ensure that the return
parameters are filled in correctly, set up a (static) return variable
with the correct data, and then return a pointer to this value.
These can be done separately for each branch, or once at the start,
being overridden in particular branches if necessary.
In fact, the default validation routines make the assumption that
the variable is both read-only, and of integer type (which includes the
COUNTER
and GAUGE
types among others), and set the
return paramaters write_method
and var_len
appropriately.
These settings can then be corrected for those cases when either or
both of these assumptions are wrong.
Examples of this can be seen in the example module.
EXAMPLEINTEGER
is writeable, so this branch sets the
write_method
parameter,
and EXAMPLEOBJECTID
is not an integer, so this branch sets the
var_len
parameter.
In the case of EXAMPLESTRING
, both assumptions are wrong, so this
branch needs to set both these parameters explicitly.
Note that because the routine returns a pointer to a static result,
a suitable variable must be declared somewhere for this. Two global variables
are provided for this purpose - long_return
(for integer results) and
return_buf
(for other types). This latter is a generic array (of type
u_char
) that can contain up to 256 bytes of data.
Alternatively, static variables can be declared, either within the code file,
or local to this particular variable routine. This last is the approach
adopted by mib2c
, which defines four such local variables,
(long_ret, string, objid
and c64
).
mib2c
.
The main exceptions (which therefore need to be provided by the programmer)
are
var_len
(and possibly write_method
)
return parameters for variable types that are not recognised by mib2c
Last modified: Wednesday, 01-Aug-2018 04:41:28 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.