# 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," describes the access function skeletons that need to be fleshed out by the programmer to provide access to the managed resource.

- "Constants and Variables," describes the constants and variables that are available for use or modification by the programmer in the access functions file.

- "Generated Functions," describes the generated functions in the access functions file that normally do not need to be modified by the programmer.

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.