# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table.c
1 /*
2  * table.c 
3  */
4
5 #include <net-snmp/net-snmp-config.h>
6
7 #if HAVE_STRING_H
8 #include <string.h>
9 #else
10 #include <strings.h>
11 #endif
12
13
14 #include <net-snmp/net-snmp-includes.h>
15 #include <net-snmp/agent/net-snmp-agent-includes.h>
16
17 #include <net-snmp/agent/table.h>
18 #include <net-snmp/library/snmp_assert.h>
19
20 #if HAVE_DMALLOC_H
21 #include <dmalloc.h>
22 #endif
23
24 static void     table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
25                                      netsnmp_request_info *request,
26                                      int status);
27 static void     table_data_free_func(void *data);
28
29 /** @defgroup table table: Helps you implement a table.
30  *  @ingroup handler
31  *
32  *  This handler helps you implement a table by doing some of the
33  *  processing for you.
34  *  
35  *  This handler truly shows the power of the new handler mechanism.
36  *  By creating a table handler and injecting it into your calling
37  *  chain, or by using the netsnmp_register_table() function to register your
38  *  table, you get access to some pre-parsed information.
39  *  Specifically, the table handler pulls out the column number and
40  *  indexes from the request oid so that you don't have to do the
41  *  complex work to do that parsing within your own code.
42  *
43  *  To do this, the table handler needs to know up front how your
44  *  table is structured.  To inform it about this, you fill in a
45  *  table_registeration_info structure that is passed to the table
46  *  handler.  It contains the asn index types for the table as well as
47  *  the minimum and maximum column that should be used.
48  *  
49  *  @{
50  */
51
52 /** Given a netsnmp_table_registration_info object, creates a table handler.
53  *  You can use this table handler by injecting it into a calling
54  *  chain.  When the handler gets called, it'll do processing and
55  *  store it's information into the request->parent_data structure.
56  */
57 netsnmp_mib_handler *
58 netsnmp_get_table_handler(netsnmp_table_registration_info *tabreq)
59 {
60     netsnmp_mib_handler *ret = NULL;
61
62     if (!tabreq) {
63         snmp_log(LOG_INFO, "netsnmp_get_table_handler(NULL) called\n");
64         return NULL;
65     }
66
67     ret = netsnmp_create_handler(TABLE_HANDLER_NAME, table_helper_handler);
68     if (ret) {
69         ret->myvoid = (void *) tabreq;
70         tabreq->number_indexes = count_varbinds(tabreq->indexes);
71     }
72     return ret;
73 }
74
75
76 /** creates a table handler given the netsnmp_table_registration_info object,
77  *  inserts it into the request chain and then calls
78  *  netsnmp_register_handler() to register the table into the agent.
79  */
80 int
81 netsnmp_register_table(netsnmp_handler_registration *reginfo,
82                        netsnmp_table_registration_info *tabreq)
83 {
84     netsnmp_inject_handler(reginfo, netsnmp_get_table_handler(tabreq));
85     return netsnmp_register_handler(reginfo);
86 }
87
88 /** extracts the processed table information from a given request.
89  *  call this from subhandlers on a request to extract the processed
90  *  netsnmp_request_info information.  The resulting information includes the
91  *  index values and the column number.
92  */
93 NETSNMP_INLINE netsnmp_table_request_info *
94 netsnmp_extract_table_info(netsnmp_request_info *request)
95 {
96     return (netsnmp_table_request_info *)
97         netsnmp_request_get_list_data(request, TABLE_HANDLER_NAME);
98 }
99
100 /** extracts the registered netsnmp_table_registration_info object from a
101  *  netsnmp_handler_registration object */
102 netsnmp_table_registration_info *
103 netsnmp_find_table_registration_info(netsnmp_handler_registration *reginfo)
104 {
105     return (netsnmp_table_registration_info *)
106         netsnmp_find_handler_data_by_name(reginfo, TABLE_HANDLER_NAME);
107 }
108
109 /** implements the table helper handler */
110 int
111 table_helper_handler(netsnmp_mib_handler *handler,
112                      netsnmp_handler_registration *reginfo,
113                      netsnmp_agent_request_info *reqinfo,
114                      netsnmp_request_info *requests)
115 {
116
117     netsnmp_request_info *request;
118     netsnmp_table_registration_info *tbl_info;
119     int             oid_index_pos;
120     unsigned int    oid_column_pos;
121     unsigned int    tmp_idx, tmp_len;
122     int             incomplete, out_of_range, cleaned_up = 0;
123     int             status = SNMP_ERR_NOERROR, need_processing = 0;
124     oid            *tmp_name;
125     netsnmp_table_request_info *tbl_req_info;
126     netsnmp_variable_list *vb;
127
128     if (!reginfo || !handler)
129         return SNMPERR_GENERR;
130
131     oid_index_pos  = reginfo->rootoid_len + 2;
132     oid_column_pos = reginfo->rootoid_len + 1;
133     tbl_info = (netsnmp_table_registration_info *) handler->myvoid;
134
135     if ((!handler->myvoid) || (!tbl_info->indexes)) {
136         snmp_log(LOG_INFO, "improperly registered table found\n");
137
138         /*
139          * XXX-rks: unregister table? 
140          */
141         return SNMP_ERR_GENERR;
142     }
143
144     DEBUGMSGTL(("helper:table", "Got request for handler %s: base oid:",
145                 handler->handler_name));
146     DEBUGMSGOID(("helper:table", reginfo->rootoid, reginfo->rootoid_len));
147     DEBUGMSG(("helper:table", "\n"));
148     
149     /*
150      * if the agent request info has a state reference, then this is a 
151      * later pass of a set request and we can skip all the lookup stuff.
152      *
153      * xxx-rks: this might break for handlers which only handle one varbind
154      * at a time... those handlers should not save data by their handler_name
155      * in the netsnmp_agent_request_info. 
156      */
157     if (netsnmp_agent_get_list_data(reqinfo, handler->next->handler_name)) {
158         if (MODE_IS_SET(reqinfo->mode)) {
159             return netsnmp_call_next_handler(handler, reginfo, reqinfo,
160                                              requests);
161         } else {
162 /** XXX-rks: memory leak. add cleanup handler? */
163             netsnmp_free_agent_data_sets(reqinfo);
164         }
165     }
166
167     if ( MODE_IS_SET(reqinfo->mode) &&
168          (reqinfo->mode != MODE_SET_RESERVE1)) {
169         /*
170          * for later set modes, we can skip all the index parsing,
171          * and we always need to let child handlers have a chance
172          * to clean up.
173          */
174         need_processing = 1;
175     }
176     else {
177         /*
178          * for RESERVE1 and GETS, only continue if we have at least
179          * one valid request.
180          */
181            
182     /*
183      * loop through requests
184      */
185
186     for (request = requests; request; request = request->next) {
187         netsnmp_variable_list *var = request->requestvb;
188
189         DEBUGMSGOID(("verbose:table", var->name, var->name_length));
190         DEBUGMSG(("verbose:table", "\n"));
191
192         if (request->processed) {
193             DEBUGMSG(("helper:table", "already processed\n"));
194             continue;
195         }
196         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
197
198         /*
199          * this should probably be handled further up 
200          */
201         if ((reqinfo->mode == MODE_GET) && (var->type != ASN_NULL)) {
202             /*
203              * valid request if ASN_NULL 
204              */
205             DEBUGMSGTL(("helper:table",
206                         "  GET var type is not ASN_NULL\n"));
207             netsnmp_set_request_error(reqinfo, request,
208                                       SNMP_ERR_WRONGTYPE);
209             continue;
210         }
211
212         if (reqinfo->mode == MODE_SET_RESERVE1) {
213             DEBUGIF("helper:table") {
214                 u_char         *buf = NULL;
215                 size_t          buf_len = 0, out_len = 0;
216                 DEBUGMSGTL(("helper:table", " SET_REQUEST for OID: "));
217                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
218                 out_len = 0;
219                 if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
220                                            var, 0, 0, 0)) {
221                     DEBUGMSG(("helper:table"," type=%d(%02x), value=%s\n",
222                               var->type, var->type, buf));
223                 } else {
224                     if (buf != NULL) {
225                         DEBUGMSG(("helper:table",
226                                   " type=%d(%02x), value=%s [TRUNCATED]\n",
227                                   var->type, var->type, buf));
228                     } else {
229                         DEBUGMSG(("helper:table",
230                                   " type=%d(%02x), value=[NIL] [TRUNCATED]\n",
231                                   var->type, var->type));
232                     }
233                 }
234                 if (buf != NULL) {
235                     free(buf);
236                 }
237             }
238         }
239
240         /*
241          * check to make sure its in table range 
242          */
243
244         out_of_range = 0;
245         /*
246          * if our root oid is > var->name and this is not a GETNEXT, 
247          * then the oid is out of range. (only compare up to shorter 
248          * length) 
249          */
250         if (reginfo->rootoid_len > var->name_length)
251             tmp_len = var->name_length;
252         else
253             tmp_len = reginfo->rootoid_len;
254         if (snmp_oid_compare(reginfo->rootoid, reginfo->rootoid_len,
255                              var->name, tmp_len) > 0) {
256             if (reqinfo->mode == MODE_GETNEXT) {
257                 if (var->name != var->name_loc)
258                     free(var->name);
259                 snmp_set_var_objid(var, reginfo->rootoid,
260                                    reginfo->rootoid_len);
261             } else {
262                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
263                 out_of_range = 1;
264             }
265         }
266         /*
267          * if var->name is longer than the root, make sure it is 
268          * table.1 (table.ENTRY).  
269          */
270         else if ((var->name_length > reginfo->rootoid_len) &&
271                  (var->name[reginfo->rootoid_len] != 1)) {
272             if ((var->name[reginfo->rootoid_len] < 1) &&
273                 (reqinfo->mode == MODE_GETNEXT)) {
274                 var->name[reginfo->rootoid_len] = 1;
275                 var->name_length = reginfo->rootoid_len;
276             } else {
277                 out_of_range = 1;
278                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
279             }
280         }
281         /*
282          * if it is not in range, then mark it in the request list 
283          * because we can't process it. If the request is not a GETNEXT 
284          * then set the error to NOSUCHOBJECT so nobody else wastes time
285          * trying to process it.  
286          */
287         if (out_of_range) {
288             DEBUGMSGTL(("helper:table", "  Not processed: "));
289             DEBUGMSGOID(("helper:table", var->name, var->name_length));
290             DEBUGMSG(("helper:table", "\n"));
291
292             /*
293              *  Reject requests of the form 'myTable.N'   (N != 1)
294              */
295             if (reqinfo->mode != MODE_GETNEXT) {
296                 table_helper_cleanup(reqinfo, request,
297                                      SNMP_NOSUCHOBJECT);
298             }
299             continue;
300         }
301
302
303         /*
304          * Check column ranges; set-up to pull out indexes from OID. 
305          */
306
307         incomplete = 0;
308         tbl_req_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_request_info);
309         tbl_req_info->reg_info = tbl_info;
310         tbl_req_info->indexes = snmp_clone_varbind(tbl_info->indexes);
311         tbl_req_info->number_indexes = 0;       /* none yet */
312         netsnmp_request_add_list_data(request,
313                                       netsnmp_create_data_list
314                                       (TABLE_HANDLER_NAME,
315                                        (void *) tbl_req_info,
316                                        table_data_free_func));
317
318         if (var->name_length > oid_column_pos) {
319             /*
320              * oid is long enough to contain index info
321              */
322             if (var->name[oid_column_pos] < tbl_info->min_column) {
323                 if (reqinfo->mode == MODE_GETNEXT) {
324                     /*
325                      * fix column, truncate useless index info 
326                      */
327                     var->name_length = oid_column_pos;
328                     tbl_req_info->colnum = tbl_info->min_column;
329                 } else
330                     out_of_range = 1;
331             } else if (var->name[oid_column_pos] > tbl_info->max_column)
332                 out_of_range = 1;
333
334             if (out_of_range) {
335                 /*
336                  * this is out of range...  remove from requests, free
337                  * memory 
338                  */
339                 DEBUGMSGTL(("helper:table",
340                             "  oid is out of range. Not processed: "));
341                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
342                 DEBUGMSG(("helper:table", "\n"));
343
344                 /*
345                  *  Reject requests of the form 'myEntry.N'   (invalid N)
346                  */
347                 if (reqinfo->mode != MODE_GETNEXT) {
348                     table_helper_cleanup(reqinfo, request,
349                                          SNMP_NOSUCHOBJECT);
350                 }
351                 continue;
352             }
353             /*
354              * use column verification 
355              */
356             else if (tbl_info->valid_columns) {
357                 tbl_req_info->colnum =
358                     netsnmp_closest_column(var->name[oid_column_pos],
359                                            tbl_info->valid_columns);
360                 if (tbl_req_info->colnum == 0)
361                     continue;
362                 if (tbl_req_info->colnum != var->name[oid_column_pos]) {
363                     /*
364                      * different column! truncate useless index info 
365                      */
366                     var->name_length = oid_column_pos;
367                 }
368             }
369             /*
370              * var->name_length may have changed - check again 
371              */
372             if (var->name_length <= oid_column_pos) { /** none available */
373                 tbl_req_info->index_oid_len = 0;
374             } else {
375                 tbl_req_info->colnum = var->name[oid_column_pos];
376                 tbl_req_info->index_oid_len =
377                     var->name_length - oid_index_pos;
378                 netsnmp_assert(tbl_req_info->index_oid_len < MAX_OID_LEN);
379                 memcpy(tbl_req_info->index_oid, &var->name[oid_index_pos],
380                        tbl_req_info->index_oid_len * sizeof(oid));
381                 tmp_name = tbl_req_info->index_oid;
382             }
383         } else if (reqinfo->mode != MODE_GETNEXT) {
384             /*
385              * oid is NOT long enough to contain index info, and this is
386              * NOT a GETNEXT, so we can't do anything with it.
387              *
388              * Reject requests of the form 'myTable' or 'myEntry'
389              */
390             table_helper_cleanup(reqinfo, request, SNMP_NOSUCHOBJECT);
391             continue;
392         } else {
393             /*
394              * oid is NOT long enough to contain column or index info, so start
395              * at the minimum column. Set index oid len to 0 because we don't
396              * have any index info in the OID.
397              */
398             tbl_req_info->index_oid_len = 0;
399             tbl_req_info->colnum = tbl_info->min_column;
400         }
401
402         /*
403          * set up tmp_len to be the number of OIDs we have beyond the column;
404          * these should be the index(s) for the table. If the index_oid_len
405          * is 0, set tmp_len to -1 so that when we try to parse the index below,
406          * we just zero fill everything.
407          */
408         if (tbl_req_info->index_oid_len == 0) {
409             incomplete = 1;
410             tmp_len = -1;
411         } else
412             tmp_len = tbl_req_info->index_oid_len;
413
414
415         /*
416          * for each index type, try to extract the index from var->name
417          */
418
419         for (tmp_idx = 0, vb = tbl_req_info->indexes;
420              tmp_idx < tbl_info->number_indexes;
421              ++tmp_idx, vb = vb->next_variable) {
422             if (incomplete && tmp_len) {
423                 /*
424                  * incomplete/illegal OID, set up dummy 0 to parse 
425                  */
426                 DEBUGMSGTL(("helper:table",
427                             "  oid indexes not complete: "));
428                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
429                 DEBUGMSG(("helper:table", "\n"));
430
431                 /*
432                  * no sense in trying anymore if this is a GET/SET. 
433                  *
434                  * Reject requests of the form 'myObject'   (no instance)
435                  */
436                 if (reqinfo->mode != MODE_GETNEXT) {
437                     table_helper_cleanup(reqinfo, requests,
438                                          SNMP_NOSUCHINSTANCE);
439                     cleaned_up = 1;
440                 }
441                 tmp_len = 0;
442                 tmp_name = (oid *) & tmp_len;
443                 break;
444             }
445             /*
446              * try and parse current index 
447              */
448             if (parse_one_oid_index(&tmp_name, &tmp_len,
449                                     vb, 1) != SNMPERR_SUCCESS) {
450                 incomplete = 1;
451                 tmp_len = -1;   /* is this necessary? Better safe than
452                                  * sorry */
453             } else {
454                 /*
455                  * do not count incomplete indexes 
456                  */
457                 if (incomplete)
458                     continue;
459                 ++tbl_req_info->number_indexes; /** got one ok */
460                 if (tmp_len <= 0) {
461                     incomplete = 1;
462                     tmp_len = -1;       /* is this necessary? Better safe
463                                          * than sorry */
464                 }
465             }
466         }                       /** for loop */
467
468         DEBUGIF("helper:table") {
469             if (!cleaned_up) {
470                 unsigned int    count;
471                 u_char         *buf = NULL;
472                 size_t          buf_len = 0, out_len = 0;
473                 DEBUGMSGTL(("helper:table", "  column: %d, indexes: %d",
474                             tbl_req_info->colnum,
475                             tbl_req_info->number_indexes));
476                 for (vb = tbl_req_info->indexes, count = 0;
477                      vb && count < tbl_info->number_indexes;
478                      count++, vb = vb->next_variable) {
479                     out_len = 0;
480                     if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
481                                                vb, 0, 0, 0)) {
482                         DEBUGMSG(("helper:table",
483                                   "   index: type=%d(%02x), value=%s",
484                                   vb->type, vb->type, buf));
485                     } else {
486                         if (buf != NULL) {
487                             DEBUGMSG(("helper:table",
488                                       "   index: type=%d(%02x), value=%s [TRUNCATED]",
489                                       vb->type, vb->type, buf));
490                         } else {
491                             DEBUGMSG(("helper:table",
492                                       "   index: type=%d(%02x), value=[NIL] [TRUNCATED]",
493                                       vb->type, vb->type));
494                         }
495                     }
496                 }
497                 if (buf != NULL) {
498                     free(buf);
499                 }
500                 DEBUGMSG(("helper:table", "\n"));
501             }
502         }
503
504
505         /*
506          * do we have sufficent index info to continue?
507          */
508
509         if ((reqinfo->mode != MODE_GETNEXT) &&
510             ((tbl_req_info->number_indexes != tbl_info->number_indexes) ||
511              (tmp_len != -1))) {
512             table_helper_cleanup(reqinfo, request, SNMP_NOSUCHINSTANCE);
513             continue;
514         }
515         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
516         
517         ++need_processing;
518
519     }                           /* for each request */
520     }
521
522     /*
523      * * call our child access function 
524      */
525     if (need_processing)
526         status =
527             netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
528
529     return status;
530 }
531
532 /** Builds the result to be returned to the agent given the table information.
533  *  Use this function to return results from lowel level handlers to
534  *  the agent.  It takes care of building the proper resulting oid
535  *  (containing proper indexing) and inserts the result value into the
536  *  returning varbind.
537  */
538 int
539 netsnmp_table_build_result(netsnmp_handler_registration *reginfo,
540                            netsnmp_request_info *reqinfo,
541                            netsnmp_table_request_info *table_info,
542                            u_char type, u_char * result, size_t result_len)
543 {
544
545     netsnmp_variable_list *var;
546
547     if (!reqinfo || !table_info)
548         return SNMPERR_GENERR;
549
550     var = reqinfo->requestvb;
551
552     if (var->name != var->name_loc)
553         free(var->name);
554     var->name = NULL;
555
556     if (netsnmp_table_build_oid(reginfo, reqinfo, table_info) !=
557         SNMPERR_SUCCESS)
558         return SNMPERR_GENERR;
559
560     snmp_set_var_typed_value(var, type, result, result_len);
561
562     return SNMPERR_SUCCESS;
563 }
564
565
566 /** given a registration info object, a request object and the table
567  *  info object it builds the request->requestvb->name oid from the
568  *  index values and column information found in the table_info
569  *  object.
570  */
571 int
572 netsnmp_table_build_oid(netsnmp_handler_registration *reginfo,
573                         netsnmp_request_info *reqinfo,
574                         netsnmp_table_request_info *table_info)
575 {
576     oid             tmpoid[MAX_OID_LEN];
577     netsnmp_variable_list *var;
578
579     if (!reginfo || !reqinfo || !table_info)
580         return SNMPERR_GENERR;
581
582     memcpy(tmpoid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
583     tmpoid[reginfo->rootoid_len] = 1;   /** .Entry */
584     tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; /** .column */
585
586     var = reqinfo->requestvb;
587     if (build_oid(&var->name, &var->name_length,
588                   tmpoid, reginfo->rootoid_len + 2, table_info->indexes)
589         != SNMPERR_SUCCESS)
590         return SNMPERR_GENERR;
591
592     return SNMPERR_SUCCESS;
593 }
594
595 /** Builds an oid from index information.
596  */
597 int
598 netsnmp_table_build_oid_from_index(netsnmp_handler_registration *reginfo,
599                                    netsnmp_request_info *reqinfo,
600                                    netsnmp_table_request_info *table_info)
601 {
602     oid             tmpoid[MAX_OID_LEN];
603     netsnmp_variable_list *var;
604     int             len;
605
606     if (!reginfo || !reqinfo || !table_info)
607         return SNMPERR_GENERR;
608
609     var = reqinfo->requestvb;
610     len = reginfo->rootoid_len;
611     memcpy(tmpoid, reginfo->rootoid, len * sizeof(oid));
612     tmpoid[len++] = 1;          /* .Entry */
613     tmpoid[len++] = table_info->colnum; /* .column */
614     memcpy(&tmpoid[len], table_info->index_oid,
615            table_info->index_oid_len * sizeof(oid));
616     len += table_info->index_oid_len;
617     snmp_clone_mem((void **) &var->name, tmpoid, len * sizeof(oid));
618     var->name_length = len;
619
620     return SNMPERR_SUCCESS;
621 }
622
623 /** parses an OID into table indexses */
624 int
625 netsnmp_update_variable_list_from_index(netsnmp_table_request_info *tri)
626 {
627     if (!tri)
628         return SNMPERR_GENERR;
629
630     return parse_oid_indexes(tri->index_oid, tri->index_oid_len,
631                              tri->indexes);
632 }
633
634 /** builds an oid given a set of indexes. */
635 int
636 netsnmp_update_indexes_from_variable_list(netsnmp_table_request_info *tri)
637 {
638     if (!tri)
639         return SNMPERR_GENERR;
640
641     return build_oid_noalloc(tri->index_oid, sizeof(tri->index_oid),
642                              &tri->index_oid_len, NULL, 0, tri->indexes);
643 }
644
645 /**
646  * checks the original request against the current data being passed in if 
647  * its greater than the request oid but less than the current valid
648  * return, set the current valid return to the new value.
649  * 
650  * returns 1 if outvar was replaced with the oid from newvar (success).
651  * returns 0 if not. 
652  */
653 int
654 netsnmp_check_getnext_reply(netsnmp_request_info *request,
655                             oid * prefix,
656                             size_t prefix_len,
657                             netsnmp_variable_list * newvar,
658                             netsnmp_variable_list ** outvar)
659 {
660     oid      myname[MAX_OID_LEN];
661     size_t   myname_len;
662
663     build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
664                       prefix, prefix_len, newvar);
665     /*
666      * is the build of the new indexes less than our current result 
667      */
668     if ((!(*outvar) || snmp_oid_compare(myname + prefix_len,
669                                         myname_len - prefix_len,
670                                         (*outvar)->name + prefix_len,
671                                         (*outvar)->name_length -
672                                         prefix_len) < 0)) {
673         /*
674          * and greater than the requested oid 
675          */
676         if (snmp_oid_compare(myname, myname_len,
677                              request->requestvb->name,
678                              request->requestvb->name_length) > 0) {
679             /*
680              * the new result must be better than the old 
681              */
682             if (!*outvar)
683                 *outvar = snmp_clone_varbind(newvar);
684             snmp_set_var_objid(*outvar, myname, myname_len);
685
686             return 1;
687         }
688     }
689     return 0;
690 }
691
692 /** @} */
693
694 /*
695  * internal routines 
696  */
697 void
698 table_data_free_func(void *data)
699 {
700     netsnmp_table_request_info *info = (netsnmp_table_request_info *) data;
701     if (!info)
702         return;
703     snmp_free_varbind(info->indexes);
704     free(info);
705 }
706
707
708
709 static void
710 table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
711                      netsnmp_request_info *request, int status)
712 {
713     netsnmp_set_request_error(reqinfo, request, status);
714     netsnmp_free_request_data_sets(request);
715     if (!request)
716         return;
717     request->parent_data = NULL;
718 }
719
720
721 unsigned int
722 netsnmp_closest_column(unsigned int current,
723                        netsnmp_column_info *valid_columns)
724 {
725     unsigned int    closest = 0;
726     char            done = 0;
727     int             idx;
728
729     if (valid_columns == NULL)
730         return 0;
731
732     do {
733
734         if (valid_columns->isRange) {
735
736             if (current < valid_columns->details.range[0]) {
737                 if (valid_columns->details.range[0] < closest) {
738                     closest = valid_columns->details.range[0];
739                 }
740             } else if (current <= valid_columns->details.range[1]) {
741                 closest = current;
742                 done = 1;       /* can not get any closer! */
743             }
744
745         } /* range */
746         else {                  /* list */
747
748             if (current < valid_columns->details.list[0]) {
749                 if (valid_columns->details.list[0] < closest)
750                     closest = valid_columns->details.list[0];
751                 continue;
752             }
753
754             if (current >
755                 valid_columns->details.list[(int)valid_columns->list_count])
756                 continue;       /* not in list range. */
757
758             for (idx = 0; idx < (int)valid_columns->list_count; ++idx) {
759                 if (current == valid_columns->details.list[idx]) {
760                     closest = current;
761                     done = 1;   /* can not get any closer! */
762                     break;      /* for */
763                 } else if (current < valid_columns->details.list[idx]) {
764                     if (valid_columns->details.list[idx] < closest)
765                         closest = valid_columns->details.list[idx];
766                     break;      /* list should be sorted */
767                 }
768             }                   /* for */
769
770         }                       /* list */
771
772         valid_columns = valid_columns->next;
773
774     } while (!done && valid_columns);
775
776     return closest;
777 }
778
779 void
780 #if HAVE_STDARG_H
781 netsnmp_table_helper_add_indexes(netsnmp_table_registration_info *tinfo,
782                                  ...)
783 #else
784 netsnmp_table_helper_add_indexes(va_alist)
785      va_dcl
786 #endif
787 {
788     va_list         debugargs;
789     int             type;
790
791 #if HAVE_STDARG_H
792     va_start(debugargs, tinfo);
793 #else
794     netsnmp_table_registration_info *tinfo;
795
796     va_start(debugargs);
797     tinfo = va_arg(debugargs, table_info *);
798 #endif
799
800     while ((type = va_arg(debugargs, int)) != 0) {
801         netsnmp_table_helper_add_index(tinfo, type);
802     }
803
804     va_end(debugargs);
805 }
806
807 /** returns a row-wide place to store data in.
808     @todo This function will likely change to add free pointer functions. */
809 netsnmp_oid_stash_node **
810 netsnmp_table_get_or_create_row_stash(netsnmp_agent_request_info *reqinfo,
811                                       const u_char * storage_name)
812 {
813     netsnmp_oid_stash_node **stashp = NULL;
814     stashp = (netsnmp_oid_stash_node **)
815         netsnmp_agent_get_list_data(reqinfo, storage_name);
816
817     if (!stashp) {
818         /*
819          * hasn't be created yet.  we create it here. 
820          */
821         stashp = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node *);
822
823         if (!stashp)
824             return NULL;        /* ack. out of mem */
825
826         netsnmp_agent_add_list_data(reqinfo,
827                                     /*
828                                      * XXX: free: wrong 
829                                      */
830                                     netsnmp_create_data_list(storage_name,
831                                                              stashp,
832                                                              free));
833     }
834     return stashp;
835 }