Chapter 5. Generated Access Function Templates


This chapter describes function templates and related elements created in the access functions file and header file that are generated by the Agent Development Kit code generator (imibgenall). The following topics are discussed:

Function templates are skeletons that require the programmer to fill in implementation-specific code. The access functions source contains that portion of the agent that accesses the attributes of the managed resource, to modify those attributes in response to a SET request from a manager, or to retrieve current values in response to a GET request from a manager. This code is specific to your implementation because it depends upon the particular instrumentation available in the managed resource, such as application-specific APIs or UNIX system calls.

To generate the source code file containing the access functions for your agent, you target the MIB compiler/code generator (imibgenall) at a MIB group or table with the following command:

imibgenall MIBRootName

where MIBRootName is the target MIB group or table.

When you run the MIB compiler/code generator with this command, the code generator reads the my.asn1 file and generates access function skeletons corresponding to the target MIB objects. The code is created in a file named MIBRootName.c. The code generator also creates a declaration for a C structure that is used by the agent to cache the current values of the managed objects. This structure, v_MIBRootName, is declared in the header file, MIBRootName.h.

Normally the v_MIBRootName cache structure and the get_ObjectName functions do not need to be modified by the programmer.

Function Templates

This section describes the function templates that are created by the code generator.

init_MIBRootName

This function is called once by the agent the first time any object in the MIBRootName group or table is polled. You need to fill in this function skeleton with your initialization code. It is recommended that you print a reason for failure in case SNMP_FAILURE is returned.

Syntax

int init_MIBRootName()

Return Value

This function returns SNMP_SUCCESS if successful and SNMP_FAILURE if not successful. If this function returns a failure, the agent or subagent always returns "NO SUCH NAME" for any object in this MIB group or table.

refresh_MIBRootName

Syntax

int refresh_MIBRootName()

Return Value

This function has no return value for MIB groups with scalar objects. For tabular objects, the function returns either SNMP_SUCCESS or SNMP_FAILURE. If SNMP_FAILURE is returned, no SNMP operation can be performed on objects in that table for the next refresh period.

One such refresh function is generated for the MIB group or table that is the target passed to the code generator when generating an access functions skeleton file.

This refresh function is responsible for updating the agent cache by directly accessing the managed resource - for example, using an API or system calls - to obtain current values of the attributes of the managed resource. Accordingly, you must edit the refresh_MIBRootName function to add the code that directly accesses the managed resource and updates the object values in the agent cache.

For example, if your MIB group has a scalar object myQEntryCount under the myQ MIB group, there will be a line in the generated code such as the following:

v_myQ.val_myQEntryCount = dummy_value;

The object values for a given MIB group are cached in the record v_MIBRootName - in this example, v_myQ. This record is an instance of a structure of type s_MIBRootName, defined in the header file. You will need to replace dummy_value with the code that provides the actual value accessed from the managed resource - in this example, this might involve a call to the queue system's API.

For each field val_ObjectName in the cache structure, there is also a status field sts_ObjectName. For the previous example, there would be a line in the generated code that looks something like this:

v_myQ.sts_myQEntryCount = dummy_value;

This field should contain the status of the corresponding val_ObjectName field. If the value of ObjectName is not available, your code should set v_MIBRootName.sts_ObjectName to SNMP_INVALID. For example:

v_myQ.sts_myQEntryCount = SNMP_INVALID;

Also, for each field val_ObjectName in the structure that is of type string, there will be a corresponding field len_ObjectName, which should contain the length of the string in bytes.

The structures that constitute the agent's cache of current object values are used by the get_ObjectName functions to retrieve object values in response to a GET request from a management station. The agent tracks the age of the cache. Before calling the get_ObjectName functions to respond to manager GET requests, the agent checks to determine whether the cache is older than the refresh rate. If it is older, the agent calls refresh_MIBRootName to update the cache before calling the get_ObjectName functions for that MIB group. For more information about the refresh rate, see the following section, "Constants and Variables."

