<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML ><HEAD ><TITLE >Creating and Writing (SNMP SET or user) to rows (and columns)</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="UP" TITLE="C code" HREF="c299.html"><LINK REL="PREVIOUS" TITLE="C code" HREF="c299.html"><LINK REL="NEXT" TITLE="Getting values" HREF="x628.html"><link rel="stylesheet" href="/openhpi.css" type="text/css"> </head ><BODY CLASS="SECT1" 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="c299.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" >C code</TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="x628.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="CREATING_WRITING" >Creating and Writing (SNMP SET or user) to rows (and columns)</A ></H1 ><P > As mentioned in <A HREF="c299.html#ALTERNATION_AND_CREATION_OF_ROW_SUPPORT" >the Section called <I >Alternation and creation of row support</I ></A >, there are three different write-states (RESERVE1, RESERVE2, ACTION) through which a write request to a column, or creation of a row, must pass before it is considered safe to change. If there is any failure during these write-states, it is automaticly free-ed and the request is discarded. If there are no problems, the new request will replace the original (if a column is being modified), or added (if the row did not exist beforehand). </P ><P > There are also helper functions that are required for this to work, such as making a copy of the row (to keep the original in case the write is unsucessful), extracting the index tuple, creating the index tuple, free-ing the row and checking to see if a row can be safely deleted. </P ><P > Most of these functions (and the helper ones) generated by the 'mib2c' tool have all of its basic functionality writen. However, some of them require special attention. </P ><P > The functions are geared towards using SNMP SET and changing the columns of a row. As such their implementation is geared towards such goal, and at first it might be unclear how a sub-agent would create a row by itself. This will be explained in details in <A HREF="x406.html#DEVELOPER_ROW_CREATION" >the Section called <I >Developer row creation</I ></A >. </P ><P > To have a better grasp of how the sub-agent would handle request, it is important to first explain some of the helper functions. </P ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN415" >Row copy</A ></H2 ><P > <I CLASS="EMPHASIS" >netSnmpIETFWGTable_row_copy</I >. As the name implies - this function purpose is to copy rows. The generated implementation takes care of copying all of the <I CLASS="EMPHASIS" >context</I > structure records. Of interest might be the function <I CLASS="EMPHASIS" >snmp_clone_mem</I > which copies the index tuple based on the length of the oid. The reason why a normal memcpy function is not used is due to the neccessity of error checking. If the function cannot determing the correct length of the index tuple (for example the index tuple length might be defined as zero) the copying of the row is stopped. Of course having rows with no index values should never have happened in the first place, but you never known. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="EXTRACT_INDEX" >Extracting index</A ></H2 ><P > The <I CLASS="EMPHASIS" >netSnmpIETFWGTable_extract_index</I > is a very important function and needs tweaking to work. Its purpose is to extract the index tuple in an appropiate format for NetSNMP library to understand. </P ><P > The function purpose is to extract from a linked list of indexes (passed in as netsnmp_index * hdr) its values and store them in the corresponding <I CLASS="EMPHASIS" >context</I > structure. </P ><P > The snippets of code generated by the 'mib2c' tool do most of the work. However the linking of the linked list entries must be done by the developer. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > netsnmp_variable_list var_nsIETFWGName; ... /** * Create variable to hold each component of the index */ memset( & var_nsIETFWGName, 0x00, sizeof(var_nsIETFWGName) ); var_nsIETFWGName.type = ASN_OCTET_STR; /** TODO: link this index to the next, or NULL for the last one */ #ifdef TABLE_CONTAINER_TODO snmp_log(LOG_ERR, "netSnmpIETFWGTable_extract_index index list not implemented !\n" ); return 0; #else var_nsIETFWGName.next_variable = & var_XX; #endif </PRE ></TD ></TR ></TABLE ><P > The <I CLASS="EMPHASIS" >var_nsIETFWGName</I > is the first index of the row. If there were more index values they would be defined as <I CLASS="EMPHASIS" >var_<name of columnar node></I >. </P ><P >In this snippet of code the linked list of the index values is built. This linked list will be used by <I CLASS="EMPHASIS" >parse_oid_indexes</I > to figure out where each index value is suppose to go. </P ><P > Each of the var_<named values> (there would be more than just one in the example if the index tuple had more than one object defined) type is set to its type (ASN_OCTET_STR, ASN_INTEGER, etc - look in the net-snmp/library/asn1.h) and linked to the next index value. The last index value is set to NULL. </P ><P > For example, if this table had two extra index values: an enumerated integer value (nsIETFWGProgress) and TruthValue (nsIETFWGIsWorking) with the following ASN.1 definition: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >nsIETFWGProgress OBJECT-TYPE SYNTAX INTEGER { undefined(0), proposed(1), debated(2), rewritting(3), draft(4), standard(50) } MAX-ACCESS read-only STATUS current DESCRIPTION "Progress of a work-group" ::= { netSnmpIETFWGEntry 4 } nsIETFWGIsWorking OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current DESCRIPTION "Is the work group still working?" ::= { netSnmpIETFWGEntry 5 }</PRE ></TD ></TR ></TABLE ><P > netSnmpIETFWGEntry would have these two objects as extra index nodes: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > INDEX { nsIETFWGName, nsIETFWGProgress, nsIETFWGIsWorking }</PRE ></TD ></TR ></TABLE ><P > These two extra ASN.1 entries would add two extra variables in the <I CLASS="EMPHASIS" > context</I > structure: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /** INTEGER = ASN_INTEGER */ long nsIETFWGProgress; /** TruthValue = ASN_INTEGER */ long nsIETFWGIsWorking;</PRE ></TD ></TR ></TABLE ><P > As such, the <I CLASS="EMPHASIS" >_extract_index</I > code snippet would look like: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > netsnmp_variable_list var_nsIETFWGName; netsnmp_variable_list var_nsIETFWGProgress; netsnmp_variable_list var_nsIETFWGIsWorking; memset( & var_nsIETFWGName, 0x00, sizeof(var_nsIETFWGName) ); var_nsIETFWGName.type = ASN_OCTET_STR; var_nsIETFWGName.next_variable = & var_nsIETFWGProgress; memset( & var_nsIETFWGProgress, 0x00, sizeof(var_nsIETFWGProgress) ); var_nsIETFWGProgress.type = ASN_INTEGER; var_nsIETFWGProgress.next_variable = & var_nsIETFWGIsWorking; memset( & var_nsIETFWGIsWorking, 0x00, sizeof(var_nsIETFWGIsWorking)); var_nsIETFWGIsWorking.type = ASN_INTEGER; var_nsIETFWGIsWorking.next_variable = NULL;</PRE ></TD ></TR ></TABLE ><P > The <I CLASS="EMPHASIS" >parse_oid_indexes</I > parses the linked list of index value against the <I CLASS="EMPHASIS" >hdr</I >, checks to make sure it is right type, and correct length. If everything is correct, each item of the linked list is populated with the index value taken from the <I CLASS="EMPHASIS" >hdr</I >. </P ><P >However, that does not fill the values of <I CLASS="EMPHASIS" >context</I > structure. That is the job of the next part of the code segment in which the return code of <I CLASS="EMPHASIS" >parse_oid_indexes</I > is checked, and if found to be OK, the values from linked list are copied to the corresponding <I CLASS="EMPHASIS" >context</I > structure entries. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > err = parse_oid_indexes( hdr->oids, hdr->len, & var_nsIETFWGName ); if (err == SNMP_ERR_NOERROR) { /* * copy components into the context structure */ /** skipping external index nsIETFWGName */ if(var_nsIETFWGName.val_len > sizeof(ctx->nsIETFWGName)) err = -1; else memcpy( ctx->nsIETFWGName, var_nsIETFWGName.val.string, var_nsIETFWGName.val_len ); ctx->nsIETFWGName_len = var_nsIETFWGName.val_len; } </PRE ></TD ></TR ></TABLE ><P > Lastly the linked list is cleaned up (during the parsing it might have allocated memory) using: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > snmp_reset_var_buffers( & var_nsIETFWGName );</PRE ></TD ></TR ></TABLE ><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 >If you add more index values and change the order in which they appear, make sure that it is always the first index tuple, defined as one of <I CLASS="EMPHASIS" >var_<name of columnar node></I > is being free-ed using <I CLASS="EMPHASIS" >snmp_reset_var_buffers</I >. </P ></TD ></TR ></TABLE ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN460" >Row deletion checking</A ></H2 ><P > <I CLASS="EMPHASIS" >netSnmpIETFWGTable_can_delete</I > does a small check on the passed <I CLASS="EMPHASIS" >context</I > structure to see if it can be deleted. </P ><P > If there are any specific conditions under which a row can not be deleted these should be implemented here. </P ><P > By default the function returns a positive number, which implies that the row can be safely deleted. </P ><P > Returning zero means that the row cannot be deleted. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN468" >Duplicating rows</A ></H2 ><P > <I CLASS="EMPHASIS" >netSnmpIETFWGTable_duplicate_row</I > is pretty self-explanatory. Duplicate a row. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="DELETE_ROWS" >Deleting rows</A ></H2 ><P > <I CLASS="EMPHASIS" >netSnmpIETFWGTable_delete_row</I > purpose is to delete a row. This is function that frees the index tuple, and any other memory that the user might have allocated using </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > void * data;</PRE ></TD ></TR ></TABLE ><P > defined in the <I CLASS="EMPHASIS" >context</I > structure in the header file. </P ><P > This function is only used when the NetSNMP library is trying to write to a row and finds out that something is wrong. Then it deletes the temporary row (a copy of the original). </P ><P > To delete a row from the sub-agent, you need to use the CONTAINER_REMOVE macro. Look in <A HREF="x406.html#DEVELOPER_ROW_DELETE" >the Section called <I >Developer row deletion.</I ></A > for more information. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="CREATE_ROWS" >Creating rows</A ></H2 ><P > The <I CLASS="EMPHASIS" >netSnmpIETFWGTable_create_row</I > purpose is to create a newly allocated <I CLASS="EMPHASIS" >context</I > structure. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >netSnmpIETFWGTable_context * netSnmpIETFWGTable_create_row( netsnmp_index* hdr) { netSnmpIETFWGTable_context * ctx = SNMP_MALLOC_TYPEDEF(netSnmpIETFWGTable_context); if(!ctx) return NULL; /* * TODO: check indexes, if necessary. */ if(netSnmpIETFWGTable_extract_index( ctx, hdr )) { free(ctx->index.oids); free(ctx); return NULL; } /* netsnmp_mutex_init(ctx->lock); netsnmp_mutex_lock(ctx->lock); */ /* * TODO: initialize any default values here. This is also * first place you really should allocate any memory for * yourself to use. If you allocated memory earlier, * make sure you free it for earlier error cases! */ ctx->nsIETFWGChair1_len = 0; ctx->nsIETFWGChair2_len = 0; return ctx; }</PRE ></TD ></TR ></TABLE ><P > The passed in argument <I CLASS="EMPHASIS" >netsnmp_index* hdr</I > defines the index of the row. This <I CLASS="EMPHASIS" >hdr</I > value is checked using <I CLASS="EMPHASIS" >netSnmpIETFWGTable_extract_index</I > routine which extracts the index values from <I CLASS="EMPHASIS" >hdr</I > and populates the correct entries in the <I CLASS="EMPHASIS" >context</I > structure. If this call fails, the newly allocated <I CLASS="EMPHASIS" >context</I > structure is free-ed and the function returns a NULL. </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 > In case you do not know how <I CLASS="EMPHASIS" >netSnmpIETFWGTable_extract_index</I > knows which entries to populate, refer to <A HREF="x406.html#EXTRACT_INDEX" >the Section called <I >Extracting index</I ></A >, for details. </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" >netsnmp_mutex_init(ctx->lock);</I > purpose is to create a locking mutex mechanism in case your application is multi-threaded. </P ></TD ></TR ></TABLE ></DIV ><P >Otherwise the <I CLASS="EMPHASIS" >context</I > is filled with default values - specified by the developer in the last lines of this routine. </P ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="AEN504" >What calls netSnmpIETFWGTable_create_row></A ></H3 ><P > Looking back at <A HREF="c299.html#ALTERNATION_AND_CREATION_OF_ROW_SUPPORT" >the Section called <I >Alternation and creation of row support</I ></A >, which described part of the initialization process for a table, the <I CLASS="EMPHASIS" >netSnmpIETFWGTable_create_row</I > is initialized in call-back mechansim: </P ><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 > The NetSNMP library uses this information to call the create_row function whenever a SET request is issued against a non-existing row. The NetSNMP library first searches through the rows - if it cannot find a row with the matching index value, it calls <I CLASS="EMPHASIS" >netSnmpIETFWGTable_create_row</I >. If this call returns a NULL value the SET request is dropped. Otherwise the next call the NetSNMP library makes is the <I CLASS="EMPHASIS" >netSnmpIETFWGTable_set_reserve1</I > routine, explained later in this chapter. </P ><P > The create_row function is also the vehicle by which the sub-agent internally can create fully populated rows. </P ></DIV ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="DEVELOPER_ROW_CREATION" >Developer row creation</A ></H3 ><P > The source code has a perfect spot for user creation - it is right after the call to <I CLASS="EMPHASIS" >initialize_table_netSnmpIETFWGTable</I >. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > void init_netSnmpIETFWGTable(void) { initialize_table_netSnmpIETFWGTable(); /* * TODO: perform any startup stuff here */ }</PRE ></TD ></TR ></TABLE ><P > After the initialize call, the table is ready to populated with rows. The rows are the <I CLASS="EMPHASIS" >context</I > structures, explained in the previous sections. </P ><P > Consult <A HREF="c188.html#CONTEXT_STRUCTURE" >the Section called <I >Context structure</I > in the Chapter called <I >Header file</I ></A > for more in depth explanation of the <I CLASS="EMPHASIS" >context</I > structure. </P ><P > The big question is what magic will make NetSNMP library aware of the <I CLASS="EMPHASIS" >container</I > structures? By the usage of <I CLASS="EMPHASIS" >CONTAINER</I > macros. Look in net-snmp/library/container.h. In it, there are a couple of macro calls: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /* * useful macros */ #define CONTAINER_FIRST(x) (x)->find_next(x,NULL) #define CONTAINER_FIND(x,k) (x)->find(x,k) #define CONTAINER_NEXT(x,k) (x)->find_next(x,k) #define CONTAINER_GET_SUBSET(x,k) (x)->get_subset(x,k) #define CONTAINER_SIZE(x) (x)->get_size(x) #define CONTAINER_ITERATOR(x) (x)->get_iterator(x) #define CONTAINER_COMPARE(x,l,r) (x)->compare(l,r) #define CONTAINER_FOR_EACH(x,f,c) (x)->for_each(x,f,c)</PRE ></TD ></TR ></TABLE ><P > These macros provide a <I CLASS="EMPHASIS" >wrapper</I > around the row-data (<I CLASS="EMPHASIS" >context</I > structure) and make its manipulation possible. </P ><DIV CLASS="SECT4" ><H4 CLASS="SECT4" ><A NAME="EXAMPLE_CONTAINER" >Example of CONTAINER usage</A ></H4 ><P > A good comprehensive example of how to use CONTAINER_ macros is available in NetSNMP source tarball - in the snmplib/test_binary_array.c , or the following example in this tutorial (these lines of code were inserted after initialize_table_netSnmpIETFWGTable(). </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 >It is assumed that create_row function is fully implemented for this example to work properly. </P ></TD ></TR ></TABLE ></DIV ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static void print_string(netsnmp_index *i, void *v) { int a; printf("item %p = [",i); for (a = 1; a <= i->oids[0]; a++) printf("%c", (char) i->oids[a]); printf("]\n"); } /************************************************************ * Initializes the netSnmpIETFWGTable module */ void init_netSnmpIETFWGTable(void) { netsnmp_index index; oid index_oid[MAX_OID_LEN]; char *index_char[] = {"hickory","joe","hickory", "bob","new orleans","help"}; int i,j; netSnmpIETFWGTable_context *ctx; initialize_table_netSnmpIETFWGTable(); for (i = 0; i< 6; i++) { /* First value of an index that is ASN_OCTET_STR is the length of the string. */ index_oid[0] = strlen(index_char[i]); /* The rest is the data copied. */ for (j = 0; j < index_oid[0];j++) { index_oid[j+1] = *(index_char[i]+j); } index.oids = (oid *) & index_oid; index.len = index_oid[0]+1; ctx = NULL; /* Search for it first. */ ctx = CONTAINER_FIND (cb.container, & index); if (!ctx) { /* No dice. We add the new row */ ctx = netSnmpIETFWGTable_create_row( & index); printf("inserting %s\n", ctx-> nsIETFWGName); CONTAINER_INSERT (cb.container, ctx); } } /* Since we are done adding the rows, let us display them for the fun. The easy way: */ CONTAINER_FOR_EACH(cb.container, print_string, NULL); /* We do not like 'joe', so we remove him. */ index_oid[0] = 3; index_oid[1] = 'j'; index_oid[2] = 'o'; index_oid[3] = 'e'; index.oids = (oid *) & index_oid; index.len = 4; ctx = CONTAINER_FIND(cb.container, & index); if (ctx) { CONTAINER_REMOVE( cb.container, & index); netSnmpIETFWGTable_delete_row ( ctx ); printf("Removed joe\n"); } /* Print the hard way: */ ctx = CONTAINER_FIRST(cb.container); printf("Find first = %p %s\n",ctx, ctx->nsIETFWGName); while( ctx ) { ctx = CONTAINER_NEXT(cb.container,ctx); if(ctx) printf("Find next = %p %s\n",ctx, ctx->nsIETFWGName); else printf("Find next = %p\n",ctx); } }</PRE ></TD ></TR ></TABLE ><P > The output of this daemon should look like this: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >inserting hickory inserting joe inserting bob inserting new orleans inserting help item 0x4036d008 = [bob] item 0x4033c008 = [joe] item 0x4030b008 = [hickory] item 0x4039e008 = [new orleans] item 0x403cf008 = [help] Removed joe Find first = 0x4036d008 bob Find next = 0x403cf008 help Find next = 0x4030b008 hickory Find next = 0x4039e008 new orleans Find next = (nil)</PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT4" ><H4 CLASS="SECT4" ><A NAME="COMPARE_FUNCTION_EXPLANATION" >Sorted</A ></H4 ><P > You might wonder why the rows are alphabethicly sorted? The reason is that the NetSNMP library uses its own sorting method when adding/deleting rows. It is set by default to be the function <I CLASS="EMPHASIS" >netsnmp_compare_netsnmp_index</I > which has the exact same function prototype as the <I CLASS="EMPHASIS" >static int netSnmpIETFWGTable_cmp( const void *lhs, const void *rhs );</I >. If you would like to use your own sorting method, add in initialize_table_netSnmpIETFWGTable function the following line: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > cb.container->compare = netSnmpIETFWGTable_cmp;</PRE ></TD ></TR ></TABLE ><P > And make sure that your compare function works properly. Consult <A HREF="c299.html#NETSNMPIETFWGTABLE_IDX2" >the Section called <I >Generic compare function</I ></A > and <A HREF="c299.html#REGISTER_COMPARE_FUNCTION" >the Section called <I >Register compare function</I ></A > for more details. </P ></DIV ></DIV ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="DEVELOPER_ROW_DELETE" >Developer row deletion.</A ></H3 ><P > Removing rows requires four steps. You have know which row you want (by the index value), find it, remove from the container, and then finally free it. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /* We do not like 'joe', so we remove him. */ index_oid[0] = 3; index_oid[1] = 'j'; index_oid[2] = 'o'; index_oid[3] = 'e'; index.oids = (oid *) & index_oid; index.len = 4; ctx = CONTAINER_FIND(cb.container, & index); if (ctx) { CONTAINER_REMOVE( cb.container, & index); netSnmpIETFWGTable_delete_row ( ctx ); printf("Removed joe\n"); }</PRE ></TD ></TR ></TABLE ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="WRITE_ROW" >Writing to a row</A ></H2 ><P > There are two ways to write to a row. From the perspective of a SNMP SET command and sub-agent (developer). </P ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="DEVELOPER_WRITING" >Developer writing to a row</A ></H3 ><P > The process of writing to a row from a sub-agent perspective (developer) is simplistic. It requires the task of retrieving the row and modifying it. No need to re-insert it using CONTAINER_INSERT, since it is already in there. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > char chair* = "John Block"; /* * Modify 'bob' */ index_oid[0] = 3; index_oid[1] = 'b'; index_oid[2] = 'o'; index_oid[3] = 'b'; index.oids = (oid *) & index_oid; index.len = 4; ctx = CONTAINER_FIND(cb.container, & index); if (ctx) { /* Modify the context to our content. */ ctx->nsIETFWGChair1_len = strlen(chair); memcpy(ctx->nsIETFWGChair1, chair, ctx->nsIETFWGChair1_len); }</PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="SNMP_SET_WRITE_TO_A_ROW" >SNMP SET writing to a row</A ></H3 ><P > This process is more complex due to the neccessity of checking that the SNMP SET request is the correct type, length, and other checks that the developer might deem neccesary. </P ><P > The process by which the NetSNMP library uses to decide if the data is OK is by a state machine. If the SET request passes succesfully through the RESERVE1, RESERVE2, and ACTION phase it is committed to memory. </P ><P > The following picture, borrowed from NetSNMP webpage, demonstrates these steps. A more detailed explanation of what happens during these steps is explained in this <A HREF="http://www.net-snmp.org/faqs/rstory/#steps" TARGET="_top" >rstory's NET-SNMP Developers Frequently Asked Questions Page: Baby Steps Flow</A >. </P ><DIV CLASS="MEDIAOBJECT" ><P ><IMG SRC="images/set-actions.jpg"></P ></DIV ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="RESERVE1" >RESERVE1 function</A ></H2 ><P > The <I CLASS="EMPHASIS" >netSnmpIETFWGTable_set_reserve1</I > job is to make sure that the SET request is of right type. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > for( current = rg->list; current; current = current->next ) { var = current->ri->requestvb; rc = SNMP_ERR_NOERROR; switch(current->tri->colnum) { case COLUMN_NSIETFWGCHAIR1: /** OCTETSTR = ASN_OCTET_STR */ rc = netsnmp_check_vb_type_and_size(var, ASN_OCTET_STR, sizeof(row_ctx->nsIETFWGChair1)); break;</PRE ></TD ></TR ></TABLE ><P > The <I CLASS="EMPHASIS" >for</I > loop goes through all of the SNMP SET requests. The <I CLASS="EMPHASIS" >netsnmp_request_group *rg</I > keeps a list of aggregated SNMP SET request for this particular table. </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 can mean that this function is called with more than one SNMP SET request for different columns. </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 > This list of SNMP SET request can also be for non-existent rows, because the index values do not match what the NetSNMP library has in memory. For rows that do not exist in the container (as in, they have not been inserted using CONTAINER_INSERT), the NetSNMP creates a <I CLASS="EMPHASIS" >context</I > structure using the <I CLASS="EMPHASIS" >netSnmpIETFWGTable_create_row</I >. For those that do exist, it grabs them from the container. </P ></TD ></TR ></TABLE ></DIV ><P > The <I CLASS="EMPHASIS" >netsnmp_check_vb_type_and_size</I > checks the type of the SNMP SET request and also the size of the payload. </P ><P > If checking process fails, the <I CLASS="EMPHASIS" >rc</I > is set to an appropiate error code (consult net-snmp/library/snmp.h for the list) and <I CLASS="EMPHASIS" >netsnmp_set_mode_request_error</I > is notified. This will result in removal of this SNMP SET request and the end-user will be notified of the appropiate error code. </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 >If the end-user is using SNMP v1, only a selective set of error codes is available. This might give the user a different error code than what the developer had set. </P ></TD ></TR ></TABLE ></DIV ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="RESERVE2" >RESERVE2 function</A ></H2 ><P > The second stage is checking for appropiate values of the SNMP SET request. This is where the developer checks for the correct length and values of the columnar nodes. </P ><P > This check is necessary for enumerated integers - it is important to check for the right enumeration values. If you refer back to <A HREF="x406.html#EXTRACT_INDEX" >the Section called <I >Extracting index</I ></A >, you will notice the extra defined ASN.1 columnar node called <I CLASS="EMPHASIS" >nsIETFWGProgress</I >. This object defines six enumerations: undefined(0), proposed(1), debated(2), rewritting(3), draft(4) and standard(50). The check for the proper enumerations can be carried out in this function, such as: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > case COLUMN_NSIETFWGPROGRESS: if (((*var->val.integer < 0) || (*var->val.integer > 4)) && (*var->val.integer != 50)) { rc = SNMP_ERR_BADVALUE; } break;</PRE ></TD ></TR ></TABLE ><P > For string variables, it important to check the length of the string - to make sure it is not more (or less) to what is defined in the MIB. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >nsIETFWGChair1 OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-create STATUS current DESCRIPTION "One of the names of the chairs for the IETF working group." ::= { netSnmpIETFWGEntry 2 }</PRE ></TD ></TR ></TABLE ><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 > And our check for strings of length more than 255. In truth, this check might be inappropriate because an OCTET STRING can have a length of 65536. However most SNMP implementations cannot carry such large payloads. </P ></TD ></TR ></TABLE ></DIV ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > case COLUMN_NSIETFWGCHAIR2: /** OCTETSTR = ASN_OCTET_STR */ if (var->val_len > 255) rc = SNMP_ERR_WRONGLENGTH; break;</PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="ACTION" >ACTION</A ></H2 ><P > The <I CLASS="EMPHASIS" >netSnmpIETFWGTable_set_action</I > is the function where the new value is writen in the appropiate <I CLASS="EMPHASIS" >context</I > structure and where the developer would perform the write to his/her datastore. </P ><P >The changes are being writen to row_ctx. A copy of the original row is in undo_ctx. If this function calls <I CLASS="EMPHASIS" >netsnmp_set_mode_request_error</I > the newly modified row is discarded and the user is notified of the error state. </P ><P > It is in this function that the developer can decide if the row has to be removed or created. For deleting, the variable <I CLASS="EMPHASIS" >row_deleted</I > in the <I CLASS="EMPHASIS" >netsnmp_request_group</I > has to be set: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > rg->row_deleted = 1;</PRE ></TD ></TR ></TABLE ><P > For creating: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > rg->row_created = 1;</PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="COMMIT" >COMMIT function</A ></H2 ><P > This routine is used to commit the changes to the row. The intent of the ACTION/COMMIT division is that all of the fallible code should be done in the ACTION phase, so that it can be backed out if necessary. </P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="FREE_AND_UNDO" >FREE and UNDO function</A ></H2 ><P > This explanation is taken from the generated C code. </P ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="AEN620" >FREE function</A ></H3 ><P > If either of the RESERVE calls fail, the write routines are called again with the FREE action, to release any resources that have been allocated. The agent will then return a failure response to the requesting application.</P ><P > AFTER calling this routine, the agent will delete undo_info.</P ></DIV ><DIV CLASS="SECT3" ><H3 CLASS="SECT3" ><A NAME="AEN624" >UNDO function</A ></H3 ><P > If the ACTION phase does fail (for example due to an apparently valid, but unacceptable value, or an unforeseen problem), then the list of write routines are called again, with the UNDO action. This requires the routine to reset the value that was changed to its previous value (assuming it was actually changed), and then to release any resources that had been allocated. As with the FREE phase, the agent will then return an indication of the error to the requesting application. </P ><P > BEFORE calling this routine, the agent will update the container (remove any newly inserted row, re-insert any removed row). </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="c299.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="x628.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >C code</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="c299.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Getting values</TD ></TR ></TABLE ></DIV ></BODY ></HTML >