summaryrefslogtreecommitdiff
path: root/local/mib2c.array-user.conf
diff options
context:
space:
mode:
Diffstat (limited to 'local/mib2c.array-user.conf')
-rw-r--r--local/mib2c.array-user.conf1305
1 files changed, 1305 insertions, 0 deletions
diff --git a/local/mib2c.array-user.conf b/local/mib2c.array-user.conf
new file mode 100644
index 0000000..31559ac
--- /dev/null
+++ b/local/mib2c.array-user.conf
@@ -0,0 +1,1305 @@
+## -*- c -*-
+##
+## For documentation on the code generated by this configuration file,
+## see the file agent/helpers/table_array.c.
+##
+######################################################################
+## Do the .h file
+## @perleval $vars{shortname} =~ s/([A-Z])[a-z]+/$1/g@
+######################################################################
+@foreach $i table@
+@open ${i}.h@
+@eval $hack = "Id"
+/*
+ * Note: this file originally auto-generated by mib2c using
+ * $Id$
+ *
+ * $$hack:$
+ *
+ * Yes, there is lots of code here that you might not use. But it is much
+ * easier to remove code than to add it!
+ */
+#ifndef $i.uc_H
+#define $i.uc_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/library/container.h>
+#include <net-snmp/agent/table_array.h>
+
+ @eval $ext_index = 0@
+ @foreach $idx index@
+ @if "$idx" ne ""@
+ @eval $found = "external"@
+ @foreach $c column@
+ @if "$idx" eq "$c"@
+ @eval $found = "internal"@
+ @end@
+ @end@
+ /** Index $idx is $found */
+ @if "$found" eq "external"@
+ @eval $ext_index = 1@
+ @end@
+ @end@
+ @end@
+
+typedef struct ${i}_context_s {
+ netsnmp_index index; /** THIS MUST BE FIRST!!! */
+
+ /*************************************************************
+ * You can store data internally in this structure.
+ *
+ * TODO: You will probably have to fix a few types here...
+ */
+ @if $ext_index != 0@
+ /** TODO: add storage for external index(s)! */
+ @end@
+ @foreach $c column@
+ /** $c.syntax = $c.type */
+ @eval $have_type = 0@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_type = 1@
+ @eval $o_len = "65535"@
+ @if "$c.syntax" eq "DisplayString"@
+ @eval $o_len = "255"@
+ @end@
+ @if "$c.syntax" eq "SnmpAdminString"@
+ @eval $o_len = "255"@
+ @end@
+ unsigned char $c[$o_len];
+ long ${c}_len;
+ @end@
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_type = 1@
+ oid $c[MAX_OID_LEN];
+ long ${c}_len;
+ @end@
+ @if "$c.type" eq "ASN_UNSIGNED"@
+ @eval $have_type = 1@
+ unsigned long $c;
+ @end@
+ @if "$c.type" eq "ASN_TIMETICKS"@
+ @eval $have_type = 1@
+ unsigned long $c;
+ @end@
+ @if "$c.type" eq "ASN_IPADDRESS"@
+ @eval $have_type = 1@
+ unsigned long $c;
+ @end@
+ @if "$c.type" eq "ASN_UINTEGER"@
+ @eval $have_type = 1@
+ unsigned long $c;
+ @end@
+ @if "$c.type" eq "ASN_INTEGER"@
+ @eval $have_type = 1@
+ long $c;
+ @end@
+ @if "$c.type" eq "ASN_COUNTER"@
+ @eval $have_type = 1@
+ unsigned long $c;
+ @end@
+ @if $have_type == 0@
+ /** TODO: Is this type correct? */
+ long $c;
+ @end@
+
+ @end@
+
+ /*
+ * OR
+ *
+ * Keep a pointer to your data
+ */
+ void * data;
+
+ /*
+ *add anything else you want here
+ */
+
+} ${i}_context;
+
+/*************************************************************
+ * function declarations
+ */
+void init_$i(void);
+void initialize_table_$i(void);
+const ${i}_context * ${i}_get_by_idx(netsnmp_index *);
+const ${i}_context * ${i}_get_by_idx_rs(netsnmp_index *,
+ int row_status);
+int ${i}_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *);
+
+
+/*************************************************************
+ * oid declarations
+ */
+extern const oid ${i}_oid[];
+extern const size_t ${i}_oid_len;
+
+#define ${i}_TABLE_OID $i.commaoid
+
+/*************************************************************
+ * column number definitions for table $i
+ */
+@eval $minv = 0xffffffff@
+@eval $maxv = 0@
+@foreach $c column@
+#define COLUMN_$c.uc $c.subid
+@if ! $c.noaccess@
+@eval $minv = min($minv, $c.subid)@
+@eval $maxv = max($maxv, $c.subid)@
+@end@
+@if "$c.syntax" eq "RowStatus"@
+ @eval $rs_name = "$c"@
+@end@
+@if "$c.syntax" eq "StorageType"@
+ @eval $st_name = "$c"@
+@end@
+@end@
+#define ${i}_COL_MIN $minv
+#define ${i}_COL_MAX $maxv
+
+/* comment out the following line if you don't want a custom sort */
+#define ${i}_CUSTOM_SORT
+
+@if "$rs_name" ne ""@
+/* uncommend the following line if you allow modifications to an
+ * active row */
+/** define ${i}_CAN_MODIFY_ACTIVE_ROW */
+
+@end@
+@if $i.settable@
+int ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr );
+
+void ${i}_set_reserve1( netsnmp_request_group * );
+void ${i}_set_reserve2( netsnmp_request_group * );
+void ${i}_set_action( netsnmp_request_group * );
+void ${i}_set_commit( netsnmp_request_group * );
+void ${i}_set_free( netsnmp_request_group * );
+void ${i}_set_undo( netsnmp_request_group * );
+
+${i}_context * ${i}_duplicate_row( ${i}_context* );
+netsnmp_index * ${i}_delete_row( ${i}_context* );
+
+@if "$rs_name" ne ""@
+int ${i}_can_activate(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg);
+int ${i}_can_deactivate(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg);
+@end@
+int ${i}_can_delete(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg);
+
+
+@if $i.creatable@
+${i}_context * ${i}_create_row( netsnmp_index* );
+@end@
+@end@
+
+#ifdef ${i}_CUSTOM_SORT
+${i}_context * ${i}_get( const char *name, int len );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /** $i.uc_H */
+@end@
+######################################################################
+## Do the .c file
+######################################################################
+@foreach $i table@
+@open ${i}.c@
+@eval $hack = "Id"@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ * $Id$
+ *
+ * $$hack:$
+ *
+ *
+ * For help understanding NET-SNMP in general, please check the
+ * documentation and FAQ at:
+ *
+ * http://www.net-snmp.org/
+ *
+ *
+ * For help understanding this code, the agent and how it processes
+ * requests, please check the following references.
+ *
+ * http://www.net-snmp.org/tutorial-5/
+ *
+ *
+ * You can also join the #net-snmp channel on irc.freenode.net
+ * and ask for help there.
+ *
+ *
+ * And if all else fails, send a detailed message to the developers
+ * describing the problem you are having to:
+ *
+ * net-snmp-coders@lists.sourceforge.net
+ *
+ *
+ * Yes, there is lots of code here that you might not use. But it is much
+ * easier to remove code than to add it!
+ */
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include <net-snmp/library/snmp_assert.h>
+
+#include "${i}.h"
+
+static netsnmp_handler_registration *my_handler = NULL;
+static netsnmp_table_array_callbacks cb;
+
+const oid ${i}_oid[] = { ${i}_TABLE_OID };
+const size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
+
+
+#ifdef ${i}_CUSTOM_SORT
+/************************************************************
+ * keep binary tree to find context by name
+ */
+static int ${i}_cmp( const void *lhs, const void *rhs );
+
+/************************************************************
+ * compare two context pointers here. Return -1 if lhs < rhs,
+ * 0 if lhs == rhs, and 1 if lhs > rhs.
+ */
+static int
+${i}_cmp( const void *lhs, const void *rhs )
+{
+ ${i}_context *context_l =
+ (${i}_context *)lhs;
+ ${i}_context *context_r =
+ (${i}_context *)rhs;
+
+ /*
+ * check primary key, then secondary. Add your own code if
+ * there are more than 2 keys
+ */
+ int rc;
+
+ /*
+ * TODO: implement compare. Remove this ifdef code and
+ * add your own code here.
+ */
+#ifdef TABLE_CONTAINER_TODO
+ snmp_log(LOG_ERR,
+ "${i}_compare not implemented! Container order undefined\n" );
+ return 0;
+#endif
+
+ /*
+ * EXAMPLE (assuming you want to sort on a name):
+ *
+ * rc = strcmp( context_l->xxName, context_r->xxName );
+ *
+ * if(rc)
+ * return rc;
+ *
+ * TODO: fix secondary keys (or delete if there are none)
+ *
+ * if(context_l->yy < context_r->yy)
+ * return -1;
+ *
+ * return (context_l->yy == context_r->yy) ? 0 : 1;
+ */
+}
+
+/************************************************************
+ * search tree
+ */
+/** TODO: set additional indexes as parameters */
+${i}_context *
+${i}_get( const char *name, int len )
+{
+ ${i}_context tmp;
+
+ /** we should have a custom container */
+ netsnmp_assert(cb.container->next != NULL);
+
+ /*
+ * TODO: implement compare. Remove this ifdef code and
+ * add your own code here.
+ */
+#ifdef TABLE_CONTAINER_TODO
+ snmp_log(LOG_ERR, "${i}_get not implemented!\n" );
+ return NULL;
+#endif
+
+ /*
+ * EXAMPLE:
+ *
+ * if(len > sizeof(tmp.xxName))
+ * return NULL;
+ *
+ * strncpy( tmp.xxName, name, sizeof(tmp.xxName) );
+ * tmp.xxName_len = len;
+ *
+ * return CONTAINER_FIND(cb.container->next, &tmp);
+ */
+}
+#endif
+
+
+/************************************************************
+ * Initializes the $i module
+ */
+void
+init_$i(void)
+{
+ initialize_table_$i();
+
+ /*
+ * TODO: perform any startup stuff here, such as
+ * populating the table with initial data.
+ *
+ * ${i}_context * new_row = create_row(index);
+ * CONTAINER_INSERT(cb.container,new_row);
+ */
+}
+
+@if $i.settable@
+/************************************************************
+ * the *_row_copy routine
+ */
+static int ${i}_row_copy(${i}_context * dst,
+ ${i}_context * src)
+{
+ if(!dst||!src)
+ return 1;
+
+ /*
+ * copy index, if provided
+ */
+ if(dst->index.oids)
+ free(dst->index.oids);
+ if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids,
+ src->index.len * sizeof(oid) )) {
+ dst->index.oids = NULL;
+ return 1;
+ }
+ dst->index.len = src->index.len;
+
+
+ /*
+ * copy components into the context structure
+ */
+ @if $ext_index != 0@
+ /** TODO: add code for external index(s)! */
+ @end@
+ @foreach $c column@
+ @eval $have_type = 0@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_type = 1@
+ memcpy( dst->$c, src->$c, src->${c}_len );
+ dst->${c}_len = src->${c}_len;
+ @end@
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_type = 1@
+ memcpy( dst->$c, src->$c, src->${c}_len );
+ dst->${c}_len = src->${c}_len;
+ @end@
+ @if $have_type == 0@
+ dst->$c = src->$c;
+ @end@
+
+ @end@
+ return 0;
+}
+
+/**
+ * the *_extract_index routine
+ *
+ * This routine is called when a set request is received for an index
+ * that was not found in the table container. Here, we parse the oid
+ * in the the individual index components and copy those indexes to the
+ * context. Then we make sure the indexes for the new row are valid.
+ */
+int
+${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr )
+{
+ /*
+ * temporary local storage for extracting oid index
+ *
+ * extract index uses varbinds (netsnmp_variable_list) to parse
+ * the index OID into the individual components for each index part.
+ */
+ @if $ext_index != 0@
+ /** TODO: add storage for external index(s)! */
+ @end@
+ @eval $first_idx = ""@
+ @foreach $idx index@
+ @if "$first_idx" eq ""@
+ @eval $first_idx = $idx@
+ @end@
+ netsnmp_variable_list var_$idx;
+ @end@
+ int err;
+
+ /*
+ * copy index, if provided
+ */
+ if(hdr) {
+ netsnmp_assert(ctx->index.oids == NULL);
+ if((hdr->len > MAX_OID_LEN) ||
+ snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids,
+ hdr->len * sizeof(oid) )) {
+ return -1;
+ }
+ ctx->index.len = hdr->len;
+ }
+
+ /*
+ * initialize variable that will hold each component of the index.
+ * If there are multiple indexes for the table, the variable_lists
+ * need to be linked together, in order.
+ */
+ @if $ext_index != 0@
+ /** TODO: add code for external index(s)! */
+ @end@
+ @foreach $idx index@
+ memset( &var_$idx, 0x00, sizeof(var_$idx) );
+ var_${idx}.type = $idx.type; /* type hint for parse_oid_indexes */
+ /** TODO: link this index to the next, or NULL for the last one */
+#ifdef TABLE_CONTAINER_TODO
+ snmp_log(LOG_ERR, "${i}_extract_index index list not implemented!\n" );
+ return 0;
+#else
+ var_${idx}.next_variable = &var_XX;
+#endif
+
+ @end@
+
+ /*
+ * parse the oid into the individual index components
+ */
+ err = parse_oid_indexes( hdr->oids, hdr->len, &var_$first_idx );
+ if (err == SNMP_ERR_NOERROR) {
+ /*
+ * copy index components into the context structure
+ */
+ @foreach $idx index@
+ @eval $found = "external"@
+ @foreach $c column@
+ @if "$idx" eq "$c"@
+ @eval $found = "internal"@
+ @end@
+ @end@
+ @if "$found" eq "external"@
+ /** skipping external index $idx */
+ @end@
+ @if "$found" eq "internal"@
+ @eval $have_type = 0@
+ @if "$idx.type" eq "ASN_OCTET_STR"@
+ @eval $have_type = 1@
+ if(var_${idx}.val_len > sizeof(ctx->$idx))
+ err = -1;
+ else
+ memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len );
+ ctx->${idx}_len = var_${idx}.val_len;
+ @end@
+ @if "$idx.type" eq "ASN_OBJECT_ID"@
+ @eval $have_type = 1@
+ memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len );
+ ctx->${idx}_len = var_${idx}.val_len;
+ @end@
+ @if $have_type == 0@
+ ctx->$idx = *var_${idx}.val.integer;
+ @end@
+ @end@
+
+ @end@
+
+ @foreach $c index@
+ /*
+ * TODO: check index for valid values. For EXAMPLE:
+ *
+ @eval $have_check = 0@
+ @if "$c.type" eq "ASN_IPADDRESS"@
+ @eval $have_check = 1@
+ * if ( XXX_check_ip( *var_${c}.val.integer ) ) {
+ @end@
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_check = 1@
+ * if ( XXX_check_oid( var_${c}.val.objid, var_${c}.val_len /
+ sizeof(oid) ) ) {
+ @end@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_check = 1@
+ * if ( XXX_check_value( var_${c}.val.string, XXX ) ) {
+ @end@
+ @if $have_check != 1@
+ * if ( *var_${c}.val.integer != XXX ) {
+ @end@
+ * err = -1;
+ * }
+ */
+ @end@
+ }
+
+ /*
+ * parsing may have allocated memory. free it.
+ */
+ snmp_reset_var_buffers( &var_$first_idx );
+
+ return err;
+}
+
+@if "$rs_name" ne ""@
+/************************************************************
+ * the *_can_activate routine is called
+ * when a row is changed to determine if all the values
+ * set are consistent with the row's rules for a row status
+ * of ACTIVE.
+ *
+ * return 1 if the row could be ACTIVE
+ * return 0 if the row is not ready for the ACTIVE state
+ */
+int ${i}_can_activate(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg)
+{
+ /*
+ * TODO: check for activation requirements here
+ */
+
+
+ /*
+ * be optimistic.
+ */
+ return 1;
+}
+
+/************************************************************
+ * the *_can_deactivate routine is called when a row that is
+ * currently ACTIVE is set to a state other than ACTIVE. If
+ * there are conditions in which a row should not be allowed
+ * to transition out of the ACTIVE state (such as the row being
+ * referred to by another row or table), check for them here.
+ *
+ * return 1 if the row can be set to a non-ACTIVE state
+ * return 0 if the row must remain in the ACTIVE state
+ */
+int ${i}_can_deactivate(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg)
+{
+ /*
+ * TODO: check for deactivation requirements here
+ */
+ return 1;
+}
+
+@end@
+/************************************************************
+ * the *_can_delete routine is called to determine if a row
+ * can be deleted.
+ *
+ * return 1 if the row can be deleted
+ * return 0 if the row cannot be deleted
+ */
+int ${i}_can_delete(${i}_context *undo_ctx,
+ ${i}_context *row_ctx,
+ netsnmp_request_group * rg)
+{
+@if "$rs_name" ne ""@
+ /*
+ * probably shouldn't delete a row that we can't
+ * deactivate.
+ */
+ if(${i}_can_deactivate(undo_ctx,row_ctx,rg) != 1)
+ return 0;
+@end@
+
+ /*
+ * TODO: check for other deletion requirements here
+ */
+ return 1;
+}
+
+@if $i.creatable@
+/************************************************************
+ * the *_create_row routine is called by the table handler
+ * to create a new row for a given index. If you need more
+ * information (such as column values) to make a decision
+ * on creating rows, you must create an initial row here
+ * (to hold the column values), and you can examine the
+ * situation in more detail in the *_set_reserve1 or later
+ * states of set processing. Simple check for a NULL undo_ctx
+ * in those states and do detailed creation checking there.
+ *
+ * returns a newly allocated ${i}_context
+ * structure if the specified indexes are not illegal
+ * returns NULL for errors or illegal index values.
+ */
+${i}_context *
+${i}_create_row( netsnmp_index* hdr)
+{
+ ${i}_context * ctx =
+ SNMP_MALLOC_TYPEDEF(${i}_context);
+ if(!ctx)
+ return NULL;
+
+ /*
+ * TODO: check indexes, if necessary.
+ */
+ if(${i}_extract_index( ctx, hdr )) {
+ if (NULL != ctx->index.oids)
+ 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!
+ */
+ /**
+ @foreach $c column@
+ @if $c.settable@
+ ctx->$c = 0;
+ @end@
+ @end@
+ */
+
+ return ctx;
+}
+@end@
+
+/************************************************************
+ * the *_duplicate row routine
+ */
+${i}_context *
+${i}_duplicate_row( ${i}_context * row_ctx)
+{
+ ${i}_context * dup;
+
+ if(!row_ctx)
+ return NULL;
+
+ dup = SNMP_MALLOC_TYPEDEF(${i}_context);
+ if(!dup)
+ return NULL;
+
+ if(${i}_row_copy(dup,row_ctx)) {
+ free(dup);
+ dup = NULL;
+ }
+
+ return dup;
+}
+
+/************************************************************
+ * the *_delete_row method is called to delete a row.
+ */
+netsnmp_index * ${i}_delete_row( ${i}_context * ctx )
+{
+ /* netsnmp_mutex_destroy(ctx->lock); */
+
+ if(ctx->index.oids)
+ free(ctx->index.oids);
+
+ /*
+ * TODO: release any memory you allocated here...
+ */
+
+ /*
+ * release header
+ */
+ free( ctx );
+
+ return NULL;
+}
+
+
+/************************************************************
+ * RESERVE is used to check the syntax of all the variables
+ * provided, that the values being set are sensible and consistent,
+ * and to allocate any resources required for performing the SET.
+ * After this stage, the expectation is that the set ought to
+ * succeed, though this is not guaranteed. (In fact, with the UCD
+ * agent, this is done in two passes - RESERVE1, and
+ * RESERVE2, to allow for dependancies between variables).
+ *
+ * BEFORE calling this routine, the agent will call duplicate_row
+ * to create a copy of the row (unless this is a new row; i.e.
+ * row_created == 1).
+ *
+ * next state -> SET_RESERVE2 || SET_FREE
+ */
+void ${i}_set_reserve1( netsnmp_request_group *rg )
+{
+ ${i}_context *row_ctx =
+ (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx =
+ (${i}_context *)rg->undo_info;
+ netsnmp_variable_list *var;
+ netsnmp_request_group_item *current;
+ int rc;
+
+ @if "$st_name" ne ""@
+ /*
+ * Block all attempts to modify a readOnly row
+ */
+ if( row_ctx && (row_ctx->$st_name == SNMP_STORAGE_READONLY) ) {
+ netsnmp_set_mode_request_error(MODE_SET_BEGIN, rg->list->ri,
+ SNMP_ERR_NOTWRITABLE);
+ return;
+ }
+ @end@
+
+ /*
+ * TODO: loop through columns, check syntax and lengths. For
+ * columns which have no dependencies, you could also move
+ * the value/range checking here to attempt to catch error
+ * cases as early as possible.
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+ rc = SNMP_ERR_NOERROR;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ @if $c.needlength@
+ /* or possibly 'netsnmp_check_vb_type_and_size' */
+ rc = netsnmp_check_vb_type_and_max_size(var, $c.type,
+ sizeof(row_ctx->$c));
+ @else@
+ /* or possibly 'netsnmp_check_vb_int_range' */
+ rc = netsnmp_check_vb_int( var );
+ @end@
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ rc = SNMP_ERR_GENERR;
+ snmp_log(LOG_ERR, "unknown column in "
+ "${i}_set_reserve1\n");
+ }
+
+ if (rc)
+ netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc );
+ rg->status = SNMP_MAX( rg->status, current->ri->status );
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+}
+
+void ${i}_set_reserve2( netsnmp_request_group *rg )
+{
+ ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
+ netsnmp_request_group_item *current;
+ netsnmp_variable_list *var;
+ int rc;
+
+ rg->rg_void = rg->list->ri;
+
+ /*
+ * TODO: loop through columns, check for valid
+ * values and any range constraints.
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+ rc = SNMP_ERR_NOERROR;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ @eval $have_check = 0@
+ @if "$c" eq "$st_name"@
+ @eval $have_check = 1@
+ rc = netsnmp_check_vb_storagetype(current->ri->requestvb,
+ undo_ctx ?
+ undo_ctx->$c:0);
+ @end@
+ @if "$c" eq "$rs_name"@
+ @eval $have_check = 1@
+ rc = netsnmp_check_vb_rowstatus(current->ri->requestvb,
+ undo_ctx ?
+ undo_ctx->$c:0);
+ rg->rg_void = current->ri;
+ @end@
+ @if "$c.syntax" eq "TruthValue"@
+ @eval $have_check = 1@
+ rc = netsnmp_check_vb_truthvalue(current->ri->requestvb);
+ @end@
+ @if $have_check == 0@
+ /*
+ * TODO: routine to check valid values
+ *
+ * EXAMPLE:
+ *
+ @if "$c.type" eq "ASN_IPADDRESS"@
+ @eval $have_check = 1@
+ * if ( XXX_check_ip( *var->val.integer ) ) {
+ @end@
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_check = 1@
+ * if ( XXX_check_oid( var ) ) {
+ @end@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_check = 1@
+ * if ( XXX_check_value( var->val.string, XXX ) ) {
+ @end@
+ @if $have_check != 1@
+ * if ( *var->val.integer != XXX ) {
+ @end@
+ * rc = SNMP_ERR_INCONSISTENTVALUE;
+ * rc = SNMP_ERR_BADVALUE;
+ * }
+ */
+ @end@
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ netsnmp_assert(0); /** why wasn't this caught in reserve1? */
+ }
+
+ if (rc)
+ netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc);
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+}
+
+/************************************************************
+ * Assuming that the RESERVE phases were successful, the next
+ * stage is indicated by the action value ACTION. This is used
+ * to actually implement the set operation. However, this must
+ * either be done into temporary (persistent) storage, or the
+ * previous value stored similarly, in case any of the subsequent
+ * ACTION calls fail.
+ *
+ * In your case, changes should be made to row_ctx. A copy of
+ * the original row is in undo_ctx.
+ */
+void ${i}_set_action( netsnmp_request_group *rg )
+{
+ netsnmp_variable_list *var;
+ ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
+ netsnmp_request_group_item *current;
+
+ @if "$rs_name" ne ""@
+ int row_err = 0;
+ @end@
+
+ /*
+ * TODO: loop through columns, copy varbind values
+ * to context structure for the row.
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ @eval $have_type = 0@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_type = 1@
+ memcpy(row_ctx->$c,var->val.string,var->val_len);
+ row_ctx->${c}_len = var->val_len;
+ @end@
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_type = 1@
+ memcpy(row_ctx->$c,var->val.objid,var->val_len);
+ row_ctx->${c}_len = var->val_len;
+ @end@
+ @if $have_type == 0@
+ row_ctx->$c = *var->val.integer;
+ @end@
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ netsnmp_assert(0); /** why wasn't this caught in reserve1? */
+ }
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+ @if "$rs_name" ne ""@
+#ifndef ${i}_CAN_MODIFY_ACTIVE_ROW
+ if( undo_ctx && RS_IS_ACTIVE(undo_ctx->$rs_name) &&
+ row_ctx && RS_IS_ACTIVE(row_ctx->$rs_name) ) {
+ row_err = 1;
+ }
+#endif
+
+ /*
+ * check activation/deactivation
+ */
+ row_err = netsnmp_table_array_check_row_status(&cb, rg,
+ row_ctx ? &row_ctx->$rs_name : NULL,
+ undo_ctx ? &undo_ctx->$rs_name : NULL);
+ if(row_err) {
+ netsnmp_set_mode_request_error(MODE_SET_BEGIN,
+ (netsnmp_request_info*)rg->rg_void,
+ row_err);
+ return;
+ }
+
+ @end@
+ /*
+ * TODO: if you have dependencies on other tables, this would be
+ * a good place to check those, too.
+ */
+}
+
+/************************************************************
+ * Only once the ACTION phase has completed successfully, can
+ * the final COMMIT phase be run. This is used to complete any
+ * writes that were done into temporary storage, and then release
+ * any allocated resources. Note that all the code in this phase
+ * should be "safe" code that cannot possibly fail (cue
+ * hysterical laughter). The whole 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.
+ *
+ * BEFORE calling this routine, the agent will update the
+ * container (inserting a row if row_created == 1, or removing
+ * the row if row_deleted == 1).
+ *
+ * AFTER calling this routine, the agent will delete the
+ * undo_info.
+ */
+void ${i}_set_commit( netsnmp_request_group *rg )
+{
+ netsnmp_variable_list *var;
+ ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
+ netsnmp_request_group_item *current;
+
+ /*
+ * loop through columns
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ netsnmp_assert(0); /** why wasn't this caught in reserve1? */
+ }
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+}
+
+/************************************************************
+ * 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.
+ *
+ * AFTER calling this routine, the agent will delete undo_info.
+ */
+void ${i}_set_free( netsnmp_request_group *rg )
+{
+ netsnmp_variable_list *var;
+ ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
+ netsnmp_request_group_item *current;
+
+ /*
+ * loop through columns
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ /** should have been logged in reserve1 */
+ }
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+}
+
+/************************************************************
+ * 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.
+ *
+ * BEFORE calling this routine, the agent will update the container
+ * (remove any newly inserted row, re-insert any removed row).
+ *
+ * AFTER calling this routing, the agent will call row_copy
+ * to restore the data in existing_row from the date in undo_info.
+ * Then undo_info will be deleted (or existing row, if row_created
+ * == 1).
+ */
+void ${i}_set_undo( netsnmp_request_group *rg )
+{
+ netsnmp_variable_list *var;
+ ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
+ ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
+ netsnmp_request_group_item *current;
+
+ /*
+ * loop through columns
+ */
+ for( current = rg->list; current; current = current->next ) {
+
+ var = current->ri->requestvb;
+
+ switch(current->tri->colnum) {
+
+ @foreach $c column@
+ @if $c.settable@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ netsnmp_assert(0); /** why wasn't this caught in reserve1? */
+ }
+ }
+
+ /*
+ * done with all the columns. Could check row related
+ * requirements here.
+ */
+}
+
+@end@
+
+
+/************************************************************
+ *
+ * Initialize the $i table by defining its contents and how it's structured
+ */
+void
+initialize_table_$i(void)
+{
+ netsnmp_table_registration_info *table_info;
+
+ if(my_handler) {
+ snmp_log(LOG_ERR, "initialize_table_${i}_handler called again\n");
+ return;
+ }
+
+ memset(&cb, 0x00, sizeof(cb));
+
+ /** create the table structure itself */
+ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
+
+ my_handler = netsnmp_create_handler_registration("$i",
+ netsnmp_table_array_helper_handler,
+ ${i}_oid,
+ ${i}_oid_len,
+@if $i.settable@
+ HANDLER_CAN_RWRITE
+@else@
+ HANDLER_CAN_RONLY
+@end@
+ );
+
+ if (!my_handler || !table_info) {
+ snmp_log(LOG_ERR, "malloc failed in "
+ "initialize_table_${i}_handler\n");
+ return; /** mallocs failed */
+ }
+
+ /***************************************************
+ * Setting up the table's definition
+ */
+ /*
+ * TODO: add any external indexes here.
+ */
+ @if $ext_index != 0@
+ /** TODO: add code for external index(s)! */
+ @end@
+
+ /*
+ * internal indexes
+ */
+ @foreach $idx index@
+ /** index: $idx */
+ netsnmp_table_helper_add_index(table_info, $idx.type);
+ @end@
+
+ table_info->min_column = ${i}_COL_MIN;
+ table_info->max_column = ${i}_COL_MAX;
+
+ /***************************************************
+ * registering the table with the master agent
+ */
+ cb.get_value = ${i}_get_value;
+ cb.container = netsnmp_container_find("${i}_primary:"
+ "${i}:"
+ "table_container");
+#ifdef ${i}_CUSTOM_SORT
+ netsnmp_container_add_index(cb.container,
+ netsnmp_container_find("${i}_custom:"
+ "${i}:"
+ "table_container"));
+ cb.container->next->compare = ${i}_cmp;
+#endif
+@if $i.settable@
+ cb.can_set = 1;
+@if $i.creatable@
+ cb.create_row = (UserRowMethod*)${i}_create_row;
+@end@
+ cb.duplicate_row = (UserRowMethod*)${i}_duplicate_row;
+ cb.delete_row = (UserRowMethod*)${i}_delete_row;
+ cb.row_copy = (Netsnmp_User_Row_Operation *)${i}_row_copy;
+
+@if "$rs_name" ne ""@
+ cb.can_activate = (Netsnmp_User_Row_Action *)${i}_can_activate;
+ cb.can_deactivate = (Netsnmp_User_Row_Action *)${i}_can_deactivate;
+@end@
+ cb.can_delete = (Netsnmp_User_Row_Action *)${i}_can_delete;
+
+ cb.set_reserve1 = ${i}_set_reserve1;
+ cb.set_reserve2 = ${i}_set_reserve2;
+ cb.set_action = ${i}_set_action;
+ cb.set_commit = ${i}_set_commit;
+ cb.set_free = ${i}_set_free;
+ cb.set_undo = ${i}_set_undo;
+@end@
+ DEBUGMSGTL(("initialize_table_$i",
+ "Registering table $i "
+ "as a table array\n"));
+ netsnmp_table_container_register(my_handler, table_info, &cb,
+ cb.container, 1);
+}
+
+/************************************************************
+ * ${i}_get_value
+ *
+ * This routine is called for get requests to copy the data
+ * from the context to the varbind for the request. If the
+ * context has been properly maintained, you don't need to
+ * change in code in this fuction.
+ */
+int ${i}_get_value(
+ netsnmp_request_info *request,
+ netsnmp_index *item,
+ netsnmp_table_request_info *table_info )
+{
+ netsnmp_variable_list *var = request->requestvb;
+ ${i}_context *context = (${i}_context *)item;
+
+ switch(table_info->colnum) {
+
+ @foreach $c column@
+ @if $c.readable@
+ @eval $have_type = 0@
+ case COLUMN_$c.uc:
+ /** $c.syntax = $c.type */
+ @if "$c.type" eq "ASN_OBJECT_ID"@
+ @eval $have_type = 1@
+ snmp_set_var_typed_value(var, $c.type,
+ (char*)&context->$c,
+ context->${c}_len );
+ @end@
+ @if "$c.type" eq "ASN_OCTET_STR"@
+ @eval $have_type = 1@
+ snmp_set_var_typed_value(var, $c.type,
+ (char*)&context->$c,
+ context->${c}_len );
+ @end@
+ @if $have_type == 0@
+ snmp_set_var_typed_value(var, $c.type,
+ (char*)&context->$c,
+ sizeof(context->$c) );
+ @end@
+ break;
+
+ @end@
+ @end@
+ default: /** We shouldn't get here */
+ snmp_log(LOG_ERR, "unknown column in "
+ "${i}_get_value\n");
+ return SNMP_ERR_GENERR;
+ }
+ return SNMP_ERR_NOERROR;
+}
+
+/************************************************************
+ * ${i}_get_by_idx
+ */
+const ${i}_context *
+${i}_get_by_idx(netsnmp_index * hdr)
+{
+ return (const ${i}_context *)
+ CONTAINER_FIND(cb.container, hdr );
+}
+
+
+@end@