test_ObjectName

For each scalar or columnar object that is defined as read-write in the MIB, a test_ObjectName function skeleton is generated in the access functions file. You must flesh out each of these functions with code that checks to determine whether the value in the set request is valid for the target attribute in the managed resource.

The syntax of the test function depends upon the type of ObjectName.

Syntax

For scalar objects that are OIDs, strings, or IP addresses:

int test_ObjectName(value, length)
u_char *value;
int length;

For scalar objects other than strings, OIDs or IP addresses:

int test_ObjectName(value)
int value;

For columnar objects that are strings, OIDs, or IP addresses:

int test_ObjectName(row_index, value, length)
int row_index;
u_char *val_ptr;
int length;

For columnar objects other than OIDs, strings, or IP addresses:

int test_ObjectName(row_index,value)
int row_index;
int value;

Return Value

The test function should return SNMP_SUCCESS if and only if the requested set value is valid for the target. Otherwise, the function returns SNMP_FAILURE.

If an SNMP agent receives an SNMP SET request containing more than one variable, the agent either sets all the objects in the SET request or else none are set. This behavior is known as atomic set and is one of the requirements of the SNMP standard. Atomic sets are supported using two-phase commit. In order to implement this, for each read-write object a test and a set function is generated as part of the access functions file.

On receiving a SET request from the management station, the SNMP agent first invokes the test function corresponding to each object involved in the SET request. If all test functions return success, only then does the agent invoke the set functions corresponding to these objects. Otherwise, no set function is called.

For string objects or IP addresses, length is the length in bytes. For OIDs, length is the number of nodes in the absolute path from the root of the OID tree.

set_ObjectName

To support SNMP SET requests, a set_ObjectName function skeleton is generated in the access functions file for each scalar or columnar MIB object that is defined as read-write. You need to add the necessary code to directly access the managed resource, such as API function calls.

The syntax of this function depends upon the type of ObjectName.

Syntax

For scalar objects other than OIDs, strings, or IP addresses:

int set_ObjectName(value)
int value;

For scalar objects that are strings, OIDs, or IP addresses:

int set_ObjectName(value, length)
u_char *value;
int length;

For columnar objects that are strings, IP addresses, or OIDs:

int set_ObjectName(row_index, value, length)
int row_index;
u_char *value;
int length;

For columnar objects other than strings, IP addresses, or OIDs:

int set_ObjectName(row_index, value)
int row_index;
int value;

Return Value

This function should always return SNMP_SUCCESS.

The agent executes a set_ObjectName function if and only if a call to the corresponding test_ObjectName function returns SNMP_SUCCESS. The programmer must fill out the set_ObjectName function to directly access the managed resource to modify the specified attribute.

If an SNMP agent receives an SNMP set request containing more than one variable, the agent either sets all the objects in the SET request or else none are set. This "atomic set" behavior is one of the requirements of the SNMP standard. Atomic sets are supported using two-phase commit. In order to implement this, for each read-write object a test and a set function is generated as part of the access functions file. On receiving a set request from the management station, the SNMP agent first invokes the test function corresponding to each object involved in the set request. If all test functions return success, only then does the agent invoke the set functions corresponding to these objects. This function must always return either SNMP_SUCCESS or SNMP_FAILURE for an atomic set to work properly.

For string objects or IP addresses, length is the length in bytes. For OIDs, length is the number of nodes in the absolute path from the root of the OID tree.

test_TableName_row_create

Syntax

int test_TableName_row_create(row_index)
int row_index;

Return Value

Returns SNMP_SUCCESS if enough object values have been provided to set an entire row in the table and the values are appropriate for the target objects. Returns SNMP_FAILURE otherwise.

