Chapter 5. C code
Reading this chapter from top to bottom is the best approach.
5.1. Initializiation process of sub-agent
The sub-agent is initialized by the call
33 /* mib code: nit_netSnmpIETFWGTable from netSnmpIETFWGTable.c */ 34 init_netSnmpIETFWGTable();
from the sub-agent daemon code. This call in the generated code calls the function which sets up the skeleton of the table. It does not add any actual values in the columns.
/************************************************************ * * Initialize the netSnmpIETFWGTable table by defining its contents and how it's structured */ void initialize_table_netSnmpIETFWGTable(void) { netsnmp_table_registration_info *table_info; if(my_handler) { snmp_log(LOG_ERR, "initialize_table_netSnmpIETFWGTable_handler called again\n"); return; } memset(& cb, 0x00, sizeof(cb)); /** create the table structure itself */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); /* if your table is read only, it's easiest to change the HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */ my_handler = netsnmp_create_handler_registration( "netSnmpIETFWGTable", netsnmp_table_array_helper_handler, netSnmpIETFWGTable_oid, netSnmpIETFWGTable_oid_len, HANDLER_CAN_RWRITE); if (!my_handler || !table_info) { snmp_log(LOG_ERR, "malloc failed in " "initialize_table_netSnmpIETFWGTable_handler\n"); return; /** mallocs failed */ } /*************************************************** * Setting up the table's definition */ /* * TODO: add any external indexes here. */ /* * internal indexes */ /** index: nsIETFWGName */ netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR); table_info->min_column = netSnmpIETFWGTable_COL_MIN; table_info->max_column = netSnmpIETFWGTable_COL_MAX; /*************************************************** * registering the table with the master agent */ cb.get_value = netSnmpIETFWGTable_get_value; cb.container = netsnmp_container_find("netSnmpIETFWGTable_primary:" "netSnmpIETFWGTable:" "table_container"); #ifdef netSnmpIETFWGTable_IDX2 netsnmp_container_add_index(cb.container, netsnmp_container_find("netSnmpIETFWGTable_secondary:" "netSnmpIETFWGTable:" "table_container")); cb.container->next->compare = netSnmpIETFWGTable_cmp; #endif #ifdef netSnmpIETFWGTable_SET_HANDLING cb.can_set = 1; #ifdef netSnmpIETFWGTable_ROW_CREATION cb.create_row = (UserRowMethod*)netSnmpIETFWGTable_create_row; #endif cb.duplicate_row = (UserRowMethod*)netSnmpIETFWGTable_duplicate_row; cb.delete_row = (UserRowMethod*)netSnmpIETFWGTable_delete_row; cb.row_copy = (Netsnmp_User_Row_Operation *) netSnmpIETFWGTable_row_copy; /* cb.can_activate = (Netsnmp_User_Row_Action *) netSnmpIETFWGTable_can_activate; cb.can_deactivate = (Netsnmp_User_Row_Action *) netSnmpIETFWGTable_can_deactivate; */ cb.can_delete = (Netsnmp_User_Row_Action *) netSnmpIETFWGTable_can_delete; cb.set_reserve1 = netSnmpIETFWGTable_set_reserve1; cb.set_reserve2 = netSnmpIETFWGTable_set_reserve2; cb.set_action = netSnmpIETFWGTable_set_action; cb.set_commit = netSnmpIETFWGTable_set_commit; cb.set_free = netSnmpIETFWGTable_set_free; cb.set_undo = netSnmpIETFWGTable_set_undo; #endif DEBUGMSGTL(("initialize_table_netSnmpIETFWGTable", "Registering table netSnmpIETFWGTable " "as a table array\n")); netsnmp_table_container_register(my_handler, table_info, & cb, cb.container, 1); }
The init routines performs a couple of basic steps.
5.1.1. Handler
/* if your table is read only, it's easiest to change the HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */ my_handler = netsnmp_create_handler_registration("netSnmpIETFWGTable", netsnmp_table_array_helper_handler, netSnmpIETFWGTable_oid, netSnmpIETFWGTable_oid_len, HANDLER_CAN_RWRITE);
Create a handler which contains the OID of the table, make it writeable, and which (if any) if the NET-SNMP library helper functions to use.
An OID - Object IDentifier is a an array of longs. The netSnmpIETFWGTable_oid is defined as: oid netSnmpIETFWGTable_oid[] = { 1,3,6,1,4,1,8072,2,2,1 }; |
The netsnmp_table_array_helper_handler is one of many helper functions available. Consult the manpage - man netsnmp_table |
5.1.2. Notify the helper function
/** index: nsIETFWGName */ netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR); table_info->min_column = netSnmpIETFWGTable_COL_MIN; table_info->max_column = netSnmpIETFWGTable_COL_MAX;
Notify the helper how many indexes tuples to have and which type they are.
The netSnmpIETFWGTable_COL_MIN is defined in the automaticly generated header file. |
The ASN_OCTET_STR and other types are defined in the net-snmp/library/asn1.h file |
5.1.3. Register the GET operation
/*************************************************** * registering the table with the master agent */ cb.get_value = netSnmpIETFWGTable_get_value;
This registers the GET routine which will be responsible for providing the Net-SNMP library with the correct data for appropiate columnar nodes. Consult Section 5.3.
5.1.4. Register compare function
#ifdef netSnmpIETFWGTable_IDX2 netsnmp_container_add_index(cb.container, netsnmp_container_find("netSnmpIETFWGTable_secondary:" "netSnmpIETFWGTable:" "table_container")); cb.container->next->compare = netSnmpIETFWGTable_cmp; #endif
Register with the main NetSNMP code (by injecting the address of a user-written routine in the callback mechanism) the OID compare routine. This routine is NOT needed if your table rows only have one index value. If you do have more than one index value, and you do need to sort the rows based on a custom sorting algorithm, then make sure that netSnmpIETFWGTable_cmp is properly implemented.
If you do not need to use a custom sorting algorithm, and have more than one index value comment out netSnmpIETFWGTable_IDX2 and let the NetSNMP library do the sorting.
The compare routine should ONLY be used during adding and removal of rows that have more than one index value. For more reasons why, consult this array-user Frequently Asked Questions Page. |
More on the compare function in Section 5.1.8.
5.1.5. Alternation and creation of row support
#ifdef netSnmpIETFWGTable_ROW_CREATION cb.create_row = (UserRowMethod*)netSnmpIETFWGTable_create_row; #endif
Without this routine being injected in the call-back mechanism, no row creation is possible. The row creation process is user-agnostic - the sub-agent code using internal routines or the SNMP-user using SET operation can create new rows.
However, the SNMP SET operation has to go throughout a set of routines to determine if it has the valid syntax, the right type and length, and other user-defined tests. Those tests are not exercised when the sub-agent code uses the internal routines.
The process by which the NetSNMP library uses to decide if the data is OK is a four state based machine. The data is writen to a new row or an existing one, if it has passed the RESERVE1, RESERVE2, and ACTION phase. The ACTION phase does the modification and if anything goes wrong the process moves to UNDO phase - which restores the original data. Otherwise a COMMIT is perfomed - which modifies the data.
This two-phase commit system is unique. Many other SNMP sub-agent librarys do not implement such technique and have only one SET operation which has to take care of checking the type, size, limitation, perform the write, and undo if needed. |
The following picture, from the Net-SNMP Tutorial webpage clearly explains the states.
They are also part of the call-back mechanism. It is the developer responsibility to make sure that those functions are properly implemented (more on this in Section 5.2.7.2).
cb.set_reserve1 = netSnmpIETFWGTable_set_reserve1; cb.set_reserve2 = netSnmpIETFWGTable_set_reserve2; cb.set_action = netSnmpIETFWGTable_set_action; cb.set_commit = netSnmpIETFWGTable_set_commit; cb.set_free = netSnmpIETFWGTable_set_free; cb.set_undo = netSnmpIETFWGTable_set_undo;
Consult Section 5.2.8, Section 5.2.9, Section 5.2.10, Section 5.2.11 and Section 5.2.12 for more details on what these functions do.
5.1.6. Miscellaneous
The above mentioned functions also require a couple of helper functions. Usually you don't need to modify them as the mib2c tool does a great job of implementing them. The routines in question are:
cb.duplicate_row = (UserRowMethod*)netSnmpIETFWGTable_duplicate_row; cb.delete_row = (UserRowMethod*)netSnmpIETFWGTable_delete_row; cb.row_copy = (Netsnmp_User_Row_Operation *) netSnmpIETFWGTable_row_copy; cb.can_activate = (Netsnmp_User_Row_Action *) netSnmpIETFWGTable_can_activate; cb.can_deactivate = (Netsnmp_User_Row_Action *) netSnmpIETFWGTable_can_deactivate; cb.can_delete = (Netsnmp_User_Row_Action *)netSnmpIETFWGTable_can_delete;
5.1.7. Registering handler with container
The last thing that must be done is to register the handler, table information (how many rows, columns, etc), and the call-back mechansim with the container.
The container is the Net-SNMP library part of code that will keep our rows in memory. It will take care of sorting it (when rows are added or removed), providing a specific row for a GET/SET request (so you don't have to extract from the OID the index/column values and find the row by yourself), and more. This container mechanism provides a seperation of the sub-agent developer to have to deal with SNMP GET/SET details and instead concentrate on operating the data.
Look in net-snmp/library/container.h for more details. The macro calls which are defined in there (CONTAINER_FIRST, CONTAINER_GET_SUBSET, etc) will be explained in more details in Section 5.2.6.2. |
5.1.7.1. Helper functions to handle n-tuple indexes
Usually tables only require one index value (for example the enumeration of network driver). But in some cases there is a need for a second index, or third, or an index value coded as a string.
The index values are used in determining the order of rows. If the index value is a string in UNICODE of a foreign language - the ascending order the user expects might be completly different from ASCII sort order. Therefore the NetSNMP library provides a mechanism to register the developer's own sorting routine. More on this topic in the section below and in Section 5.2.6.2.2.
5.1.8. Generic compare function
The secondary compare function is only used if netSnmpIETFWGTable_IDX2 is defined. This compare function is used by NetSNMP library when rows are being added or deleted that have more than one index value. If this function is not enabled, the NetSNMP library will use its own generic compare function - which compares index tuples and returns an response based on ASCII ascending order.
The goal of this compare function is to return an integer less than, equal to, or greater than zero if lhs (first argument) is found, respectively, to be less than, to match, or be greater than rhs (second argument).
The implementation of this function is quite straightforward. You use your own method of figuring out which of passed context structures is less, matches or greater.
Consult array-user Frequently Asked Questions Page for more advanced information.
5.1.9. Tree searching function
The tree searching function purpose is to find a context structure based on the custom arguments (defined by developer). This function is not used by NetSNMP library - it is a helper function for the developer if needed.
It returns a matched context structure based on the arguments.