| ## -*- 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@ |