The agent cache maintains a record v_TableName for each row in a table. In this record, the field val_ObjectName stores the value of the columnar object while sts_ObjectName stores the status of the object. If the value of an object cannot be provided, the status should be assigned SNMP_INVALID. If sts_ObjectName is set to SNMP_INVALID, the corresponding value is ignored.

When a row creation request is received, the values are first loaded into the v_TableName record and then the agent calls the test_TableName_row_create to determine if the values are acceptable for the target objects. If this function returns SNMP_SUCCESS, set_TableName_row_create is called. Both of these functions are passed an index to the newly created row structure in the cache. If test_TableName_row_create returns SNMP_FAILURE, the newly created row structure is removed from the agent cache.

A SET request from a master agent to a subagent may fail to contain values for all of the columnar objects in the row. It is the responsibility of the test_TableName_row_create function to ensure that a sufficient number of columnar object values have been provided to create a row. The validity of the object values is indicated to the set_TableName_create_row function by the sts_ObjectName field corresponding to the object in the v_TableName structure being assigned a value of SNMP_VALID.

set_TableName_row_create

Syntax

int set_TableName_row_create(row_index)
int row_index;

Return Value

This function should always return SNMP_SUCCESS.

The agent cache maintains a record v_TableName for each row in a table. In this record, the field val_ObjectName stores the value of the columnar object while sts_ObjectName stores the status of the object. If the value of an object cannot be provided, the status should be assigned SNMP_INVALID. If sts_ObjectName is set to SNMP_INVALID, the corresponding value is ignored.

When a SET request for a non-existent row is received, the values are first loaded into the v_TableName record and then the agent calls the test_TableName_row_create to determine if the values are acceptable for the target objects. If this function returns SNMP_SUCCESS, set_TableName_row_create is called. Both of these functions are passed an index to the newly created row structure in the cache. If test_TableName_row_create returns SNMP_FAILURE, the newly created row structure is removed from the agent cache.

The validity of the object values is indicated to the set_TableName_create_row function by the sts_ObjectName field corresponding to the object in the v_TableName structure being assigned a value of SNMP_VALID.

Constants and Variables

This section describes user-modifiable constant values and the generated agent cache structure variable, v_MIBRootName.

DELTA_TableName_ENTRIES

This constant is defined in the header file, MIBRootName.h, when the code generator is targeted at a table. Each row in a table is stored in memory in a structure v_TableName, which is of type s_TableName. This type is defined in the MIBRootName.h header file. Objects within a table are stored in this structure. The number of rows in the table are dynamically adjusted. Whenever it is necessary to add more rows, the value of the constant DELTA_TableName_ENTRIES determines the amount of space that is added incrementally to provide space for additional rows.

INIT_TableName_ENTRIES

This constant is defined in the header file, MIBRootName.h, when the code generator is targeted at a table. Each row in a table is stored in memory in a structure v_TableName, which is of type s_TableName. This type is defined in the MIBRootName.h header file. Objects within a table are stored in this structure. The number of rows in the table are dynamically adjusted. However, when the table is created it is initially allocated a number of rows equal to the constant INIT_TableName_ENTRIES.

MAX_StringObjectName

For objects of type string in the MIB group or table, a maximum string length constant is defined in the MIBRootName.h header file. For example:

#define MAX_beaExStrRO 256 /* length of beaExStrRO in bytes */

You can modify the value of this constant as appropriate.

MIBRootName_refresh_rate

This variable specifies in seconds the smallest interval at which the refresh function, refresh_MIBRootName, is called within the agent. The value of this variable is set in the access functions source file, MIBRootName.c.

When a manager sends a GET request to the agent, get_ObjectName functions are called to retrieve the current value from the agent cache. The get_ObjectName functions do not directly access the managed resource. Direct access of the managed resource is the work of the refresh_MIBRootName function, which updates the agent cache. The agent tracks the age of the cache. Before calling the get_ObjectName functions, the agent checks to determine whether the cache is older than the refresh rate. If it is older, the agent calls the function refresh_MIBRootName before calling the get_ObjectName functions for that MIB group.

