Sophie

Sophie

distrib > Fedora > 14 > i386 > media > os > by-pkgid > e04c30fdccbd194f92dd8f835139c4e8 > files > 32

openhpi-subagent-2.3.4-13.fc14.i686.rpm

<!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"
>&#60;&#60;&#60; 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 &#62;&#62;&#62;</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( &#38; 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 = &#38; 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_&#60;name of columnar node&#62;</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_&#60;named values&#62; (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( &#38; var_nsIETFWGName, 0x00, sizeof(var_nsIETFWGName) );
    var_nsIETFWGName.type = ASN_OCTET_STR;
    var_nsIETFWGName.next_variable = &#38; var_nsIETFWGProgress;

    memset( &#38; var_nsIETFWGProgress, 0x00, sizeof(var_nsIETFWGProgress) );
    var_nsIETFWGProgress.type = ASN_INTEGER;
    var_nsIETFWGProgress.next_variable = &#38; var_nsIETFWGIsWorking;

    memset( &#38; 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-&#62;oids, hdr-&#62;len, &#38; var_nsIETFWGName );
    if (err == SNMP_ERR_NOERROR) {
       /*
        * copy components into the context structure
        */
              /** skipping external index nsIETFWGName */
                if(var_nsIETFWGName.val_len &#62; sizeof(ctx-&#62;nsIETFWGName))
                   err = -1;
                else
                    memcpy( ctx-&#62;nsIETFWGName, 
			   var_nsIETFWGName.val.string, 
			   var_nsIETFWGName.val_len );
                ctx-&#62;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( &#38; 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_&#60;name of columnar node&#62;</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-&#62;index.oids);
        free(ctx);
        return NULL;
    }

    /* netsnmp_mutex_init(ctx-&#62;lock);
       netsnmp_mutex_lock(ctx-&#62;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-&#62;nsIETFWGChair1_len = 0;
    ctx-&#62;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-&#62;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&#62;</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)-&#62;find_next(x,NULL)
#define CONTAINER_FIND(x,k)         (x)-&#62;find(x,k)
#define CONTAINER_NEXT(x,k)         (x)-&#62;find_next(x,k)
#define CONTAINER_GET_SUBSET(x,k)   (x)-&#62;get_subset(x,k)
#define CONTAINER_SIZE(x)           (x)-&#62;get_size(x)
#define CONTAINER_ITERATOR(x)       (x)-&#62;get_iterator(x)
#define CONTAINER_COMPARE(x,l,r)    (x)-&#62;compare(l,r)
#define CONTAINER_FOR_EACH(x,f,c)   (x)-&#62;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 &#60;= i-&#62;oids[0]; a++)
	printf("%c", (char) i-&#62;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&#60; 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 &#60; index_oid[0];j++) {
		index_oid[j+1] = *(index_char[i]+j);	

	}
        index.oids = (oid *) &#38; index_oid;
        index.len = index_oid[0]+1;
	ctx = NULL;
	/* Search for it first. */
	ctx = CONTAINER_FIND (cb.container, &#38; index);
	if (!ctx) {
	  /* No dice. We add the new row */
		ctx = netSnmpIETFWGTable_create_row( &#38; index);
		printf("inserting %s\n", ctx-&#62;	 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 *) &#38; index_oid;
   index.len = 4;

   ctx = CONTAINER_FIND(cb.container, &#38; index);
   if (ctx) {
        CONTAINER_REMOVE( cb.container, &#38; 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-&#62;nsIETFWGName);
    while( ctx ) {
        ctx = CONTAINER_NEXT(cb.container,ctx);
        if(ctx)
            printf("Find next = %p %s\n",ctx, ctx-&#62;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-&#62;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 *) &#38; index_oid;
   index.len = 4;

   ctx = CONTAINER_FIND(cb.container, &#38; index);
   if (ctx) {
        CONTAINER_REMOVE( cb.container, &#38; 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 *) &#38; index_oid;
   index.len = 4;
   ctx = CONTAINER_FIND(cb.container, &#38; index);
   if (ctx) {
        /* Modify the context to our content. */
	ctx-&#62;nsIETFWGChair1_len = strlen(chair);
	memcpy(ctx-&#62;nsIETFWGChair1, chair, ctx-&#62;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-&#62;list; current; current = current-&#62;next ) {

        var = current-&#62;ri-&#62;requestvb;
        rc = SNMP_ERR_NOERROR;

        switch(current-&#62;tri-&#62;colnum) {

        case COLUMN_NSIETFWGCHAIR1:
            /** OCTETSTR = ASN_OCTET_STR */
            rc = netsnmp_check_vb_type_and_size(var, ASN_OCTET_STR,
                                    sizeof(row_ctx-&#62;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-&#62;val.integer &#60; 0) ||
               (*var-&#62;val.integer &#62; 4)) &#38;&#38; (*var-&#62;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-&#62;val_len &#62; 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-&#62;row_deleted = 1;</PRE
></TD
></TR
></TABLE
><P
>	For creating:
	</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>	rg-&#62;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"
>&#60;&#60;&#60; 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 &#62;&#62;&#62;</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
>