## -*- 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: mib2c.array-user.conf,v 1.1.1.1 2005/04/29 01:45:17 echo Exp $ * * $$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 #include #include @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 oid ${i}_oid[]; extern 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 handle SET-REQUEST for $i */ #define ${i}_SET_HANDLING /* comment out the following line if you can't create new rows */ #define ${i}_ROW_CREATION /* comment out the following line if you don't want the secondary index */ #define ${i}_IDX2 @if "$rs_name" ne ""@ /* uncommend the following line if you allow modifications to an * active row */ /** define ${i}_CAN_MODIFY_ACTIVE_ROW */ @end@ #ifdef ${i}_SET_HANDLING 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); #ifdef ${i}_ROW_CREATION ${i}_context * ${i}_create_row( netsnmp_index* ); #endif #endif #ifdef ${i}_IDX2 ${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: mib2c.array-user.conf,v 1.1.1.1 2005/04/29 01:45:17 echo Exp $ * * $$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/ * * * You can also join the #net-snmp channel on irc.openprojects.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 #include #include #include #include "${i}.h" static netsnmp_handler_registration *my_handler = NULL; static netsnmp_table_array_callbacks cb; oid ${i}_oid[] = { ${i}_TABLE_OID }; size_t ${i}_oid_len = OID_LENGTH(${i}_oid); #ifdef ${i}_IDX2 /************************************************************ * 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 indexes */ 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: * * 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 secondary index */ 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 */ } /************************************************************ * 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( src->$c, dst->$c, src->${c}_len ); dst->${c}_len = src->${c}_len; @end@ @if $have_type == 0@ dst->$c = src->$c; @end@ @end@ return 0; } #ifdef ${i}_SET_HANDLING /* * the *_extract_index routine */ int ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr ) { /* * temporary local storage for extracting oid index */ @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(snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids, hdr->len * sizeof(oid) )) { return -1; } ctx->index.len = hdr->len; } /** * Create variable to hold each component of the index */ @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; /** 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 components */ err = parse_oid_indexes( hdr->oids, hdr->len, &var_$first_idx ); if (err == SNMP_ERR_NOERROR) { /* * copy 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 */ 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; } #ifdef ${i}_ROW_CREATION /************************************************************ * 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 )) { 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; } #endif /************************************************************ * 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 */ rc = netsnmp_check_vb_type_and_size(var, $c.type, sizeof(row_ctx->$c)); 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. */ } #endif /** ${i}_SET_HANDLING */ /************************************************************ * * 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); /* if your table is read only, it's easiest to change the HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */ my_handler = netsnmp_create_handler_registration("$i", netsnmp_table_array_helper_handler, ${i}_oid, ${i}_oid_len, HANDLER_CAN_RWRITE); 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}_IDX2 netsnmp_container_add_index(cb.container, netsnmp_container_find("${i}_secondary:" "${i}:" "table_container")); cb.container->next->compare = ${i}_cmp; #endif #ifdef ${i}_SET_HANDLING cb.can_set = 1; #ifdef ${i}_ROW_CREATION cb.create_row = (UserRowMethod*)${i}_create_row; #endif 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; #endif 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 */ 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@ @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@ 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@ @end@ @end@