If the value of MIBRootName_refresh_rate is set to 0, the refresh function is called every time an object is queried by a management station. In that case, the refresh function is not invoked if no requests are received by the agent for these objects.

Example:

int beaExTable_refresh_rate = 10 /* seconds */;

v_MIBRootName

This is a structure of type s_MIBRootName, declared in the MIBRootName.h header file. A structure of this sort is declared for each MIB group or table entry (row).

The agent stores the most recently retrieved values of managed objects in these structures. When a get_ObjectName function retrieves a value in response to a GET request from a management station, it retrieves the value from this agent cache.

Note: Normally you will not need to modify this structure declaration.

The first field in the structure is always:

int index_oid[MAX_INDEX_OID];

and the second field in the structure must always be:

int oid_len;

The structure contains a component:

v_MIBRootName.val_ObjectName

for each object in the target MIB group or table. For each such component there is another component:

v_MIBRootName.sts_ObjectName

that contains the status of the object in the cache. The status may be set to either SNMP_VALID or SNMP_INVALID. If val_ObjectName contains a valid value, the corresponding sts_ObjectName should be set to SNMP_VALID. If the value of ObectName cannot be provided, sts_ObjectName must be set to SNMP_INVALID.

If ObjectName is of type string, there will be a component:

v_MIBRootName.len_ObjectName

which specifies the length of the string in bytes. Each component in a table row structure corresponding to the MIB INDEX clause must have a valid value if table-handing is to work correctly. Because the INDEX specifies an instance of the table entry (row) object, the value of INDEX affects the OID of all the objects in the row.

The following is an example declaration of a structure for a MIB table object, beaExTable:

struct s_beaExTable  {
oid index_oid[MAX_INDEX_OID];/* must be the first element */
int oid_len; /* must be the second element */
int row_status; /* only for internal use */
int val_beaExTblIndex;
int sts_beaExTblIndex;
int val_beaExTblIntRO;
int sts_beaExTblIntRO;
int val_beaExTblIntRW;
int sts_beaExTblIntRW;
char val_beaExTblStrRO[MAX_beaExTblStrRO];
int len_beaExTblSrRO;
int sts_beaExTblStrRO;
int val_beaExTblStrRW[MAX_beaTblStrRW];
int len_beaExTblStrRW;
int sts_beaExTblStrRW;
};
extern struct s_beaExTable *v_beaExTable;

The constants MAX_beaExTblStrRO and MAX_beaExTblStrRW define the length, in bytes, of string objects. These constants are also defined in the header file.

Generated Functions

This section describes generated functions that normally do not need to be modified by the programmer.

get_ObjectName

These functions retrieve the current value of ObjectName from the agent cache. These functions do not directly access the managed resource. Normally it is not necessary to modify these functions.

The syntax of the get functions depends on the type of ObjectName.

Syntax

For columnar objects that are strings or OIDs:

int get_ObjectName(row_index, val_ptr, val_len)
int row_index:
u_char **val_ptr;
int *val_len;

For columnar objects other than strings or OIDs:

int get_ObjectName(row_index,val_ptr)
int row_index;
int *val_ptr;

For scalar objects that are OIDs or strings:

int get_ObjectName(val_ptr,val_len)
u_char **val_ptr;
int *val_len;

For scalar objects other than OIDs or strings:

int get_ObjectName(val_ptr)

Return Value

The function returns SNMP_VALID if the appropriate value of the object is made available in the location pointed to by val_ptr. Otherwise, the function returns SNMP_INVALID and the contents of the location pointed to by val_ptr are undefined.

If ObjectName is a string or IP address, the length of the object in bytes is pointed to by the input argument val_len. If ObjectName is an OID, val_len points to the number of nodes in the absolute OID path from root. If ObjectName is a columnar object, row_index refers to the row number in the table; 0 is the first row.