# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / local / mib2c.array-user.conf
1 ## -*- c -*-
2 ##
3 ## For documentation on the code generated by this configuration file,
4 ## see the file agent/helpers/table_array.c.
5 ##
6 ######################################################################
7 ## Do the .h file
8 ## @perleval $vars{shortname} =~ s/([A-Z])[a-z]+/$1/g@
9 ######################################################################
10 @foreach $i table@
11 @open ${i}.h@
12 @eval $hack = "Id"
13 /*
14  * Note: this file originally auto-generated by mib2c using
15  *        $Id: mib2c.array-user.conf,v 1.1.1.1 2005/04/29 01:45:17 echo Exp $
16  *
17  * $$hack:$
18  *
19  * Yes, there is lots of code here that you might not use. But it is much
20  * easier to remove code than to add it!
21  */
22 #ifndef $i.uc_H
23 #define $i.uc_H
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28
29     
30 #include <net-snmp/net-snmp-config.h>
31 #include <net-snmp/library/container.h>
32 #include <net-snmp/agent/table_array.h>
33
34     @eval $ext_index = 0@
35     @foreach $idx index@
36         @if "$idx" ne ""@
37         @eval $found = "external"@
38         @foreach $c column@
39             @if "$idx" eq "$c"@
40                @eval $found = "internal"@
41             @end@
42         @end@
43         /** Index $idx is $found */
44         @if "$found" eq "external"@
45            @eval $ext_index = 1@
46         @end@
47         @end@
48     @end@
49
50 typedef struct ${i}_context_s {
51     netsnmp_index index; /** THIS MUST BE FIRST!!! */
52
53     /*************************************************************
54      * You can store data internally in this structure.
55      *
56      * TODO: You will probably have to fix a few types here...
57      */
58     @if $ext_index != 0@
59     /** TODO: add storage for external index(s)! */
60     @end@
61     @foreach $c column@
62         /** $c.syntax = $c.type */
63         @eval $have_type = 0@
64         @if "$c.type" eq "ASN_OCTET_STR"@
65             @eval $have_type = 1@
66             @eval $o_len = "65535"@
67             @if "$c.syntax" eq "DisplayString"@
68                 @eval $o_len = "255"@
69             @end@
70             @if "$c.syntax" eq "SnmpAdminString"@
71                 @eval $o_len = "255"@
72             @end@
73             unsigned char $c[$o_len];
74             long ${c}_len;
75         @end@
76         @if "$c.type" eq "ASN_OBJECT_ID"@
77             @eval $have_type = 1@
78             oid $c[MAX_OID_LEN];
79             long ${c}_len;
80         @end@
81         @if "$c.type" eq "ASN_UNSIGNED"@
82             @eval $have_type = 1@
83             unsigned long $c;
84         @end@
85         @if "$c.type" eq "ASN_TIMETICKS"@
86             @eval $have_type = 1@
87             unsigned long $c;
88         @end@
89         @if "$c.type" eq "ASN_IPADDRESS"@
90             @eval $have_type = 1@
91             unsigned long $c;
92         @end@
93         @if "$c.type" eq "ASN_UINTEGER"@
94             @eval $have_type = 1@
95             unsigned long $c;
96         @end@
97         @if "$c.type" eq "ASN_INTEGER"@
98             @eval $have_type = 1@
99             long $c;
100         @end@
101         @if "$c.type" eq "ASN_COUNTER"@
102             @eval $have_type = 1@
103             unsigned long $c;
104         @end@
105         @if $have_type == 0@
106     /** TODO: Is this type correct? */
107             long $c;
108         @end@
109
110     @end@
111
112     /*
113      * OR
114      *
115      * Keep a pointer to your data
116      */
117     void * data;
118
119     /*
120      *add anything else you want here
121      */
122
123 } ${i}_context;
124
125 /*************************************************************
126  * function declarations
127  */
128 void init_$i(void);
129 void initialize_table_$i(void);
130 const ${i}_context * ${i}_get_by_idx(netsnmp_index *);
131 const ${i}_context * ${i}_get_by_idx_rs(netsnmp_index *,
132                                         int row_status);
133 int ${i}_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *);
134
135
136 /*************************************************************
137  * oid declarations
138  */
139 extern oid ${i}_oid[];
140 extern size_t ${i}_oid_len;
141
142 #define ${i}_TABLE_OID $i.commaoid
143     
144 /*************************************************************
145  * column number definitions for table $i
146  */
147 @eval $minv = 0xffffffff@
148 @eval $maxv = 0@
149 @foreach $c column@
150 #define COLUMN_$c.uc $c.subid
151 @if ! $c.noaccess@
152 @eval $minv = min($minv, $c.subid)@
153 @eval $maxv = max($maxv, $c.subid)@
154 @end@
155 @if "$c.syntax" eq "RowStatus"@
156    @eval $rs_name = "$c"@
157 @end@
158 @if "$c.syntax" eq "StorageType"@
159    @eval $st_name = "$c"@
160 @end@
161 @end@
162 #define ${i}_COL_MIN $minv
163 #define ${i}_COL_MAX $maxv
164
165 /* comment out the following line if you don't handle SET-REQUEST for $i */
166 #define ${i}_SET_HANDLING
167
168 /* comment out the following line if you can't create new rows */
169 #define ${i}_ROW_CREATION
170
171 /* comment out the following line if you don't want the secondary index */
172 #define ${i}_IDX2
173
174 @if "$rs_name" ne ""@
175 /* uncommend the following line if you allow modifications to an
176  * active row */
177 /** define ${i}_CAN_MODIFY_ACTIVE_ROW */
178
179 @end@
180 #ifdef ${i}_SET_HANDLING
181
182 int ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr );
183
184 void ${i}_set_reserve1( netsnmp_request_group * );
185 void ${i}_set_reserve2( netsnmp_request_group * );
186 void ${i}_set_action( netsnmp_request_group * );
187 void ${i}_set_commit( netsnmp_request_group * );
188 void ${i}_set_free( netsnmp_request_group * );
189 void ${i}_set_undo( netsnmp_request_group * );
190
191 ${i}_context * ${i}_duplicate_row( ${i}_context* );
192 netsnmp_index * ${i}_delete_row( ${i}_context* );
193
194 @if "$rs_name" ne ""@
195 int ${i}_can_activate(${i}_context *undo_ctx,
196                       ${i}_context *row_ctx,
197                       netsnmp_request_group * rg);
198 int ${i}_can_deactivate(${i}_context *undo_ctx,
199                         ${i}_context *row_ctx,
200                         netsnmp_request_group * rg);
201 @end@
202 int ${i}_can_delete(${i}_context *undo_ctx,
203                     ${i}_context *row_ctx,
204                     netsnmp_request_group * rg);
205     
206     
207 #ifdef ${i}_ROW_CREATION
208 ${i}_context * ${i}_create_row( netsnmp_index* );
209 #endif
210 #endif
211
212 #ifdef ${i}_IDX2
213 ${i}_context * ${i}_get( const char *name, int len );
214 #endif
215
216 #ifdef __cplusplus
217 };
218 #endif
219
220 #endif /** $i.uc_H */
221 @end@
222 ######################################################################
223 ## Do the .c file
224 ######################################################################
225 @foreach $i table@
226 @open ${i}.c@
227 @eval $hack = "Id"@
228 /*
229  * Note: this file originally auto-generated by mib2c using
230  *       $Id: mib2c.array-user.conf,v 1.1.1.1 2005/04/29 01:45:17 echo Exp $
231  *
232  * $$hack:$
233  *
234  *
235  * For help understanding NET-SNMP in general, please check the 
236  *     documentation and FAQ at:
237  *
238  *     http://www.net-snmp.org/
239  *
240  *
241  * For help understanding this code, the agent and how it processes
242  *     requests, please check the following references.
243  *
244  *     http://www.net-snmp.org/tutorial/
245  *
246  *
247  * You can also join the #net-snmp channel on irc.openprojects.net
248  *     and ask for help there.
249  *
250  *
251  * And if all else fails, send a detailed message to the developers
252  *     describing the problem you are having to:
253  *
254  *    net-snmp-coders@lists.sourceforge.net
255  *
256  *
257  * Yes, there is lots of code here that you might not use. But it is much
258  * easier to remove code than to add it!
259  */
260 #include <net-snmp/net-snmp-config.h>
261 #include <net-snmp/net-snmp-includes.h>
262 #include <net-snmp/agent/net-snmp-agent-includes.h>
263
264 #include <net-snmp/library/snmp_assert.h>
265
266 #include "${i}.h"
267
268 static     netsnmp_handler_registration *my_handler = NULL;
269 static     netsnmp_table_array_callbacks cb;
270
271 oid ${i}_oid[] = { ${i}_TABLE_OID };
272 size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
273
274
275 #ifdef ${i}_IDX2
276 /************************************************************
277  * keep binary tree to find context by name
278  */
279 static int ${i}_cmp( const void *lhs, const void *rhs );
280
281 /************************************************************
282  * compare two context pointers here. Return -1 if lhs < rhs,
283  * 0 if lhs == rhs, and 1 if lhs > rhs.
284  */
285 static int
286 ${i}_cmp( const void *lhs, const void *rhs )
287 {
288     ${i}_context *context_l =
289         (${i}_context *)lhs;
290     ${i}_context *context_r =
291         (${i}_context *)rhs;
292
293     /*
294      * check primary key, then secondary. Add your own code if
295      * there are more than 2 indexes
296      */
297     int rc;
298
299     /*
300      * TODO: implement compare. Remove this ifdef code and
301      * add your own code here.
302      */
303 #ifdef TABLE_CONTAINER_TODO
304     snmp_log(LOG_ERR,
305              "${i}_compare not implemented! Container order undefined\n" );
306     return 0;
307 #endif
308     
309     /*
310      * EXAMPLE:
311      *   
312      * rc = strcmp( context_l->xxName, context_r->xxName);
313      *
314      * if(rc)
315      *   return rc;
316      *
317      * TODO: fix secondary keys (or delete if there are none)
318      *
319      * if(context_l->yy < context_r->yy) 
320      *   return -1;
321      *
322      * return (context_l->yy == context_r->yy) ? 0 : 1;
323      */
324 }
325
326 /************************************************************
327  * search tree
328  */
329 /** TODO: set additional indexes as parameters */
330 ${i}_context *
331 ${i}_get( const char *name, int len )
332 {
333     ${i}_context tmp;
334
335     /** we should have a secondary index */
336     netsnmp_assert(cb.container->next != NULL);
337     
338     /*
339      * TODO: implement compare. Remove this ifdef code and
340      * add your own code here.
341      */
342 #ifdef TABLE_CONTAINER_TODO
343     snmp_log(LOG_ERR, "${i}_get not implemented!\n" );
344     return NULL;
345 #endif
346
347     /*
348      * EXAMPLE:
349      *
350      * if(len > sizeof(tmp.xxName))
351      *   return NULL;
352      *
353      * strncpy( tmp.xxName, name, sizeof(tmp.xxName) );
354      * tmp.xxName_len = len;
355      *
356      * return CONTAINER_FIND(cb.container->next, &tmp);
357      */
358 }
359 #endif
360
361
362 /************************************************************
363  * Initializes the $i module
364  */
365 void
366 init_$i(void)
367 {
368     initialize_table_$i();
369
370     /*
371      * TODO: perform any startup stuff here
372      */
373 }
374
375 /************************************************************
376  * the *_row_copy routine
377  */
378 static int ${i}_row_copy(${i}_context * dst,
379                          ${i}_context * src)
380 {
381     if(!dst||!src)
382         return 1;
383         
384     /*
385      * copy index, if provided
386      */
387     if(dst->index.oids)
388         free(dst->index.oids);
389     if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids,
390                            src->index.len * sizeof(oid) )) {
391         dst->index.oids = NULL;
392         return 1;
393     }
394     dst->index.len = src->index.len;
395     
396
397     /*
398      * copy components into the context structure
399      */
400     @if $ext_index != 0@
401     /** TODO: add code for external index(s)! */
402     @end@
403     @foreach $c column@
404     @eval $have_type = 0@
405     @if "$c.type" eq "ASN_OCTET_STR"@
406     @eval $have_type = 1@
407     memcpy( dst->$c, src->$c, src->${c}_len );
408     dst->${c}_len = src->${c}_len;
409     @end@
410     @if "$c.type" eq "ASN_OBJECT_ID"@
411     @eval $have_type = 1@
412     memcpy( src->$c, dst->$c, src->${c}_len );
413     dst->${c}_len = src->${c}_len;
414     @end@
415     @if $have_type == 0@
416     dst->$c = src->$c;
417     @end@
418
419     @end@
420     return 0;
421 }
422
423 #ifdef ${i}_SET_HANDLING
424
425 /*
426  * the *_extract_index routine
427  */
428 int
429 ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr )
430 {
431     /*
432      * temporary local storage for extracting oid index
433      */
434     @if $ext_index != 0@
435     /** TODO: add storage for external index(s)! */
436     @end@
437     @eval $first_idx = ""@
438     @foreach $idx index@
439         @if "$first_idx" eq ""@
440         @eval $first_idx = $idx@
441         @end@
442     netsnmp_variable_list var_$idx;
443     @end@
444     int err;
445
446     /*
447      * copy index, if provided
448      */
449     if(hdr) {
450         netsnmp_assert(ctx->index.oids == NULL);
451         if(snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids,
452                            hdr->len * sizeof(oid) )) {
453             return -1;
454         }
455         ctx->index.len = hdr->len;
456     }
457
458     /**
459      * Create variable to hold each component of the index
460      */
461     @if $ext_index != 0@
462        /** TODO: add code for external index(s)! */
463     @end@
464     @foreach $idx index@
465        memset( &var_$idx, 0x00, sizeof(var_$idx) );
466        var_${idx}.type = $idx.type;
467        /** TODO: link this index to the next, or NULL for the last one */
468 #ifdef TABLE_CONTAINER_TODO
469     snmp_log(LOG_ERR, "${i}_extract_index index list not implemented!\n" );
470     return 0;
471 #else
472        var_$idx.next_variable = &var_XX;
473 #endif
474
475     @end@
476
477     /*
478      * parse the oid into the individual components
479      */
480     err = parse_oid_indexes( hdr->oids, hdr->len, &var_$first_idx );
481     if (err == SNMP_ERR_NOERROR) {
482        /*
483         * copy components into the context structure
484         */
485        @foreach $idx index@
486           @eval $found = "external"@
487           @foreach $c column@
488               @if "$idx" eq "$c"@
489                  @eval $found = "internal"@
490               @end@
491           @end@
492           @if "$found" eq "external"@
493               /** skipping external index $idx */
494           @end@
495           @if "$found" eq "internal"@
496              @eval $have_type = 0@
497              @if "$idx.type" eq "ASN_OCTET_STR"@
498              @eval $have_type = 1@
499                 if(var_$idx.val_len > sizeof(ctx->$idx))
500                    err = -1;
501                 else
502                     memcpy( ctx->$idx, var_$idx.val.string, var_$idx.val_len );
503                 ctx->${idx}_len = var_$idx.val_len;
504              @end@
505              @if "$idx.type" eq "ASN_OBJECT_ID"@
506              @eval $have_type = 1@
507                 memcpy( ctx->$idx, var_$idx.val.string, var_$idx.val_len );
508                 ctx->$idx_len = var_$idx.val_len;
509              @end@
510              @if $have_type == 0@
511                 ctx->$idx = *var_$idx.val.integer;
512              @end@
513           @end@
514    
515        @end@
516    
517        @foreach $c index@
518            /*
519             * TODO: check index for valid values. For EXAMPLE:
520             *
521           @eval $have_check = 0@
522           @if "$c.type" eq "ASN_IPADDRESS"@
523               @eval $have_check = 1@
524               * if ( XXX_check_ip( *var_$c.val.integer ) ) {
525           @end@
526           @if "$c.type" eq "ASN_OBJECT_ID"@
527               @eval $have_check = 1@
528               * if ( XXX_check_oid( var_$c.val.objid, var_$c.val_len /
529                                     sizeof(oid) ) ) {
530           @end@
531           @if "$c.type" eq "ASN_OCTET_STR"@
532               @eval $have_check = 1@
533               * if ( XXX_check_value( var_$c.val.string, XXX ) ) {
534           @end@
535           @if $have_check != 1@
536               * if ( *var_$c.val.integer != XXX ) {
537           @end@
538           *    err = -1;
539           * }
540           */
541        @end@
542     }
543
544     /*
545      * parsing may have allocated memory. free it.
546      */
547     snmp_reset_var_buffers( &var_$first_idx );
548
549     return err;
550 }
551
552 @if "$rs_name" ne ""@
553 /************************************************************
554  * the *_can_activate routine is called
555  * when a row is changed to determine if all the values
556  * set are consistent with the row's rules for a row status
557  * of ACTIVE.
558  *
559  * return 1 if the row could be ACTIVE
560  * return 0 if the row is not ready for the ACTIVE state
561  */
562 int ${i}_can_activate(${i}_context *undo_ctx,
563                       ${i}_context *row_ctx,
564                       netsnmp_request_group * rg)
565 {
566     /*
567      * TODO: check for activation requirements here
568      */
569     return 1;
570 }
571
572 /************************************************************
573  * the *_can_deactivate routine is called when a row that is
574  * currently ACTIVE is set to a state other than ACTIVE. If
575  * there are conditions in which a row should not be allowed
576  * to transition out of the ACTIVE state (such as the row being
577  * referred to by another row or table), check for them here.
578  *
579  * return 1 if the row can be set to a non-ACTIVE state
580  * return 0 if the row must remain in the ACTIVE state
581  */
582 int ${i}_can_deactivate(${i}_context *undo_ctx,
583                         ${i}_context *row_ctx,
584                         netsnmp_request_group * rg)
585 {
586     /*
587      * TODO: check for deactivation requirements here
588      */
589     return 1;
590 }
591
592 @end@
593 /************************************************************
594  * the *_can_delete routine is called to determine if a row
595  * can be deleted.
596  *
597  * return 1 if the row can be deleted
598  * return 0 if the row cannot be deleted
599  */
600 int ${i}_can_delete(${i}_context *undo_ctx,
601                     ${i}_context *row_ctx,
602                     netsnmp_request_group * rg)
603 {
604 @if "$rs_name" ne ""@
605     /*
606      * probably shouldn't delete a row that we can't
607      * deactivate.
608      */
609     if(${i}_can_deactivate(undo_ctx,row_ctx,rg) != 1)
610         return 0;
611 @end@
612     
613     /*
614      * TODO: check for other deletion requirements here
615      */
616     return 1;
617 }
618
619 #ifdef ${i}_ROW_CREATION
620 /************************************************************
621  * the *_create_row routine is called by the table handler
622  * to create a new row for a given index. If you need more
623  * information (such as column values) to make a decision
624  * on creating rows, you must create an initial row here
625  * (to hold the column values), and you can examine the
626  * situation in more detail in the *_set_reserve1 or later
627  * states of set processing. Simple check for a NULL undo_ctx
628  * in those states and do detailed creation checking there.
629  *
630  * returns a newly allocated ${i}_context
631  *   structure if the specified indexes are not illegal
632  * returns NULL for errors or illegal index values.
633  */
634 ${i}_context *
635 ${i}_create_row( netsnmp_index* hdr)
636 {
637     ${i}_context * ctx =
638         SNMP_MALLOC_TYPEDEF(${i}_context);
639     if(!ctx)
640         return NULL;
641         
642     /*
643      * TODO: check indexes, if necessary.
644      */
645     if(${i}_extract_index( ctx, hdr )) {
646         free(ctx->index.oids);
647         free(ctx);
648         return NULL;
649     }
650
651     /* netsnmp_mutex_init(ctx->lock);
652        netsnmp_mutex_lock(ctx->lock); */
653
654     /*
655      * TODO: initialize any default values here. This is also
656      * first place you really should allocate any memory for
657      * yourself to use.  If you allocated memory earlier,
658      * make sure you free it for earlier error cases!
659      */
660     /**
661      @foreach $c column@
662      @if $c.settable@
663      ctx->$c = 0;
664      @end@
665      @end@
666     */
667
668     return ctx;
669 }
670 #endif
671
672 /************************************************************
673  * the *_duplicate row routine
674  */
675 ${i}_context *
676 ${i}_duplicate_row( ${i}_context * row_ctx)
677 {
678     ${i}_context * dup;
679
680     if(!row_ctx)
681         return NULL;
682
683     dup = SNMP_MALLOC_TYPEDEF(${i}_context);
684     if(!dup)
685         return NULL;
686         
687     if(${i}_row_copy(dup,row_ctx)) {
688         free(dup);
689         dup = NULL;
690     }
691
692     return dup;
693 }
694
695 /************************************************************
696  * the *_delete_row method is called to delete a row.
697  */
698 netsnmp_index * ${i}_delete_row( ${i}_context * ctx )
699 {
700   /* netsnmp_mutex_destroy(ctx->lock); */
701
702     if(ctx->index.oids)
703         free(ctx->index.oids);
704
705     /*
706      * TODO: release any memory you allocated here...
707      */
708
709     /*
710      * release header
711      */
712     free( ctx );
713
714     return NULL;
715 }
716
717
718 /************************************************************
719  * RESERVE is used to check the syntax of all the variables
720  * provided, that the values being set are sensible and consistent,
721  * and to allocate any resources required for performing the SET.
722  * After this stage, the expectation is that the set ought to
723  * succeed, though this is not guaranteed. (In fact, with the UCD
724  * agent, this is done in two passes - RESERVE1, and
725  * RESERVE2, to allow for dependancies between variables).
726  *
727  * BEFORE calling this routine, the agent will call duplicate_row
728  * to create a copy of the row (unless this is a new row; i.e.
729  * row_created == 1).
730  *
731  * next state -> SET_RESERVE2 || SET_FREE
732  */
733 void ${i}_set_reserve1( netsnmp_request_group *rg )
734 {
735     ${i}_context *row_ctx =
736             (${i}_context *)rg->existing_row;
737     ${i}_context *undo_ctx =
738             (${i}_context *)rg->undo_info;
739     netsnmp_variable_list *var;
740     netsnmp_request_group_item *current;
741     int rc;
742
743     @if "$st_name" ne ""@
744         /*
745          * Block all attempts to modify a readOnly row
746          */
747     if( row_ctx && (row_ctx->$st_name == SNMP_STORAGE_READONLY) ) {
748         netsnmp_set_mode_request_error(MODE_SET_BEGIN, rg->list->ri,
749                                        SNMP_ERR_NOTWRITABLE);
750         return;
751     }
752     @end@
753
754     /*
755      * TODO: loop through columns, check syntax and lengths. For
756      * columns which have no dependencies, you could also move
757      * the value/range checking here to attempt to catch error
758      * cases as early as possible.
759      */
760     for( current = rg->list; current; current = current->next ) {
761
762         var = current->ri->requestvb;
763         rc = SNMP_ERR_NOERROR;
764
765         switch(current->tri->colnum) {
766
767         @foreach $c column@
768         @if $c.settable@
769         case COLUMN_$c.uc:
770             /** $c.syntax = $c.type */
771             rc = netsnmp_check_vb_type_and_size(var, $c.type,
772                                                 sizeof(row_ctx->$c));
773         break;
774
775         @end@
776         @end@
777         default: /** We shouldn't get here */
778             rc = SNMP_ERR_GENERR;
779             snmp_log(LOG_ERR, "unknown column in "
780                      "${i}_set_reserve1\n");
781         }
782
783         if (rc)
784            netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc );
785         rg->status = SNMP_MAX( rg->status, current->ri->status );
786     }
787
788     /*
789      * done with all the columns. Could check row related
790      * requirements here.
791      */
792 }
793
794 void ${i}_set_reserve2( netsnmp_request_group *rg )
795 {
796     ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
797     ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
798     netsnmp_request_group_item *current;
799     netsnmp_variable_list *var;
800     int rc;
801
802     rg->rg_void = rg->list->ri;
803
804     /*
805      * TODO: loop through columns, check for valid
806      * values and any range constraints.
807      */
808     for( current = rg->list; current; current = current->next ) {
809
810         var = current->ri->requestvb;
811         rc = SNMP_ERR_NOERROR;
812
813         switch(current->tri->colnum) {
814
815         @foreach $c column@
816         @if $c.settable@
817         case COLUMN_$c.uc:
818             /** $c.syntax = $c.type */
819             @eval $have_check = 0@
820             @if "$c" eq "$st_name"@
821                 @eval $have_check = 1@
822                 rc = netsnmp_check_vb_storagetype(current->ri->requestvb,
823                                                   undo_ctx ?
824                                                   undo_ctx->$c:0);
825             @end@
826             @if "$c" eq "$rs_name"@
827                 @eval $have_check = 1@
828                 rc = netsnmp_check_vb_rowstatus(current->ri->requestvb,
829                                                 undo_ctx ?
830                                                 undo_ctx->$c:0);
831                 rg->rg_void = current->ri;
832             @end@
833             @if "$c.syntax" eq "TruthValue"@
834                 @eval $have_check = 1@
835                 rc = netsnmp_check_vb_truthvalue(current->ri->requestvb);
836             @end@
837             @if $have_check == 0@
838                     /*
839                      * TODO: routine to check valid values
840                      *
841                      * EXAMPLE:
842                      *
843                 @if "$c.type" eq "ASN_IPADDRESS"@
844                     @eval $have_check = 1@
845                     * if ( XXX_check_ip( *var->val.integer ) ) {
846                 @end@
847                 @if "$c.type" eq "ASN_OBJECT_ID"@
848                     @eval $have_check = 1@
849                     * if ( XXX_check_oid( var ) ) {
850                 @end@
851                 @if "$c.type" eq "ASN_OCTET_STR"@
852                     @eval $have_check = 1@
853                     * if ( XXX_check_value( var->val.string, XXX ) ) {
854                 @end@
855                 @if $have_check != 1@
856                     * if ( *var->val.integer != XXX ) {
857                 @end@
858                 *    rc = SNMP_ERR_INCONSISTENTVALUE;
859                 *    rc = SNMP_ERR_BADVALUE;
860                 * }
861                 */
862             @end@
863         break;
864
865         @end@
866         @end@
867         default: /** We shouldn't get here */
868             netsnmp_assert(0); /** why wasn't this caught in reserve1? */
869         }
870
871         if (rc)
872            netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc);
873     }
874
875     /*
876      * done with all the columns. Could check row related
877      * requirements here.
878      */
879 }
880
881 /************************************************************
882  * Assuming that the RESERVE phases were successful, the next
883  * stage is indicated by the action value ACTION. This is used
884  * to actually implement the set operation. However, this must
885  * either be done into temporary (persistent) storage, or the
886  * previous value stored similarly, in case any of the subsequent
887  * ACTION calls fail.
888  *
889  * In your case, changes should be made to row_ctx. A copy of
890  * the original row is in undo_ctx.
891  */
892 void ${i}_set_action( netsnmp_request_group *rg )
893 {
894     netsnmp_variable_list *var;
895     ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
896     ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
897     netsnmp_request_group_item *current;
898
899     @if "$rs_name" ne ""@
900     int            row_err = 0;
901     @end@
902
903     /*
904      * TODO: loop through columns, copy varbind values
905      * to context structure for the row.
906      */
907     for( current = rg->list; current; current = current->next ) {
908
909         var = current->ri->requestvb;
910
911         switch(current->tri->colnum) {
912
913         @foreach $c column@
914         @if $c.settable@
915         case COLUMN_$c.uc:
916             /** $c.syntax = $c.type */
917             @eval $have_type = 0@
918             @if "$c.type" eq "ASN_OCTET_STR"@
919             @eval $have_type = 1@
920             memcpy(row_ctx->$c,var->val.string,var->val_len);
921             row_ctx->${c}_len = var->val_len;
922             @end@
923             @if "$c.type" eq "ASN_OBJECT_ID"@
924             @eval $have_type = 1@
925             memcpy(row_ctx->$c,var->val.objid,var->val_len);
926             row_ctx->${c}_len = var->val_len;
927             @end@
928             @if $have_type == 0@
929             row_ctx->$c = *var->val.integer;
930             @end@
931         break;
932
933         @end@
934         @end@
935         default: /** We shouldn't get here */
936             netsnmp_assert(0); /** why wasn't this caught in reserve1? */
937         }
938     }
939
940     /*
941      * done with all the columns. Could check row related
942      * requirements here.
943      */
944     @if "$rs_name" ne ""@
945 #ifndef ${i}_CAN_MODIFY_ACTIVE_ROW
946     if( undo_ctx && RS_IS_ACTIVE(undo_ctx->$rs_name) &&
947         row_ctx && RS_IS_ACTIVE(row_ctx->$rs_name) ) {
948             row_err = 1;
949     }
950 #endif
951
952     /*
953      * check activation/deactivation
954      */
955     row_err = netsnmp_table_array_check_row_status(&cb, rg,
956                                   row_ctx ? &row_ctx->$rs_name : NULL,
957                                   undo_ctx ? &undo_ctx->$rs_name : NULL);
958     if(row_err) {
959         netsnmp_set_mode_request_error(MODE_SET_BEGIN,
960                                        (netsnmp_request_info*)rg->rg_void,
961                                        row_err);
962         return;
963     }
964
965     @end@
966     /*
967      * TODO: if you have dependencies on other tables, this would be
968      * a good place to check those, too.
969      */
970 }
971
972 /************************************************************
973  * Only once the ACTION phase has completed successfully, can
974  * the final COMMIT phase be run. This is used to complete any
975  * writes that were done into temporary storage, and then release
976  * any allocated resources. Note that all the code in this phase
977  * should be "safe" code that cannot possibly fail (cue
978  * hysterical laughter). The whole intent of the ACTION/COMMIT
979  * division is that all of the fallible code should be done in
980  * the ACTION phase, so that it can be backed out if necessary.
981  *
982  * BEFORE calling this routine, the agent will update the
983  * container (inserting a row if row_created == 1, or removing
984  * the row if row_deleted == 1).
985  *
986  * AFTER calling this routine, the agent will delete the
987  * undo_info.
988  */
989 void ${i}_set_commit( netsnmp_request_group *rg )
990 {
991     netsnmp_variable_list *var;
992     ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
993     ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
994     netsnmp_request_group_item *current;
995
996     /*
997      * loop through columns
998      */
999     for( current = rg->list; current; current = current->next ) {
1000
1001         var = current->ri->requestvb;
1002
1003         switch(current->tri->colnum) {
1004
1005         @foreach $c column@
1006         @if $c.settable@
1007         case COLUMN_$c.uc:
1008             /** $c.syntax = $c.type */
1009         break;
1010
1011         @end@
1012         @end@
1013         default: /** We shouldn't get here */
1014             netsnmp_assert(0); /** why wasn't this caught in reserve1? */
1015         }
1016     }
1017
1018     /*
1019      * done with all the columns. Could check row related
1020      * requirements here.
1021      */
1022 }
1023
1024 /************************************************************
1025  * If either of the RESERVE calls fail, the write routines
1026  * are called again with the FREE action, to release any resources
1027  * that have been allocated. The agent will then return a failure
1028  * response to the requesting application.
1029  *
1030  * AFTER calling this routine, the agent will delete undo_info.
1031  */
1032 void ${i}_set_free( netsnmp_request_group *rg )
1033 {
1034     netsnmp_variable_list *var;
1035     ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
1036     ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
1037     netsnmp_request_group_item *current;
1038
1039     /*
1040      * loop through columns
1041      */
1042     for( current = rg->list; current; current = current->next ) {
1043
1044         var = current->ri->requestvb;
1045
1046         switch(current->tri->colnum) {
1047
1048         @foreach $c column@
1049         @if $c.settable@
1050         case COLUMN_$c.uc:
1051             /** $c.syntax = $c.type */
1052         break;
1053
1054         @end@
1055         @end@
1056         default: /** We shouldn't get here */
1057             /** should have been logged in reserve1 */
1058         }
1059     }
1060
1061     /*
1062      * done with all the columns. Could check row related
1063      * requirements here.
1064      */
1065 }
1066
1067 /************************************************************
1068  * If the ACTION phase does fail (for example due to an apparently
1069  * valid, but unacceptable value, or an unforeseen problem), then
1070  * the list of write routines are called again, with the UNDO
1071  * action. This requires the routine to reset the value that was
1072  * changed to its previous value (assuming it was actually changed),
1073  * and then to release any resources that had been allocated. As
1074  * with the FREE phase, the agent will then return an indication
1075  * of the error to the requesting application.
1076  *
1077  * BEFORE calling this routine, the agent will update the container
1078  * (remove any newly inserted row, re-insert any removed row).
1079  *
1080  * AFTER calling this routing, the agent will call row_copy
1081  * to restore the data in existing_row from the date in undo_info.
1082  * Then undo_info will be deleted (or existing row, if row_created
1083  * == 1).
1084  */
1085 void ${i}_set_undo( netsnmp_request_group *rg )
1086 {
1087     netsnmp_variable_list *var;
1088     ${i}_context *row_ctx = (${i}_context *)rg->existing_row;
1089     ${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
1090     netsnmp_request_group_item *current;
1091
1092     /*
1093      * loop through columns
1094      */
1095     for( current = rg->list; current; current = current->next ) {
1096
1097         var = current->ri->requestvb;
1098
1099         switch(current->tri->colnum) {
1100
1101         @foreach $c column@
1102         @if $c.settable@
1103         case COLUMN_$c.uc:
1104             /** $c.syntax = $c.type */
1105         break;
1106
1107         @end@
1108         @end@
1109         default: /** We shouldn't get here */
1110             netsnmp_assert(0); /** why wasn't this caught in reserve1? */
1111         }
1112     }
1113
1114     /*
1115      * done with all the columns. Could check row related
1116      * requirements here.
1117      */
1118 }
1119
1120 #endif /** ${i}_SET_HANDLING */
1121
1122
1123 /************************************************************
1124  *
1125  * Initialize the $i table by defining its contents and how it's structured
1126  */
1127 void
1128 initialize_table_$i(void)
1129 {
1130     netsnmp_table_registration_info *table_info;
1131
1132     if(my_handler) {
1133         snmp_log(LOG_ERR, "initialize_table_${i}_handler called again\n");
1134         return;
1135     }
1136
1137     memset(&cb, 0x00, sizeof(cb));
1138
1139     /** create the table structure itself */
1140     table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
1141
1142     /* if your table is read only, it's easiest to change the
1143        HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */
1144     my_handler = netsnmp_create_handler_registration("$i",
1145                                              netsnmp_table_array_helper_handler,
1146                                              ${i}_oid,
1147                                              ${i}_oid_len,
1148                                              HANDLER_CAN_RWRITE);
1149             
1150     if (!my_handler || !table_info) {
1151         snmp_log(LOG_ERR, "malloc failed in "
1152                  "initialize_table_${i}_handler\n");
1153         return; /** mallocs failed */
1154     }
1155
1156     /***************************************************
1157      * Setting up the table's definition
1158      */
1159     /*
1160      * TODO: add any external indexes here.
1161      */
1162     @if $ext_index != 0@
1163         /** TODO: add code for external index(s)! */
1164     @end@
1165
1166     /*
1167      * internal indexes
1168      */
1169     @foreach $idx index@
1170         /** index: $idx */
1171         netsnmp_table_helper_add_index(table_info, $idx.type);
1172     @end@
1173
1174     table_info->min_column = ${i}_COL_MIN;
1175     table_info->max_column = ${i}_COL_MAX;
1176
1177     /***************************************************
1178      * registering the table with the master agent
1179      */
1180     cb.get_value = ${i}_get_value;
1181     cb.container = netsnmp_container_find("${i}_primary:"
1182                                           "${i}:"
1183                                           "table_container");
1184 #ifdef ${i}_IDX2
1185     netsnmp_container_add_index(cb.container,
1186                                 netsnmp_container_find("${i}_secondary:"
1187                                                        "${i}:"
1188                                                        "table_container"));
1189     cb.container->next->compare = ${i}_cmp;
1190 #endif
1191 #ifdef ${i}_SET_HANDLING
1192     cb.can_set = 1;
1193 #ifdef ${i}_ROW_CREATION
1194     cb.create_row = (UserRowMethod*)${i}_create_row;
1195 #endif
1196     cb.duplicate_row = (UserRowMethod*)${i}_duplicate_row;
1197     cb.delete_row = (UserRowMethod*)${i}_delete_row;
1198     cb.row_copy = (Netsnmp_User_Row_Operation *)${i}_row_copy;
1199
1200 @if "$rs_name" ne ""@
1201     cb.can_activate = (Netsnmp_User_Row_Action *)${i}_can_activate;
1202     cb.can_deactivate = (Netsnmp_User_Row_Action *)${i}_can_deactivate;
1203 @end@
1204     cb.can_delete = (Netsnmp_User_Row_Action *)${i}_can_delete;
1205
1206     cb.set_reserve1 = ${i}_set_reserve1;
1207     cb.set_reserve2 = ${i}_set_reserve2;
1208     cb.set_action = ${i}_set_action;
1209     cb.set_commit = ${i}_set_commit;
1210     cb.set_free = ${i}_set_free;
1211     cb.set_undo = ${i}_set_undo;
1212 #endif
1213     DEBUGMSGTL(("initialize_table_$i",
1214                 "Registering table $i "
1215                 "as a table array\n"));
1216     netsnmp_table_container_register(my_handler, table_info, &cb,
1217                                      cb.container, 1);
1218 }
1219
1220 /************************************************************
1221  * ${i}_get_value
1222  */
1223 int ${i}_get_value(
1224             netsnmp_request_info *request,
1225             netsnmp_index *item,
1226             netsnmp_table_request_info *table_info )
1227 {
1228     netsnmp_variable_list *var = request->requestvb;
1229     ${i}_context *context = (${i}_context *)item;
1230
1231     switch(table_info->colnum) {
1232
1233         @foreach $c column@
1234             @eval $have_type = 0@
1235         case COLUMN_$c.uc:
1236             /** $c.syntax = $c.type */
1237             @if "$c.type" eq "ASN_OBJECT_ID"@
1238                 @eval $have_type = 1@
1239             snmp_set_var_typed_value(var, $c.type,
1240                          (char*)&context->$c,
1241                          context->${c}_len );
1242             @end@
1243             @if "$c.type" eq "ASN_OCTET_STR"@
1244                 @eval $have_type = 1@
1245             snmp_set_var_typed_value(var, $c.type,
1246                          (char*)&context->$c,
1247                          context->${c}_len );
1248             @end@
1249             @if $have_type == 0@
1250             snmp_set_var_typed_value(var, $c.type,
1251                          (char*)&context->$c,
1252                          sizeof(context->$c) );
1253             @end@
1254         break;
1255     
1256         @end@
1257     default: /** We shouldn't get here */
1258         snmp_log(LOG_ERR, "unknown column in "
1259                  "${i}_get_value\n");
1260         return SNMP_ERR_GENERR;
1261     }
1262     return SNMP_ERR_NOERROR;
1263 }
1264
1265 /************************************************************
1266  * ${i}_get_by_idx
1267  */
1268 const ${i}_context *
1269 ${i}_get_by_idx(netsnmp_index * hdr)
1270 {
1271     return (const ${i}_context *)
1272         CONTAINER_FIND(cb.container, hdr );
1273 }
1274
1275
1276 @end@
1277 @end@
1278 @end@