<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML ><HEAD ><TITLE >C code</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK REL="HOME" TITLE="NetSNMP subagent development manual" HREF="book1.html"><LINK REL="PREVIOUS" TITLE="Header file" HREF="c188.html"><LINK REL="NEXT" TITLE="Creating and Writing (SNMP SET or user) to rows (and columns)" HREF="x406.html"><link rel="stylesheet" href="/openhpi.css" type="text/css"> </head ><BODY CLASS="CHAPTER" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><div id="banner"><div><h1>The OpenHPI Project</h1><small>Open Hardware Platform Interface</small></div></div><table><tr> <!--#include virtual="/sidebar.html" --> <td id="maincolumn"><div class="mainsegment"> <DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >NetSNMP subagent development manual</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="c188.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="x406.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="CHAPTER" ><H1 ><A NAME="AEN299" ></A >C code</H1 ><P > Reading this chapter from top to bottom is the best approach. </P ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="INITIALIZE_TABLE" >Initializiation process of sub-agent</A ></H1 ><P > The sub-agent is initialized by the call</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > 33 /* mib code: nit_netSnmpIETFWGTable from netSnmpIETFWGTable.c */ 34 init_netSnmpIETFWGTable(); </PRE ></TD ></TR ></TABLE ><P > 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 <I CLASS="EMPHASIS" >not</I > add any actual values in the columns. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >/************************************************************ * * 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); } </PRE ></TD ></TR ></TABLE ><P > The init routines performs a couple of basic steps. </P ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN310" >Handler</A ></H2 ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /* 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);</PRE ></TD ></TR ></TABLE ><P >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. </P ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >An OID - Object IDentifier is a an array of <I CLASS="EMPHASIS" >longs</I >. The <I CLASS="EMPHASIS" >netSnmpIETFWGTable_oid</I > is defined as:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > oid netSnmpIETFWGTable_oid[] = { 1,3,6,1,4,1,8072,2,2,1 };</PRE ></TD ></TR ></TABLE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >The <I CLASS="EMPHASIS" >netsnmp_table_array_helper_handler</I > is one of many helper functions available. Consult the manpage - <I CLASS="EMPHASIS" >man netsnmp_table</I > </P ></TD ></TR ></TABLE ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN323" >Notify the helper function</A ></H2 ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /** 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;</PRE ></TD ></TR ></TABLE ><P >Notify the helper how many indexes tuples to have and which type they are. </P ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >The netSnmpIETFWGTable_COL_MIN is defined in the automaticly generated header file. </P ></TD ></TR ></TABLE ></DIV ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >The <I CLASS="EMPHASIS" >ASN_OCTET_STR </I > and other types are defined in the net-snmp/library/asn1.h file</P ></TD ></TR ></TABLE ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="REGISTER_GET_OPERATION" >Register the GET operation</A ></H2 ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /*************************************************** * registering the table with the master agent */ cb.get_value = netSnmpIETFWGTable_get_value;</PRE ></TD ></TR ></TABLE ><P > This registers the GET routine which will be responsible for providing the Net-SNMP library with the correct data for appropiate columnar nodes. Consult <A HREF="x628.html" >the Section called <I >Getting values</I ></A >. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="REGISTER_COMPARE_FUNCTION" >Register compare function</A ></H2 ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >#ifdef netSnmpIETFWGTable_IDX2 netsnmp_container_add_index(cb.container, netsnmp_container_find("netSnmpIETFWGTable_secondary:" "netSnmpIETFWGTable:" "table_container")); cb.container->next->compare = netSnmpIETFWGTable_cmp; #endif</PRE ></TD ></TR ></TABLE ><P >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 <I CLASS="EMPHASIS" >NOT</I > needed if your table rows only have one index value. If you do have more than one index value, and you do <I CLASS="EMPHASIS" >need</I > to sort the rows based on a custom sorting algorithm, then make sure that <I CLASS="EMPHASIS" >netSnmpIETFWGTable_cmp</I > is properly implemented. </P ><P > If you do not need to use a custom sorting algorithm, and have more than one index value comment out <I CLASS="EMPHASIS" >netSnmpIETFWGTable_IDX2</I > and let the NetSNMP library do the sorting. </P ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >The compare routine should <I CLASS="EMPHASIS" >ONLY</I > be used during adding and removal of rows that have more than one index value. For more reasons why, consult this <A HREF="http://www.net-snmp.org/faqs/rstory/array-user.html" TARGET="_top" >array-user Frequently Asked Questions Page</A >. </P ></TD ></TR ></TABLE ></DIV ><P > More on the compare function in <A HREF="c299.html#NETSNMPIETFWGTABLE_IDX2" >the Section called <I >Generic compare function</I ></A >. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="ALTERNATION_AND_CREATION_OF_ROW_SUPPORT" >Alternation and creation of row support</A ></H2 ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >#ifdef netSnmpIETFWGTable_ROW_CREATION cb.create_row = (UserRowMethod*)netSnmpIETFWGTable_create_row; #endif </PRE ></TD ></TR ></TABLE ><P > 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. </P ><P > 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. </P ><P > 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. </P ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >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. </P ></TD ></TR ></TABLE ></DIV ><P > The following picture, from the Net-SNMP Tutorial webpage clearly explains the states. </P ><DIV CLASS="MEDIAOBJECT" ><P ><IMG SRC="images/set-actions.jpg"></P ></DIV ><P >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 <A HREF="x406.html#SNMP_SET_WRITE_TO_A_ROW" >the Section called <I >SNMP SET writing to a row</I ></A >). </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > 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; </PRE ></TD ></TR ></TABLE ><P > Consult <A HREF="x406.html#RESERVE1" >the Section called <I >RESERVE1 function</I ></A >, <A HREF="x406.html#RESERVE2" >the Section called <I >RESERVE2 function</I ></A >, <A HREF="x406.html#ACTION" >the Section called <I >ACTION</I ></A >, <A HREF="x406.html#COMMIT" >the Section called <I >COMMIT function</I ></A > and <A HREF="x406.html#FREE_AND_UNDO" >the Section called <I >FREE and UNDO function</I ></A > for more details on what these functions do. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN373" >Miscellaneous</A ></H2 ><P > 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: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > 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; </PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN377" >Registering handler with container</A ></H2 ><P > 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 <I CLASS="EMPHASIS" >container</I >. </P ><P > 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. </P ><DIV CLASS="NOTE" ><P ></P ><TABLE CLASS="NOTE" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="./stylesheet-images/note.gif" HSPACE="5" ALT="Note"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >Look in <I CLASS="EMPHASIS" >net-snmp/library/container.h</I > for more details. The macro calls which are defined in there (CONTAINER_FIRST, CONTAINER_GET_SUBSET, etc) will be explained in more details in <A HREF="x406.html#DEVELOPER_ROW_CREATION" >the Section called <I >Developer row creation</I ></A >. </P ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="N_TUPLE_INDEXES" >Helper functions to handle n-tuple indexes</A ></H3 ><P > 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. </P ><P > 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 <A HREF="x406.html#COMPARE_FUNCTION_EXPLANATION" >the Section called <I >Sorted</I ></A >. </P ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="NETSNMPIETFWGTABLE_IDX2" >Generic compare function</A ></H2 ><P > The secondary compare function is only used if <I CLASS="EMPHASIS" >netSnmpIETFWGTable_IDX2</I > 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. </P ><P > 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). </P ><P > The implementation of this function is quite straightforward. You use your own method of figuring out which of passed <I CLASS="EMPHASIS" >context</I > structures is less, matches or greater. </P ><P > Consult <A HREF="http://www.net-snmp.org/faqs/rstory/array-user.html" TARGET="_top" > array-user Frequently Asked Questions Page</A > for more advanced information. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN400" >Tree searching function</A ></H2 ><P > The tree searching function purpose is to find a <I CLASS="EMPHASIS" >context</I > 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. </P ><P > It returns a matched <I CLASS="EMPHASIS" >context</I > structure based on the arguments. </P ></DIV ></DIV ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="c188.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="book1.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="x406.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Header file</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" > </TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Creating and Writing (SNMP SET or user) to rows (and columns)</TD ></TR ></TABLE ></DIV ></BODY ></HTML >