and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table_dataset.c
1 #ifdef BRCM_SNMP_SUPPORT 
2
3 #include <net-snmp/net-snmp-config.h>
4
5 #if HAVE_STRING_H
6 #include <string.h>
7 #else
8 #include <strings.h>
9 #endif
10
11 #include <net-snmp/net-snmp-includes.h>
12 #include <net-snmp/agent/net-snmp-agent-includes.h>
13
14 #if HAVE_DMALLOC_H
15 #include <dmalloc.h>
16 #endif
17
18 static netsnmp_data_list *auto_tables;
19
20 typedef struct data_set_tables_s {
21     netsnmp_table_data_set *table_set;
22 } data_set_tables;
23
24 typedef struct data_set_cache_s {
25     void           *data;
26     size_t          data_len;
27 } data_set_cache;
28
29 #define STATE_ACTION   1
30 #define STATE_COMMIT   2
31 #define STATE_UNDO     3
32 #define STATE_FREE     4
33
34 typedef struct newrow_stash_s {
35     netsnmp_table_row *newrow;
36     int             state;
37     int             created;
38     int             deleted;
39 } newrow_stash;
40
41 /** @defgroup table_dataset table_dataset: Helps you implement a table with automatted storage.
42  *  @ingroup table_data
43  *
44  *  This handler helps you implement a table where all the data is
45  *  expected to be stored within the agent itself and not in some
46  *  external storage location.  It handles all MIB requests including
47  *  GETs, GETNEXTs and SETs.  It's possible to simply create a table
48  *  without actually ever defining a handler to be called when SNMP
49  *  requests come in.  To use the data, you can either attach a
50  *  sub-handler that merely uses/manipulates the data further when
51  *  requests come in, or you can loop through it externally when it's
52  *  actually needed.  This handler is most useful in cases where a
53  *  table is holding configuration data for something which gets
54  *  triggered via another event.
55  *
56  *  NOTE NOTE NOTE: This helper isn't complete and is likely to change
57  *  somewhat over time.  Specifically, the way it stores data
58  *  internally may change drastically.
59  *  
60  *  @{
61  */
62
63 /** Create a netsnmp_table_data_set structure given a table_data definition */
64 netsnmp_table_data_set *
65 netsnmp_create_table_data_set(const char *table_name)
66 {
67     netsnmp_table_data_set *table_set =
68         SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
69     if (!table_set)
70         return NULL;
71     table_set->table = netsnmp_create_table_data(table_name);
72     return table_set;
73 }
74
75 /** Given a netsnmp_table_data_set definition, create a handler for it */
76 netsnmp_mib_handler *
77 netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
78 {
79     netsnmp_mib_handler *ret = NULL;
80
81     if (!data_set) {
82         snmp_log(LOG_INFO,
83                  "netsnmp_get_table_data_set_handler(NULL) called\n");
84         return NULL;
85     }
86
87     ret =
88         netsnmp_create_handler(TABLE_DATA_SET_NAME,
89                                netsnmp_table_data_set_helper_handler);
90     if (ret) {
91         ret->myvoid = (void *) data_set;
92     }
93     return ret;
94 }
95
96
97 /** register a given data_set at a given oid (specified in the
98     netsnmp_handler_registration pointer).  The
99     reginfo->handler->access_method *may* be null if the call doesn't
100     ever want to be called for SNMP operations.
101 */
102 int
103 netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
104                                 netsnmp_table_data_set *data_set,
105                                 netsnmp_table_registration_info
106                                 *table_info)
107 {
108     if (NULL == table_info) {
109         /*
110          * allocate the table if one wasn't allocated 
111          */
112         table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
113     }
114
115     if (NULL == table_info->indexes && data_set->table->indexes_template) {
116         /*
117          * copy the indexes in 
118          */
119         table_info->indexes =
120             snmp_clone_varbind(data_set->table->indexes_template);
121     }
122
123     if ((!table_info->min_column || !table_info->max_column) &&
124         (data_set->default_row)) {
125         /*
126          * determine min/max columns 
127          */
128         unsigned int    mincol = 0xffffffff, maxcol = 0;
129         netsnmp_table_data_set_storage *row;
130
131         for (row = data_set->default_row; row; row = row->next) {
132             mincol = SNMP_MIN(mincol, row->column);
133             maxcol = SNMP_MAX(maxcol, row->column);
134         }
135         if (!table_info->min_column)
136             table_info->min_column = mincol;
137         if (!table_info->max_column)
138             table_info->max_column = maxcol;
139     }
140
141     netsnmp_inject_handler(reginfo,
142                            netsnmp_get_table_data_set_handler(data_set));
143     return netsnmp_register_table_data(reginfo, data_set->table,
144                                        table_info);
145 }
146
147 /** Finds a column within a given storage set, given the pointer to
148    the start of the storage set list.
149 */
150 netsnmp_table_data_set_storage *
151 netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
152                                    unsigned int column)
153 {
154     while (start && start->column != column)
155         start = start->next;
156     return start;
157 }
158
159 /**
160  * extracts a netsnmp_table_data_set pointer from a given request
161  */
162 netsnmp_table_data_set_storage *
163 netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
164                                      unsigned int column)
165 {
166     netsnmp_table_data_set_storage *data =
167         netsnmp_extract_table_row_data( request );
168     if (data) {
169         data = netsnmp_table_data_set_find_column(data, column);
170     }
171     return data;
172 }
173 /**
174  * extracts a netsnmp_table_data_set pointer from a given request
175  */
176 NETSNMP_INLINE netsnmp_table_data_set *
177 netsnmp_extract_table_data_set(netsnmp_request_info *request)
178 {
179     return (netsnmp_table_data_set *)
180         netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
181 }
182
183 /**
184  * marks a given column in a row as writable or not.
185  */
186 int
187 netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
188                                  int writable)
189 {
190     netsnmp_table_data_set_storage *data;
191
192     if (!row)
193         return SNMPERR_GENERR;
194
195     data = (netsnmp_table_data_set_storage *) row->data;
196     data = netsnmp_table_data_set_find_column(data, column);
197
198     if (!data) {
199         /*
200          * create it 
201          */
202         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
203         if (!data) {
204             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
205             return SNMPERR_MALLOC;
206         }
207         data->column = column;
208         data->writable = writable;
209         data->next = row->data;
210         row->data = data;
211     } else {
212         data->writable = writable;
213     }
214     return SNMPERR_SUCCESS;
215 }
216
217
218 /**
219  * sets a given column in a row with data given a type, value, and
220  * length.  Data is memdup'ed by the function.
221  */
222 int
223 netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
224                        int type, const char *value, size_t value_len)
225 {
226     netsnmp_table_data_set_storage *data;
227
228     if (!row)
229         return SNMPERR_GENERR;
230
231     data = (netsnmp_table_data_set_storage *) row->data;
232     data = netsnmp_table_data_set_find_column(data, column);
233
234     if (!data) {
235         /*
236          * create it 
237          */
238         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
239         if (!data) {
240             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
241             return SNMPERR_MALLOC;
242         }
243
244         data->column = column;
245         data->type = type;
246         data->next = row->data;
247         row->data = data;
248     }
249
250     if (value) {
251         if (data->type != type)
252             return SNMPERR_GENERR;
253
254         SNMP_FREE(data->data.voidp);
255         if (value_len) {
256             if (memdup(&data->data.string, value, (value_len)) !=
257                 SNMPERR_SUCCESS) {
258                 snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
259                 return SNMPERR_MALLOC;
260             }
261         } else {
262             data->data.string = malloc(1);
263         }
264         data->data_len = value_len;
265     }
266     return SNMPERR_SUCCESS;
267 }
268
269 /** adds a new default row to a table_set.
270  * Arguments should be the table_set, column number, variable type and
271  * finally a 1 if it is allowed to be writable, or a 0 if not.  If the
272  * default_value field is not NULL, it will be used to populate new
273  * valuse in that column fro newly created rows. It is copied into the
274  * storage template (free your calling argument).
275  *
276  * returns SNMPERR_SUCCESS or SNMPERR_FAILURE
277  */
278 int
279 netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
280                                   unsigned int column,
281                                   int type, int writable,
282                                   void *default_value,
283                                   size_t default_value_len)
284 {
285
286     netsnmp_table_data_set_storage *new_col, *ptr;
287
288     if (!table_set)
289         return SNMPERR_GENERR;
290
291     /*
292      * double check 
293      */
294     new_col =
295         netsnmp_table_data_set_find_column(table_set->default_row, column);
296     if (new_col != NULL) {
297         if (new_col->type == type && new_col->writable == writable)
298             return SNMPERR_SUCCESS;
299         return SNMPERR_GENERR;
300     }
301
302     new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
303     new_col->type = type;
304     new_col->writable = writable;
305     new_col->column = column;
306     if (default_value) {
307         memdup((u_char **) & (new_col->data.voidp),
308                (u_char *) default_value, default_value_len);
309         new_col->data_len = default_value_len;
310     }
311     if (table_set->default_row == NULL)
312         table_set->default_row = new_col;
313     else {
314         for (ptr = table_set->default_row; ptr->next; ptr = ptr->next) {
315         }
316         ptr->next = new_col;
317     }
318     return SNMPERR_SUCCESS;
319 }
320
321 /** clones a dataset row, including all data. */
322 netsnmp_table_row *
323 netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
324 {
325     netsnmp_table_data_set_storage *data, **newrowdata;
326     netsnmp_table_row *newrow = netsnmp_table_data_clone_row(row);
327
328     if (!row || !newrow)
329         return NULL;
330
331     data = (netsnmp_table_data_set_storage *) row->data;
332
333     if (data) {
334         for (newrowdata =
335              (netsnmp_table_data_set_storage **) &(newrow->data); data;
336              newrowdata = &((*newrowdata)->next), data = data->next) {
337
338             memdup((u_char **) newrowdata, (u_char *) data,
339                    sizeof(netsnmp_table_data_set_storage));
340             if (!*newrowdata)
341                 return NULL;
342
343             if (data->data.voidp) {
344                 memdup((u_char **) & ((*newrowdata)->data.voidp),
345                        (u_char *) data->data.voidp, data->data_len);
346                 if (!(*newrowdata)->data.voidp)
347                     return NULL;
348             }
349         }
350     }
351     return newrow;
352 }
353
354 /** creates a new row from an existing defined default set */
355 netsnmp_table_row *
356 netsnmp_table_data_set_create_row_from_defaults
357     (netsnmp_table_data_set_storage *defrow)
358 {
359     netsnmp_table_row *row;
360     row = netsnmp_create_table_data_row();
361     if (!row)
362         return NULL;
363     for (; defrow; defrow = defrow->next) {
364         netsnmp_set_row_column(row, defrow->column, defrow->type,
365                                defrow->data.voidp, defrow->data_len);
366         if (defrow->writable)
367             netsnmp_mark_row_column_writable(row, defrow->column, 1);
368
369     }
370     return row;
371 }
372
373
374 newrow_stash   *
375 netsnmp_table_data_set_create_newrowstash
376     (netsnmp_table_data_set     *datatable,
377      netsnmp_table_request_info *table_info)
378 {
379     newrow_stash   *newrowstash = NULL;
380     netsnmp_table_row *newrow   = NULL;
381
382     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
383     newrowstash->created = 1;
384     newrow = netsnmp_table_data_set_create_row_from_defaults
385                         (datatable->default_row);
386     newrow->indexes = snmp_clone_varbind(table_info->indexes);
387     newrowstash->newrow = newrow;
388
389     return newrowstash;
390 }
391
392 /** implements the table data helper.  This is the routine that takes
393  *  care of all SNMP requests coming into the table. */
394 int
395 netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
396                                       netsnmp_handler_registration
397                                       *reginfo,
398                                       netsnmp_agent_request_info *reqinfo,
399                                       netsnmp_request_info *requests)
400 {
401
402     netsnmp_table_data_set_storage *data = NULL;
403     newrow_stash   *newrowstash = NULL;
404     netsnmp_table_row *row, *newrow = NULL;
405     netsnmp_table_request_info *table_info;
406     netsnmp_request_info *request;
407     oid            *suffix;
408     size_t          suffix_len;
409     netsnmp_oid_stash_node **stashp = NULL;
410
411     if (!handler)
412         return SNMPERR_GENERR;
413         
414     DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n"));
415     for (request = requests; request; request = request->next) {
416         netsnmp_table_data_set *datatable =
417             (netsnmp_table_data_set *) handler->myvoid;
418         if (request->processed)
419             continue;
420
421         /*
422          * extract our stored data and table info 
423          */
424         row = netsnmp_extract_table_row(request);
425         table_info = netsnmp_extract_table_info(request);
426         suffix = requests->requestvb->name + reginfo->rootoid_len + 2;
427         suffix_len = requests->requestvb->name_length -
428             (reginfo->rootoid_len + 2);
429
430         if (MODE_IS_SET(reqinfo->mode)) {
431             /*
432              * use a cached copy of the row for modification 
433              */
434
435             /*
436              * cache location: may have been created already by other
437              * SET requests in the same master request. 
438              */
439             stashp = (netsnmp_oid_stash_node **)
440                 netsnmp_table_get_or_create_row_stash(reqinfo,
441                                                       "dataset_row_stash");
442
443             newrowstash
444                 = netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
445
446             if (!newrowstash) {
447                 if (!row) {
448                     if (datatable->allow_creation) {
449                         /*
450                          * entirely new row.  Create the row from the template 
451                          */
452                         newrowstash =
453                              netsnmp_table_data_set_create_newrowstash(
454                                                  datatable, table_info);
455                         newrow = newrowstash->newrow;
456                     } else if (datatable->rowstatus_column == 0) {
457                         /*
458                          * A RowStatus object may be used to control the
459                          *  creation of a new row.  But if this object
460                          *  isn't declared (and the table isn't marked as
461                          *  'auto-create'), then we can't create a new row.
462                          */
463                         netsnmp_set_request_error(reqinfo, request,
464                                                   SNMP_ERR_NOCREATION);
465                         continue;
466                     }
467                 } else {
468                     /*
469                      * existing row that needs to be modified 
470                      */
471                     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
472                     newrow = netsnmp_table_data_set_clone_row(row);
473                     newrowstash->newrow = newrow;
474                 }
475                 netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
476                                            newrowstash);
477             } else {
478                 newrow = newrowstash->newrow;
479             }
480             /*
481              * all future SET data modification operations use this
482              * temp pointer 
483              */
484             if (reqinfo->mode == MODE_SET_RESERVE1 ||
485                 reqinfo->mode == MODE_SET_RESERVE2)
486                 row = newrow;
487         }
488
489         if (row)
490             data = (netsnmp_table_data_set_storage *) row->data;
491
492         if (!row || !table_info || !data) {
493             if (!MODE_IS_SET(reqinfo->mode)) {
494                 netsnmp_set_request_error(reqinfo, request,
495                                           SNMP_NOSUCHINSTANCE);
496                 continue;
497             }
498         }
499
500         data =
501             netsnmp_table_data_set_find_column(data, table_info->colnum);
502
503         switch (reqinfo->mode) {
504         case MODE_GET:
505         case MODE_GETNEXT:
506         case MODE_GETBULK:     /* XXXWWW */
507             if (data && data->data.voidp)
508                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
509                                                 row,
510                                                 table_info->colnum,
511                                                 data->type,
512                                                 data->data.voidp,
513                                                 data->data_len);
514             break;
515
516         case MODE_SET_RESERVE1:
517             if (data) {
518                 /*
519                  * Can we modify the existing row?
520                  */
521                 if (!data->writable) {
522                     netsnmp_set_request_error(reqinfo, request,
523                                               SNMP_ERR_NOTWRITABLE);
524                 } else if (request->requestvb->type != data->type) {
525                     netsnmp_set_request_error(reqinfo, request,
526                                               SNMP_ERR_WRONGTYPE);
527                 }
528             } else if (datatable->rowstatus_column == table_info->colnum) {
529                 /*
530                  * Otherwise, this is where we create a new row using
531                  * the RowStatus object (essentially duplicating the
532                  * steps followed earlier in the 'allow_creation' case)
533                  */
534                 switch (*(request->requestvb->val.integer)) {
535                 case RS_CREATEANDGO:
536                 case RS_CREATEANDWAIT:
537                     newrowstash =
538                              netsnmp_table_data_set_create_newrowstash(
539                                                  datatable, table_info);
540                     newrow = newrowstash->newrow;
541                     row    = newrow;
542                     netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
543                                                newrowstash);
544                 }
545             }
546             break;
547
548         case MODE_SET_RESERVE2:
549             /*
550              * If the agent receives a SET request for an object in a non-existant
551              *  row, then the RESERVE1 pass will create the row automatically.
552              *
553              * But since the row doesn't exist at that point, the test for whether
554              *  the object is writable or not will be skipped.  So we need to check
555              *  for this possibility again here.
556              *
557              * Similarly, if row creation is under the control of the RowStatus
558              *  object (i.e. allow_creation == 0), but this particular request
559              *  doesn't include such an object, then the row won't have been created,
560              *  and the writable check will also have been skipped.  Again - check here.
561              */
562             if (data && data->writable == 0) {
563                 netsnmp_set_request_error(reqinfo, request,
564                                           SNMP_ERR_NOTWRITABLE);
565                 continue;
566             }
567             if (datatable->rowstatus_column == table_info->colnum) {
568                 switch (*(request->requestvb->val.integer)) {
569                 case RS_ACTIVE:
570                 case RS_NOTINSERVICE:
571                     /*
572                      * Can only operate on pre-existing rows.
573                      */
574                     if (!newrowstash || newrowstash->created) {
575                         netsnmp_set_request_error(reqinfo, request,
576                                                   SNMP_ERR_INCONSISTENTVALUE);
577                         continue;
578                     }
579                     break;
580
581                 case RS_CREATEANDGO:
582                 case RS_CREATEANDWAIT:
583                     /*
584                      * Can only operate on newly created rows.
585                      */
586                     if (!(newrowstash && newrowstash->created)) {
587                         netsnmp_set_request_error(reqinfo, request,
588                                                   SNMP_ERR_INCONSISTENTVALUE);
589                         continue;
590                     }
591                     break;
592
593                 case RS_DESTROY:
594                     /*
595                      * Can operate on new or pre-existing rows.
596                      */
597                     break;
598
599                 case RS_NOTREADY:
600                 default:
601                     /*
602                      * Not a valid value to Set 
603                      */
604                     netsnmp_set_request_error(reqinfo, request,
605                                               SNMP_ERR_WRONGVALUE);
606                     continue;
607                 }
608             }
609             if (!data ) {
610                 netsnmp_set_request_error(reqinfo, request,
611                                           SNMP_ERR_NOCREATION);
612                 continue;
613             }
614
615             /*
616              * modify row and set new value 
617              */
618             SNMP_FREE(data->data.string);
619             data->data.string =
620                 netsnmp_strdup_and_null(request->requestvb->val.string,
621                                         request->requestvb->val_len);
622             if (!data->data.string) {
623                 netsnmp_set_request_error(reqinfo, requests,
624                                           SNMP_ERR_RESOURCEUNAVAILABLE);
625             }
626             data->data_len = request->requestvb->val_len;
627
628             if (datatable->rowstatus_column == table_info->colnum) {
629                 switch (*(request->requestvb->val.integer)) {
630                 case RS_CREATEANDGO:
631                     /*
632                      * XXX: check legality 
633                      */
634                     *(data->data.integer) = RS_ACTIVE;
635                     break;
636
637                 case RS_CREATEANDWAIT:
638                     /*
639                      * XXX: check legality 
640                      */
641                     *(data->data.integer) = RS_NOTINSERVICE;
642                     break;
643
644                 case RS_DESTROY:
645                     newrowstash->deleted = 1;
646                     break;
647                 }
648             }
649             break;
650
651         case MODE_SET_ACTION:
652
653             /*
654              * Install the new row into the stored table.
655              * Do this only *once* per row ....
656              */
657             if (newrowstash->state != STATE_ACTION) {
658                 newrowstash->state = STATE_ACTION;
659                 if (newrowstash->created) {
660                     netsnmp_table_dataset_add_row(datatable, newrow);
661                 } else {
662                     netsnmp_table_dataset_replace_row(datatable,
663                                                       row, newrow);
664                 }
665             }
666             /*
667              * ... but every (relevant) varbind in the request will
668              * need to know about this new row, so update the
669              * per-request row information regardless
670              */
671             if (newrowstash->created) {
672                 netsnmp_request_add_list_data(request,
673                         netsnmp_create_data_list(TABLE_DATA_NAME,
674                                                  newrow, NULL));
675             }
676             break;
677
678         case MODE_SET_UNDO:
679             /*
680              * extract the new row, replace with the old or delete 
681              */
682             if (newrowstash->state != STATE_UNDO) {
683                 newrowstash->state = STATE_UNDO;
684                 if (newrowstash->created) {
685                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
686                 } else {
687                     netsnmp_table_dataset_replace_row(datatable,
688                                                       newrow, row);
689                     netsnmp_table_dataset_delete_row(newrow);
690                 }
691             }
692             break;
693
694         case MODE_SET_COMMIT:
695             if (newrowstash->state != STATE_COMMIT) {
696                 newrowstash->state = STATE_COMMIT;
697                 if (!newrowstash->created) {
698                     netsnmp_table_dataset_delete_row(row);
699                 }
700                 if (newrowstash->deleted) {
701                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
702                 }
703             }
704             break;
705
706         case MODE_SET_FREE:
707             if (newrowstash && newrowstash->state != STATE_FREE) {
708                 newrowstash->state = STATE_FREE;
709                 netsnmp_table_dataset_delete_row(newrow);
710             }
711             break;
712         }
713     }
714
715     if (handler->next && handler->next->access_method)
716         netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
717     return SNMP_ERR_NOERROR;
718 }
719
720 /** registers a table_dataset so that the "add_row" snmpd.conf token
721   * can be used to add data to this table.  If registration_name is
722   * NULL then the name used when the table was created will be used
723   * instead.
724   *
725   * @todo create a properly free'ing registeration pointer for the
726   * datalist, and get the datalist freed at shutdown.
727   */
728 void
729 netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
730                                  char *registration_name)
731 {
732     data_set_tables *tables;
733     tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
734     if (!tables)
735         return;
736     tables->table_set = table_set;
737     if (!registration_name) {
738         registration_name = table_set->table->name;
739     }
740     netsnmp_add_list_data(&auto_tables, netsnmp_create_data_list(registration_name, tables, NULL));     /* XXX */
741 }
742
743 /** @internal */
744 void
745 netsnmp_config_parse_table_set(const char *token, char *line)
746 {
747     oid             name[MAX_OID_LEN], table_name[MAX_OID_LEN];
748     size_t          name_length = MAX_OID_LEN, table_name_length =
749         MAX_OID_LEN;
750     struct tree    *tp, *indexnode;
751     netsnmp_table_data_set *table_set;
752     struct index_list *index;
753     unsigned int    mincol = 0xffffff, maxcol = 0;
754     u_char          type;
755
756     /*
757      * instatiate a fake table based on MIB information 
758      */
759     if (!snmp_parse_oid(line, table_name, &table_name_length) ||
760         (NULL == (tp = get_tree(table_name, table_name_length,
761                                 get_tree_head())))) {
762         config_pwarn
763             ("can't instatiate table %s since I can't find mib information about it\n");
764         return;
765     }
766
767     if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
768         config_pwarn
769             ("can't instatiate table since it doesn't appear to be a proper table\n");
770         return;
771     }
772
773     table_set = netsnmp_create_table_data_set(line);
774
775     /*
776      * loop through indexes and add types 
777      */
778     for (index = tp->indexes; index; index = index->next) {
779         if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
780             (NULL ==
781              (indexnode = get_tree(name, name_length, get_tree_head())))) {
782             config_pwarn
783                 ("can't instatiate table %s since I don't know anything about one index\n");
784             return;             /* xxx mem leak */
785         }
786
787         type = mib_to_asn_type(indexnode->type);
788         if (type == (u_char) - 1) {
789             config_pwarn("unknown index type");
790             return;             /* xxx mem leak */
791         }
792         if (index->isimplied)   /* if implied, mark it as such */
793             type |= ASN_PRIVATE;
794
795         DEBUGMSGTL(("table_set_add_row",
796                     "adding default index of type %d\n", type));
797         netsnmp_table_dataset_add_index(table_set, type);
798     }
799
800     /*
801      * loop through children and add each column info 
802      */
803     for (tp = tp->child_list; tp; tp = tp->next_peer) {
804         int             canwrite = 0;
805         type = mib_to_asn_type(tp->type);
806         if (type == (u_char) - 1) {
807             config_pwarn("unknown column type");
808             return;             /* xxx mem leak */
809         }
810
811         DEBUGMSGTL(("table_set_add_row", "adding column %d of type %d\n",
812                     tp->subid, type));
813
814         switch (tp->access) {
815         case MIB_ACCESS_CREATE:
816             table_set->allow_creation = 1;
817         case MIB_ACCESS_READWRITE:
818         case MIB_ACCESS_WRITEONLY:
819             canwrite = 1;
820         case MIB_ACCESS_READONLY:
821             DEBUGMSGTL(("table_set_add_row",
822                         "adding column %d of type %d\n", tp->subid, type));
823             netsnmp_table_set_add_default_row(table_set, tp->subid, type,
824                                               canwrite, NULL, 0);
825             mincol = SNMP_MIN(mincol, tp->subid);
826             maxcol = SNMP_MAX(maxcol, tp->subid);
827             break;
828
829         case MIB_ACCESS_NOACCESS:
830         case MIB_ACCESS_NOTIFY:
831             break;
832
833         default:
834             config_pwarn("unknown column access type");
835             break;
836         }
837     }
838
839     /*
840      * register the table 
841      */
842     netsnmp_register_table_data_set(netsnmp_create_handler_registration
843                                     (line, NULL, table_name,
844                                      table_name_length,
845                                      HANDLER_CAN_RWRITE), table_set, NULL);
846
847     netsnmp_register_auto_data_table(table_set, NULL);
848 }
849
850 /** @internal */
851 void
852 netsnmp_config_parse_add_row(const char *token, char *line)
853 {
854     char            buf[SNMP_MAXBUF_MEDIUM];
855     char            tname[SNMP_MAXBUF_MEDIUM];
856     size_t          buf_size;
857
858     data_set_tables *tables;
859     netsnmp_variable_list *vb;  /* containing only types */
860     netsnmp_table_row *row;
861     netsnmp_table_data_set_storage *dr;
862
863     line = copy_nword(line, tname, SNMP_MAXBUF_MEDIUM);
864
865     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
866     if (!tables) {
867         config_pwarn("Unknown table trying to add a row");
868         return;
869     }
870
871     /*
872      * do the indexes first 
873      */
874     row = netsnmp_create_table_data_row();
875
876     for (vb = tables->table_set->table->indexes_template; vb;
877          vb = vb->next_variable) {
878         if (!line) {
879             config_pwarn("missing an index value");
880             return;
881         }
882
883         DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n",
884                     vb->type));
885         buf_size = SNMP_MAXBUF_MEDIUM;
886         line = read_config_read_memory(vb->type, line, buf, &buf_size);
887         netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
888     }
889
890     /*
891      * then do the data 
892      */
893     for (dr = tables->table_set->default_row; dr; dr = dr->next) {
894         if (!line) {
895             config_pwarn("missing an data value\n");
896             return;
897         }
898
899         buf_size = SNMP_MAXBUF_MEDIUM;
900         line = read_config_read_memory(dr->type, line, buf, &buf_size);
901         DEBUGMSGTL(("table_set_add_row",
902                     "adding data at column %d of type %d\n", dr->column,
903                     dr->type));
904         netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
905         if (dr->writable)
906             netsnmp_mark_row_column_writable(row, dr->column, 1);       /* make writable */
907     }
908     netsnmp_table_data_add_row(tables->table_set->table, row);
909 }
910
911 /** adds an index to the table.  Call this repeatly for each index. */
912 NETSNMP_INLINE void
913 netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
914 {
915     if (!table)
916         return;
917     netsnmp_table_data_add_index(table->table, type);
918 }
919
920 /** adds a new row to a dataset table */
921 NETSNMP_INLINE void
922 netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
923                               netsnmp_table_row *row)
924 {
925     if (!table)
926         return;
927     netsnmp_table_data_add_row(table->table, row);
928 }
929
930 /** adds a new row to a dataset table */
931 NETSNMP_INLINE void
932 netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
933                                   netsnmp_table_row *origrow,
934                                   netsnmp_table_row *newrow)
935 {
936     if (!table)
937         return;
938     netsnmp_table_data_replace_row(table->table, origrow, newrow);
939 }
940
941 /** deletes a single dataset table data.
942  *  returns the (possibly still good) next pointer of the deleted data object.
943  */
944 NETSNMP_INLINE netsnmp_table_data_set_storage *
945 netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
946 {
947     netsnmp_table_data_set_storage *nextPtr = NULL;
948     if (data) {
949         nextPtr = data->next;
950         SNMP_FREE(data->data.voidp);
951     }
952     SNMP_FREE(data);
953     return nextPtr;
954 }
955
956 /** deletes all the data from this node and beyond in the linked list */
957 NETSNMP_INLINE void
958 netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
959 {
960
961     while (data) {
962         data = netsnmp_table_dataset_delete_data(data);
963     }
964 }
965
966 /** deletes all the data from this node and beyond in the linked list */
967 NETSNMP_INLINE void
968 netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
969 {
970     netsnmp_table_data_set_storage *data;
971
972     if (!row)
973         return;
974
975     data = netsnmp_table_data_delete_row(row);
976     netsnmp_table_dataset_delete_all_data(data);
977 }
978
979 /** removes a row from the table, but doesn't delete/free anything */
980 NETSNMP_INLINE void
981 netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
982                                  netsnmp_table_row *row)
983 {
984     if (!table)
985         return;
986
987     netsnmp_table_data_remove_and_delete_row(table->table, row);
988 }
989
990 /** removes a row from the table and then deletes it (and all it's data) */
991 NETSNMP_INLINE void
992 netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
993                                             netsnmp_table_row *row)
994 {
995     netsnmp_table_data_set_storage *data;
996
997     if (!table)
998         return;
999
1000     data = (netsnmp_table_data_set_storage *)
1001         netsnmp_table_data_remove_and_delete_row(table->table, row);
1002
1003     netsnmp_table_dataset_delete_all_data(data);
1004 }
1005
1006 /** adds multiple data column definitions to each row.  Functionally,
1007  *  this is a wrapper around calling netsnmp_table_set_add_default_row
1008  *  repeatedly for you.
1009  */
1010 void
1011 #if HAVE_STDARG_H
1012 netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
1013 #else
1014 netsnmp_table_set_multi_add_default_row(va_dcl
1015     )
1016      va_dcl
1017 #endif
1018 {
1019     va_list         debugargs;
1020     unsigned int    column;
1021     int             type, writable;
1022     void           *data;
1023     size_t          data_len;
1024
1025 #if HAVE_STDARG_H
1026     va_start(debugargs, tset);
1027 #else
1028     netsnmp_table_data_set *tset;
1029
1030     va_start(debugargs);
1031     tset = va_arg(debugargs, netsnmp_table_data_set *);
1032 #endif
1033
1034     while ((column = va_arg(debugargs, unsigned int)) != 0) {
1035         type = va_arg(debugargs, int);
1036         writable = va_arg(debugargs, int);
1037         data = va_arg(debugargs, void *);
1038         data_len = va_arg(debugargs, size_t);
1039         netsnmp_table_set_add_default_row(tset, column, type, writable,
1040                                           data, data_len);
1041     }
1042
1043     va_end(debugargs);
1044 }
1045
1046 /** adds multiple indexes to a table_dataset helper object.
1047  *  To end the list, use a 0 after the list of ASN index types. */
1048 void
1049 #if HAVE_STDARG_H
1050 netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
1051                               ...)
1052 #else
1053 netsnmp_table_set_add_indexes(va_alist)
1054      va_dcl
1055 #endif
1056 {
1057     va_list         debugargs;
1058     int             type;
1059
1060 #if HAVE_STDARG_H
1061     va_start(debugargs, tset);
1062 #else
1063     netsnmp_table_data_set *tset;
1064
1065     va_start(debugargs);
1066     tset = va_arg(debugargs, netsnmp_table_data_set *);
1067 #endif
1068
1069     while ((type = va_arg(debugargs, int)) != 0) {
1070         netsnmp_table_dataset_add_index(tset, type);
1071     }
1072
1073     va_end(debugargs);
1074 }
1075
1076 /*
1077  * @} 
1078  */
1079 #endif /* BRCM_SNMP_SUPPORT */