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.
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.
15 #include <net-snmp/net-snmp-config.h>
23 #include <net-snmp/net-snmp-includes.h>
24 #include <net-snmp/agent/net-snmp-agent-includes.h>
26 #include <net-snmp/agent/table.h>
27 #include <net-snmp/agent/serialize.h>
28 #include <net-snmp/agent/table_iterator.h>
35 * doesn't work yet, but shouldn't be serialized (for efficiency)
38 #define TABLE_ITERATOR_LAST_CONTEXT "ti_last_c"
40 /** returns a netsnmp_mib_handler object for the table_iterator helper */
42 netsnmp_get_table_iterator_handler(netsnmp_iterator_info *iinfo)
44 netsnmp_mib_handler *me =
45 netsnmp_create_handler(TABLE_ITERATOR_NAME,
46 netsnmp_table_iterator_helper_handler);
56 /** registers a table after attaching it to a table_iterator helper */
58 netsnmp_register_table_iterator(netsnmp_handler_registration *reginfo,
59 netsnmp_iterator_info *iinfo)
61 netsnmp_inject_handler(reginfo,
62 netsnmp_get_table_iterator_handler(iinfo));
64 return SNMPERR_GENERR;
66 #ifndef NOT_SERIALIZED
67 netsnmp_inject_handler(reginfo, netsnmp_get_serialize_handler());
69 return netsnmp_register_table(reginfo, iinfo->table_reginfo);
72 /** extracts the table_iterator specific data from a request */
74 netsnmp_extract_iterator_context(netsnmp_request_info *request)
76 return netsnmp_request_get_list_data(request, TABLE_ITERATOR_NAME);
79 /** implements the table_iterator helper */
81 netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler,
82 netsnmp_handler_registration
84 netsnmp_agent_request_info *reqinfo,
85 netsnmp_request_info *requests)
88 netsnmp_table_registration_info *tbl_info;
89 oid coloid[MAX_OID_LEN];
92 static oid myname[MAX_OID_LEN];
93 static int myname_len;
95 netsnmp_iterator_info *iinfo;
97 iinfo = (netsnmp_iterator_info *) handler->myvoid;
98 if (!iinfo || !reginfo || !reqinfo)
99 return SNMPERR_GENERR;
101 tbl_info = iinfo->table_reginfo;
104 * copy in the table registration oid for later use
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 */
111 * illegally got here if these functions aren't defined
113 if (iinfo->get_first_data_point == NULL ||
114 iinfo->get_next_data_point == NULL) {
116 "table_iterator helper called without data accessor functions\n");
117 return SNMP_ERR_GENERR;
121 * XXXWWW: deal with SET caching
124 #ifdef NOT_SERIALIZED
125 while (requests) /* XXX: currently only serialized */
129 * XXXWWW: optimize by reversing loops (look through data only once)
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;
140 if (requests->processed != 0) {
141 #ifdef NOT_SERIALIZED
144 return SNMP_ERR_NOERROR;
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;
154 if (table_info->colnum > tbl_info->max_column) {
155 requests->processed = 1;
156 #ifdef NOT_SERIALIZED
159 return SNMP_ERR_NOERROR;
164 * XXX: if loop through everything, these are never free'd
165 * since iterator returns NULL and thus we forget about
169 index_search = snmp_clone_varbind(table_info->indexes);
172 * hmmm.... invalid table?
174 snmp_log(LOG_WARNING,
175 "invalid index list or failed malloc for table %s\n",
176 reginfo->handlerName);
177 return SNMP_ERR_NOERROR;
180 free_this_index_search = index_search;
183 * below our minimum column?
185 if (table_info->colnum < tbl_info->min_column) {
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;
198 * XXX: do "only got some indexes"
202 * find the next legal result to return
205 * find the first node
208 (iinfo->get_first_data_point) (&callback_loop_context,
209 &callback_data_context,
210 index_search, iinfo);
212 * table.entry.column node
214 coloid[reginfo->rootoid_len + 1] = table_info->colnum;
216 switch (reqinfo->mode) {
218 case MODE_GETBULK: /* XXXWWW */
220 * loop through all data and find next one
222 while (index_search) {
224 * compare the node with previous results
226 if (netsnmp_check_getnext_reply
227 (requests, coloid, coloid_len, index_search,
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)
236 if (callback_data_keep && iinfo->free_data_context) {
237 (iinfo->free_data_context) (callback_data_keep,
239 callback_data_keep = NULL;
241 if (iinfo->make_data_context && !callback_data_context) {
242 callback_data_context =
244 make_data_context) (callback_loop_context,
248 callback_data_keep = callback_data_context;
249 callback_data_context = NULL;
251 if (callback_data_context && iinfo->free_data_context)
252 (iinfo->free_data_context) (callback_data_context,
254 callback_data_context = NULL;
258 * get the next node in the data chain
261 (iinfo->get_next_data_point) (&callback_loop_context,
262 &callback_data_context,
263 index_search, iinfo);
265 if (!index_search && !results &&
266 tbl_info->max_column > table_info->colnum) {
268 * restart loop. XXX: Should cache this better
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;
277 if (callback_loop_context &&
278 iinfo->free_loop_context_at_end) {
279 (iinfo->free_loop_context_at_end)(callback_loop_context,
281 callback_loop_context = NULL;
283 if (iinfo->free_loop_context && callback_loop_context) {
284 (iinfo->free_loop_context) (callback_loop_context,
286 callback_loop_context = NULL;
288 if (callback_data_context && iinfo->free_data_context) {
289 (iinfo->free_data_context) (callback_data_context,
291 callback_data_context = NULL;
296 get_first_data_point) (&callback_loop_context,
297 &callback_data_context,
298 index_search, iinfo);
305 case MODE_SET_RESERVE1:
307 * loop through all data till exact results are found
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) ==
318 * found the exact match, so we're done
320 if (iinfo->make_data_context && !callback_data_context) {
321 callback_data_context =
323 make_data_context) (callback_loop_context,
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);
334 * free not-needed data context
336 if (callback_data_context && iinfo->free_data_context) {
337 (iinfo->free_data_context) (callback_data_context,
339 callback_data_context = NULL;
345 * get the next node in the data chain
348 (iinfo->get_next_data_point) (&callback_loop_context,
349 &callback_data_context,
350 index_search, iinfo);
356 * the rest of the set states have been dealt with already
362 * XXX: free index_search?
364 if (callback_loop_context && iinfo->free_loop_context) {
365 (iinfo->free_loop_context) (callback_loop_context, iinfo);
366 callback_loop_context = NULL;
369 got_results: /* not milk */
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
376 if (callback_data_context && iinfo->free_data_context) {
377 (iinfo->free_data_context) (callback_data_context,
379 callback_data_context = NULL;
382 if (!results && !MODE_IS_SET(reqinfo->mode)) {
387 * XXX: check for at least one entry at the very top
389 #ifdef NOT_SERIALIZED
392 if (callback_loop_context && iinfo->free_loop_context_at_end) {
393 (iinfo->free_loop_context_at_end) (callback_loop_context,
395 callback_loop_context = NULL;
397 if (free_this_index_search != NULL) {
398 snmp_free_varbind(free_this_index_search);
400 return SNMP_ERR_NOERROR;
406 * OK, here results should be a pointer to the data that we
407 * actually need to GET
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;
415 if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK || /* XXX */
416 reqinfo->mode == MODE_SET_RESERVE1) {
418 * first (or only) pass stuff
421 * let set requsets use previously constructed data
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));
435 DEBUGMSGTL(("table_iterator", "doing mode: %s\n",
436 se_find_label_in_slist("agent_mode", oldmode)));
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) {
443 * get next skipped this value for this column, we
444 * need to keep searching forward
446 requests->requestvb->type = ASN_PRIV_RETRY;
448 reqinfo->mode = oldmode;
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);
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.
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;
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;
481 if (free_this_index_search) {
482 snmp_free_varbind(free_this_index_search);
483 free_this_index_search = NULL;
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,
489 callback_loop_context = NULL;
493 #ifdef NOT_SERIALIZED
496 requests = requests->next;
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,
507 callback_loop_context = NULL;
511 return SNMP_ERR_NOERROR;