added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table_iterator.c
1 /*
2  * table_iterator.c 
3  */
4 /** @defgroup table_iterator table_iterator: The table iterator helper is designed to simplify the task of writing a table handler for the net-snmp agent when the data being accessed is not in an oid sorted form and must be accessed externally.
5  *  @ingroup table
6  *  Functionally, it is a specialized version of the more
7  *  generic table helper but easies the burden of GETNEXT processing by
8  *  manually looping through all the data indexes retrieved through
9  *  function calls which should be supplied by the module that wishes
10  *  help.  The module the table_iterator helps should, afterwards,
11  *  never be called for the case of "MODE_GETNEXT" and only for the GET
12  *  and SET related modes instead.
13  */
14
15 #include <net-snmp/net-snmp-config.h>
16
17 #if HAVE_STRING_H
18 #include <string.h>
19 #else
20 #include <strings.h>
21 #endif
22
23 #include <net-snmp/net-snmp-includes.h>
24 #include <net-snmp/agent/net-snmp-agent-includes.h>
25
26 #include <net-snmp/agent/table.h>
27 #include <net-snmp/agent/serialize.h>
28 #include <net-snmp/agent/table_iterator.h>
29
30 #if HAVE_DMALLOC_H
31 #include <dmalloc.h>
32 #endif
33
34 /*
35  * doesn't work yet, but shouldn't be serialized (for efficiency) 
36  */
37 #undef NOT_SERIALIZED
38 #define TABLE_ITERATOR_LAST_CONTEXT "ti_last_c"
39
40 /** returns a netsnmp_mib_handler object for the table_iterator helper */
41 netsnmp_mib_handler *
42 netsnmp_get_table_iterator_handler(netsnmp_iterator_info *iinfo)
43 {
44     netsnmp_mib_handler *me =
45         netsnmp_create_handler(TABLE_ITERATOR_NAME,
46                                netsnmp_table_iterator_helper_handler);
47
48     if (!me || !iinfo)
49         return NULL;
50
51     me->myvoid = iinfo;
52     return me;
53 }
54
55
56 /** registers a table after attaching it to a table_iterator helper */
57 int
58 netsnmp_register_table_iterator(netsnmp_handler_registration *reginfo,
59                                 netsnmp_iterator_info *iinfo)
60 {
61     netsnmp_inject_handler(reginfo,
62                            netsnmp_get_table_iterator_handler(iinfo));
63     if (!iinfo)
64         return SNMPERR_GENERR;
65
66 #ifndef NOT_SERIALIZED
67     netsnmp_inject_handler(reginfo, netsnmp_get_serialize_handler());
68 #endif
69     return netsnmp_register_table(reginfo, iinfo->table_reginfo);
70 }
71
72 /** extracts the table_iterator specific data from a request */
73 NETSNMP_INLINE void    *
74 netsnmp_extract_iterator_context(netsnmp_request_info *request)
75 {
76     return netsnmp_request_get_list_data(request, TABLE_ITERATOR_NAME);
77 }
78
79 /** implements the table_iterator helper */
80 int
81 netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler,
82                                       netsnmp_handler_registration
83                                       *reginfo,
84                                       netsnmp_agent_request_info *reqinfo,
85                                       netsnmp_request_info *requests)
86 {
87
88     netsnmp_table_registration_info *tbl_info;
89     oid             coloid[MAX_OID_LEN];
90     size_t          coloid_len;
91     int             ret;
92     static oid      myname[MAX_OID_LEN];
93     static int      myname_len;
94     int             oldmode;
95     netsnmp_iterator_info *iinfo;
96
97     iinfo = (netsnmp_iterator_info *) handler->myvoid;
98     if (!iinfo || !reginfo || !reqinfo)
99         return SNMPERR_GENERR;
100
101     tbl_info = iinfo->table_reginfo;
102
103     /*
104      * copy in the table registration oid for later use 
105      */
106     coloid_len = reginfo->rootoid_len + 2;
107     memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
108     coloid[reginfo->rootoid_len] = 1;   /* table.entry node */
109
110     /*
111      * illegally got here if these functions aren't defined 
112      */
113     if (iinfo->get_first_data_point == NULL ||
114         iinfo->get_next_data_point == NULL) {
115         snmp_log(LOG_ERR,
116                  "table_iterator helper called without data accessor functions\n");
117         return SNMP_ERR_GENERR;
118     }
119
120     /*
121      * XXXWWW: deal with SET caching 
122      */
123
124 #ifdef NOT_SERIALIZED
125     while (requests)            /* XXX: currently only serialized */
126 #endif
127     {
128         /*
129          * XXXWWW: optimize by reversing loops (look through data only once) 
130          */
131         netsnmp_variable_list *results = NULL;
132         netsnmp_variable_list *index_search = NULL;     /* WWW: move up? */
133         netsnmp_variable_list *free_this_index_search = NULL;
134         netsnmp_table_request_info *table_info =
135             netsnmp_extract_table_info(requests);
136         void           *callback_loop_context = NULL;
137         void           *callback_data_context = NULL;
138         void           *callback_data_keep = NULL;
139
140         if (requests->processed != 0) {
141 #ifdef NOT_SERIALIZED
142             continue;
143 #else
144             return SNMP_ERR_NOERROR;
145 #endif
146         }
147
148         if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT && reqinfo->mode != MODE_GETBULK &&      /* XXX */
149             reqinfo->mode != MODE_SET_RESERVE1) {
150             goto skip_processing;
151         }
152
153
154         if (table_info->colnum > tbl_info->max_column) {
155             requests->processed = 1;
156 #ifdef NOT_SERIALIZED
157             break;
158 #else
159             return SNMP_ERR_NOERROR;
160 #endif
161         }
162
163         /*
164          * XXX: if loop through everything, these are never free'd
165          * since iterator returns NULL and thus we forget about
166          * these 
167          */
168
169         index_search = snmp_clone_varbind(table_info->indexes);
170         if (!index_search) {
171             /*
172              * hmmm....  invalid table? 
173              */
174             snmp_log(LOG_WARNING,
175                      "invalid index list or failed malloc for table %s\n",
176                      reginfo->handlerName);
177             return SNMP_ERR_NOERROR;
178         }
179
180         free_this_index_search = index_search;
181
182         /*
183          * below our minimum column? 
184          */
185         if (table_info->colnum < tbl_info->min_column) {
186             results =
187                 (iinfo->get_first_data_point) (&callback_loop_context,
188                                                &callback_data_context,
189                                                index_search, iinfo);
190             if (iinfo->free_loop_context) {
191                 (iinfo->free_loop_context) (callback_loop_context, iinfo);
192                 callback_loop_context = NULL;
193             }
194             goto got_results;
195         }
196
197         /*
198          * XXX: do "only got some indexes" 
199          */
200
201         /*
202          * find the next legal result to return 
203          */
204         /*
205          * find the first node 
206          */
207         index_search =
208             (iinfo->get_first_data_point) (&callback_loop_context,
209                                            &callback_data_context,
210                                            index_search, iinfo);
211         /*
212          * table.entry.column node 
213          */
214         coloid[reginfo->rootoid_len + 1] = table_info->colnum;
215
216         switch (reqinfo->mode) {
217         case MODE_GETNEXT:
218         case MODE_GETBULK:     /* XXXWWW */
219             /*
220              * loop through all data and find next one 
221              */
222             while (index_search) {
223                 /*
224                  * compare the node with previous results 
225                  */
226                 if (netsnmp_check_getnext_reply
227                     (requests, coloid, coloid_len, index_search,
228                      &results)) {
229
230                     /*
231                      * result is our current choice, so keep a pointer to
232                      * the data that the lower handler wants us to
233                      * remember (possibly freeing the last known "good"
234                      * result data pointer) 
235                      */
236                     if (callback_data_keep && iinfo->free_data_context) {
237                         (iinfo->free_data_context) (callback_data_keep,
238                                                     iinfo);
239                         callback_data_keep = NULL;
240                     }
241                     if (iinfo->make_data_context && !callback_data_context) {
242                         callback_data_context =
243                             (iinfo->
244                              make_data_context) (callback_loop_context,
245                                                  iinfo);
246
247                     }
248                     callback_data_keep = callback_data_context;
249                     callback_data_context = NULL;
250                 } else {
251                     if (callback_data_context && iinfo->free_data_context)
252                         (iinfo->free_data_context) (callback_data_context,
253                                                     iinfo);
254                     callback_data_context = NULL;
255                 }
256
257                 /*
258                  * get the next node in the data chain 
259                  */
260                 index_search =
261                     (iinfo->get_next_data_point) (&callback_loop_context,
262                                                   &callback_data_context,
263                                                   index_search, iinfo);
264
265                 if (!index_search && !results &&
266                     tbl_info->max_column > table_info->colnum) {
267                     /*
268                      * restart loop.  XXX: Should cache this better 
269                      */
270                     table_info->colnum++;
271                     coloid[reginfo->rootoid_len + 1] = table_info->colnum;
272                     if (free_this_index_search != NULL)
273                         snmp_free_varbind(free_this_index_search);
274                     index_search = snmp_clone_varbind(table_info->indexes);
275                     free_this_index_search = index_search;
276
277                     if (callback_loop_context &&
278                         iinfo->free_loop_context_at_end) {
279                         (iinfo->free_loop_context_at_end)(callback_loop_context,
280                                                           iinfo);
281                         callback_loop_context = NULL;
282                     }
283                     if (iinfo->free_loop_context && callback_loop_context) {
284                         (iinfo->free_loop_context) (callback_loop_context,
285                                                     iinfo);
286                         callback_loop_context = NULL;
287                     }
288                     if (callback_data_context && iinfo->free_data_context) {
289                         (iinfo->free_data_context) (callback_data_context,
290                                                     iinfo);
291                         callback_data_context = NULL;
292                     }
293                     
294                     index_search =
295                         (iinfo->
296                          get_first_data_point) (&callback_loop_context,
297                                                 &callback_data_context,
298                                                 index_search, iinfo);
299                 }
300             }
301
302             break;
303
304         case MODE_GET:
305         case MODE_SET_RESERVE1:
306             /*
307              * loop through all data till exact results are found 
308              */
309
310             while (index_search) {
311                 build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
312                                   coloid, coloid_len, index_search);
313                 if (snmp_oid_compare(myname, myname_len,
314                                      requests->requestvb->name,
315                                      requests->requestvb->name_length) ==
316                     0) {
317                     /*
318                      * found the exact match, so we're done 
319                      */
320                     if (iinfo->make_data_context && !callback_data_context) {
321                         callback_data_context =
322                             (iinfo->
323                              make_data_context) (callback_loop_context,
324                                                  iinfo);
325
326                     }
327                     callback_data_keep = callback_data_context;
328                     callback_data_context = NULL;
329                     results = snmp_clone_varbind(index_search);
330                     snmp_set_var_objid(results, myname, myname_len);
331                     goto got_results;
332                 } else {
333                     /*
334                      * free not-needed data context 
335                      */
336                     if (callback_data_context && iinfo->free_data_context) {
337                         (iinfo->free_data_context) (callback_data_context,
338                                                     iinfo);
339                         callback_data_context = NULL;
340                     }
341
342                 }
343
344                 /*
345                  * get the next node in the data chain 
346                  */
347                 index_search =
348                     (iinfo->get_next_data_point) (&callback_loop_context,
349                                                   &callback_data_context,
350                                                   index_search, iinfo);
351             }
352             break;
353
354         default:
355             /*
356              * the rest of the set states have been dealt with already 
357              */
358             goto got_results;
359         }
360
361         /*
362          * XXX: free index_search? 
363          */
364         if (callback_loop_context && iinfo->free_loop_context) {
365             (iinfo->free_loop_context) (callback_loop_context, iinfo);
366             callback_loop_context = NULL;
367         }
368
369       got_results:             /* not milk */
370    
371        /*
372         * This free_data_context call is required in the event that your
373         * get_next_data_point method allocates new memory, even during the
374         * calls where it eventually returns a NULL
375         */
376         if (callback_data_context && iinfo->free_data_context) {
377                (iinfo->free_data_context) (callback_data_context,
378                                            iinfo);
379                callback_data_context = NULL;
380         }
381
382         if (!results && !MODE_IS_SET(reqinfo->mode)) {
383             /*
384              * no results found. 
385              */
386             /*
387              * XXX: check for at least one entry at the very top 
388              */
389 #ifdef NOT_SERIALIZED
390             break;
391 #else
392             if (callback_loop_context && iinfo->free_loop_context_at_end) {
393                 (iinfo->free_loop_context_at_end) (callback_loop_context,
394                                                    iinfo);
395                 callback_loop_context = NULL;
396             }
397             if (free_this_index_search != NULL) {
398                 snmp_free_varbind(free_this_index_search);
399             }
400             return SNMP_ERR_NOERROR;
401 #endif
402         }
403
404       skip_processing:
405         /*
406          * OK, here results should be a pointer to the data that we
407          * actually need to GET 
408          */
409         oldmode = reqinfo->mode;
410         if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {   /* XXX */
411             snmp_set_var_objid(requests->requestvb, results->name,
412                                results->name_length);
413             reqinfo->mode = MODE_GET;
414         }
415         if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK ||      /* XXX */
416             reqinfo->mode == MODE_SET_RESERVE1) {
417             /*
418              * first (or only) pass stuff 
419              */
420             /*
421              * let set requsets use previously constructed data 
422              */
423             snmp_free_varbind(results);
424             if (callback_data_keep)
425                 netsnmp_request_add_list_data(requests,
426                                               netsnmp_create_data_list
427                                               (TABLE_ITERATOR_NAME,
428                                                callback_data_keep, NULL));
429             netsnmp_request_add_list_data(requests,
430                                           netsnmp_create_data_list
431                                           (TABLE_ITERATOR_LAST_CONTEXT,
432                                            callback_loop_context, NULL));
433         }
434
435         DEBUGMSGTL(("table_iterator", "doing mode: %s\n",
436                     se_find_label_in_slist("agent_mode", oldmode)));
437         ret =
438             netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
439         if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) {       /* XXX */
440             if (requests->requestvb->type == ASN_NULL ||
441                 requests->requestvb->type == SNMP_NOSUCHINSTANCE) {
442                 /*
443                  * get next skipped this value for this column, we
444                  * need to keep searching forward 
445                  */
446                 requests->requestvb->type = ASN_PRIV_RETRY;
447             }
448             reqinfo->mode = oldmode;
449         }
450
451         callback_data_keep =
452             netsnmp_request_get_list_data(requests, TABLE_ITERATOR_NAME);
453         callback_loop_context =
454             netsnmp_request_get_list_data(requests,
455                                           TABLE_ITERATOR_LAST_CONTEXT);
456
457         /* 
458          * This has to be done to prevent a memory leak. Notice that on
459          * SET_RESERVE1 we're assigning something to
460          * 'free_this_index_search' at the beginning of this handler (right
461          * above the line that says 'below our minimum column?'), 
462          * but we're not given a chance to free it below with the other 
463          * SET modes, hence our doing it here. 
464          */
465         if (reqinfo->mode == MODE_SET_RESERVE1) {
466             if (free_this_index_search) {
467                 snmp_free_varbind(free_this_index_search);
468                 free_this_index_search = NULL;
469             }
470         }
471         if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT ||
472             reqinfo->mode == MODE_GETBULK ||      /* XXX */
473             reqinfo->mode == MODE_SET_FREE ||
474             reqinfo->mode == MODE_SET_UNDO ||
475             reqinfo->mode == MODE_SET_COMMIT) {
476             if (callback_data_keep && iinfo->free_data_context) {
477                 (iinfo->free_data_context) (callback_data_keep, iinfo);
478                 callback_data_keep = NULL;
479             }
480
481             if (free_this_index_search) {
482                 snmp_free_varbind(free_this_index_search);
483                 free_this_index_search = NULL;
484             }
485 #ifndef NOT_SERIALIZED
486             if (callback_loop_context && iinfo->free_loop_context_at_end) {
487                 (iinfo->free_loop_context_at_end) (callback_loop_context,
488                                                    iinfo);
489                 callback_loop_context = NULL;
490             }
491 #endif
492         }
493 #ifdef NOT_SERIALIZED
494         return ret;
495 #else
496         requests = requests->next;
497 #endif
498     }
499 #ifdef NOT_SERIALIZED
500     if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK ||  /* XXX */
501         reqinfo->mode == MODE_SET_FREE ||
502         reqinfo->mode == MODE_SET_UNDO ||
503         reqinfo->mode == MODE_SET_COMMIT) {
504         if (callback_loop_context && iinfo->free_loop_context_at_end) {
505             (iinfo->free_loop_context_at_end) (callback_loop_context,
506                                                iinfo);
507             callback_loop_context = NULL;
508         }
509     }
510 #endif
511     return SNMP_ERR_NOERROR;
512 }