net-snmp
5.4.1
|
00001 /* 00002 * table_iterator.c 00003 */ 00004 /* Portions of this file are subject to the following copyright(s). See 00005 * the Net-SNMP's COPYING file for more details and other copyrights 00006 * that may apply: 00007 */ 00008 /* 00009 * Portions of this file are copyrighted by: 00010 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. 00011 * Use is subject to license terms specified in the COPYING file 00012 * distributed with the Net-SNMP package. 00013 */ 00014 00085 #include <net-snmp/net-snmp-config.h> 00086 00087 #if HAVE_STRING_H 00088 #include <string.h> 00089 #else 00090 #include <strings.h> 00091 #endif 00092 00093 #include <net-snmp/net-snmp-includes.h> 00094 #include <net-snmp/agent/net-snmp-agent-includes.h> 00095 00096 #include <net-snmp/agent/table.h> 00097 #include <net-snmp/agent/serialize.h> 00098 #include <net-snmp/agent/table_iterator.h> 00099 #include <net-snmp/agent/stash_cache.h> 00100 00101 /* ================================== 00102 * 00103 * Iterator API: Table maintenance 00104 * 00105 * ================================== */ 00106 00107 /* 00108 * Iterator-based tables are typically maintained by external 00109 * code, and this helper is really only concerned with 00110 * mapping between a walk through this local representation, 00111 * and the requirements of SNMP table ordering. 00112 * However, there's a case to be made for considering the 00113 * iterator info structure as encapsulating the table, so 00114 * it's probably worth defining the table creation/deletion 00115 * routines from the generic API. 00116 * 00117 * Time will show whether this is a sensible approach or not. 00118 */ 00119 netsnmp_iterator_info * 00120 netsnmp_iterator_create_table( Netsnmp_First_Data_Point *firstDP, 00121 Netsnmp_Next_Data_Point *nextDP, 00122 Netsnmp_First_Data_Point *getidx, 00123 netsnmp_variable_list *indexes) 00124 { 00125 netsnmp_iterator_info *iinfo = 00126 SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info); 00127 00128 if ( !iinfo ) 00129 return NULL; 00130 00131 if ( indexes ) 00132 iinfo->indexes = snmp_clone_varbind(indexes); 00133 iinfo->get_first_data_point = firstDP; 00134 iinfo->get_next_data_point = nextDP; 00135 iinfo->get_row_indexes = getidx; 00136 00137 return iinfo; 00138 } 00139 00140 void 00141 netsnmp_iterator_delete_table( netsnmp_iterator_info *iinfo ) 00142 { 00143 if (!iinfo) 00144 return; 00145 00146 if (iinfo->indexes) { 00147 snmp_free_varbind( iinfo->indexes ); 00148 iinfo->indexes = NULL; 00149 } 00150 SNMP_FREE( iinfo ); 00151 } 00152 00153 /* 00154 * The rest of the table maintenance section of the 00155 * generic table API is Not Applicable to this helper. 00156 * 00157 * The contents of a iterator-based table will be 00158 * maintained by the table-specific module itself. 00159 */ 00160 00161 /* ================================== 00162 * 00163 * Iterator API: MIB maintenance 00164 * 00165 * ================================== */ 00166 00168 netsnmp_mib_handler * 00169 netsnmp_get_table_iterator_handler(netsnmp_iterator_info *iinfo) 00170 { 00171 netsnmp_mib_handler *me; 00172 00173 if (!iinfo) 00174 return NULL; 00175 00176 me = 00177 netsnmp_create_handler(TABLE_ITERATOR_NAME, 00178 netsnmp_table_iterator_helper_handler); 00179 00180 if (!me) 00181 return NULL; 00182 00183 me->myvoid = iinfo; 00184 return me; 00185 } 00186 00204 int 00205 netsnmp_register_table_iterator(netsnmp_handler_registration *reginfo, 00206 netsnmp_iterator_info *iinfo) 00207 { 00208 reginfo->modes |= HANDLER_CAN_STASH; 00209 netsnmp_inject_handler(reginfo, 00210 netsnmp_get_table_iterator_handler(iinfo)); 00211 if (!iinfo) 00212 return SNMPERR_GENERR; 00213 if (!iinfo->indexes && iinfo->table_reginfo && 00214 iinfo->table_reginfo->indexes ) 00215 iinfo->indexes = snmp_clone_varbind( iinfo->table_reginfo->indexes ); 00216 00217 return netsnmp_register_table(reginfo, iinfo->table_reginfo); 00218 } 00219 00233 NETSNMP_INLINE void * 00234 netsnmp_extract_iterator_context(netsnmp_request_info *request) 00235 { 00236 return netsnmp_request_get_list_data(request, TABLE_ITERATOR_NAME); 00237 } 00238 00241 NETSNMP_INLINE void 00242 netsnmp_insert_iterator_context(netsnmp_request_info *request, void *data) 00243 { 00244 netsnmp_request_info *req; 00245 netsnmp_table_request_info *table_info = NULL; 00246 netsnmp_variable_list *this_index = NULL; 00247 netsnmp_variable_list *that_index = NULL; 00248 oid base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */ 00249 oid this_oid[MAX_OID_LEN]; 00250 oid that_oid[MAX_OID_LEN]; 00251 size_t this_oid_len, that_oid_len; 00252 00253 if (!request) 00254 return; 00255 00256 /* 00257 * We'll add the new row information to any request 00258 * structure with the same index values as the request 00259 * passed in (which includes that one!). 00260 * 00261 * So construct an OID based on these index values. 00262 */ 00263 00264 table_info = netsnmp_extract_table_info(request); 00265 this_index = table_info->indexes; 00266 build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len, 00267 base_oid, 2, this_index); 00268 00269 /* 00270 * We need to look through the whole of the request list 00271 * (as received by the current handler), as there's no 00272 * guarantee that this routine will be called by the first 00273 * varbind that refers to this row. 00274 * In particular, a RowStatus controlled row creation 00275 * may easily occur later in the variable list. 00276 * 00277 * So first, we rewind to the head of the list.... 00278 */ 00279 for (req=request; req->prev; req=req->prev) 00280 ; 00281 00282 /* 00283 * ... and then start looking for matching indexes 00284 * (by constructing OIDs from these index values) 00285 */ 00286 for (; req; req=req->next) { 00287 table_info = netsnmp_extract_table_info(req); 00288 that_index = table_info->indexes; 00289 build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len, 00290 base_oid, 2, that_index); 00291 00292 /* 00293 * This request has the same index values, 00294 * so add the newly-created row information. 00295 */ 00296 if (snmp_oid_compare(this_oid, this_oid_len, 00297 that_oid, that_oid_len) == 0) { 00298 netsnmp_request_add_list_data(req, 00299 netsnmp_create_data_list(TABLE_ITERATOR_NAME, data, NULL)); 00300 } 00301 } 00302 } 00303 00304 #define TI_REQUEST_CACHE "ti_cache" 00305 00306 typedef struct ti_cache_info_s { 00307 oid best_match[MAX_OID_LEN]; 00308 size_t best_match_len; 00309 void *data_context; 00310 Netsnmp_Free_Data_Context *free_context; 00311 netsnmp_iterator_info *iinfo; 00312 netsnmp_variable_list *results; 00313 } ti_cache_info; 00314 00315 static void 00316 netsnmp_free_ti_cache(void *it) { 00317 ti_cache_info *beer = it; 00318 if (!it) return; 00319 if (beer->data_context && beer->free_context) { 00320 (beer->free_context)(beer->data_context, beer->iinfo); 00321 } 00322 if (beer->results) { 00323 snmp_free_varbind(beer->results); 00324 } 00325 free(beer); 00326 } 00327 00328 /* caches information (in the request) we'll need at a later point in time */ 00329 static ti_cache_info * 00330 netsnmp_iterator_remember(netsnmp_request_info *request, 00331 oid *oid_to_save, 00332 size_t oid_to_save_len, 00333 void *callback_data_context, 00334 void *callback_loop_context, 00335 netsnmp_iterator_info *iinfo) 00336 { 00337 ti_cache_info *ti_info; 00338 00339 if (!request || !oid_to_save || oid_to_save_len > MAX_OID_LEN) 00340 return NULL; 00341 00342 /* extract existing cached state */ 00343 ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); 00344 00345 /* no existing cached state. make a new one. */ 00346 if (!ti_info) { 00347 ti_info = SNMP_MALLOC_TYPEDEF(ti_cache_info); 00348 netsnmp_request_add_list_data(request, 00349 netsnmp_create_data_list 00350 (TI_REQUEST_CACHE, 00351 ti_info, 00352 netsnmp_free_ti_cache)); 00353 } 00354 00355 /* free existing cache before replacing */ 00356 if (ti_info->data_context && ti_info->free_context) 00357 (ti_info->free_context)(ti_info->data_context, iinfo); 00358 00359 /* maybe generate it from the loop context? */ 00360 if (iinfo->make_data_context && !callback_data_context) { 00361 callback_data_context = 00362 (iinfo->make_data_context)(callback_loop_context, iinfo); 00363 00364 } 00365 00366 /* save data as requested */ 00367 ti_info->data_context = callback_data_context; 00368 ti_info->free_context = iinfo->free_data_context; 00369 ti_info->best_match_len = oid_to_save_len; 00370 ti_info->iinfo = iinfo; 00371 if (oid_to_save_len) 00372 memcpy(ti_info->best_match, oid_to_save, oid_to_save_len * sizeof(oid)); 00373 00374 return ti_info; 00375 } 00376 00377 #define TABLE_ITERATOR_NOTAGAIN 255 00378 /* implements the table_iterator helper */ 00379 int 00380 netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler, 00381 netsnmp_handler_registration *reginfo, 00382 netsnmp_agent_request_info *reqinfo, 00383 netsnmp_request_info *requests) 00384 { 00385 netsnmp_table_registration_info *tbl_info; 00386 netsnmp_table_request_info *table_info = NULL; 00387 oid coloid[MAX_OID_LEN]; 00388 size_t coloid_len; 00389 int ret; 00390 static oid myname[MAX_OID_LEN]; 00391 size_t myname_len; 00392 int oldmode = 0; 00393 netsnmp_iterator_info *iinfo; 00394 int notdone; 00395 netsnmp_request_info *request, *reqtmp = NULL; 00396 netsnmp_variable_list *index_search = NULL; 00397 netsnmp_variable_list *free_this_index_search = NULL; 00398 void *callback_loop_context = NULL, *last_loop_context; 00399 void *callback_data_context = NULL; 00400 ti_cache_info *ti_info = NULL; 00401 int request_count = 0; 00402 netsnmp_oid_stash_node **cinfo = NULL; 00403 netsnmp_variable_list *old_indexes = NULL, *vb; 00404 netsnmp_table_registration_info *table_reg_info = NULL; 00405 int i; 00406 netsnmp_data_list *ldata = NULL; 00407 00408 iinfo = (netsnmp_iterator_info *) handler->myvoid; 00409 if (!iinfo || !reginfo || !reqinfo) 00410 return SNMPERR_GENERR; 00411 00412 tbl_info = iinfo->table_reginfo; 00413 00414 /* 00415 * copy in the table registration oid for later use 00416 */ 00417 coloid_len = reginfo->rootoid_len + 2; 00418 memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); 00419 coloid[reginfo->rootoid_len] = 1; /* table.entry node */ 00420 00421 /* 00422 * illegally got here if these functions aren't defined 00423 */ 00424 if (iinfo->get_first_data_point == NULL || 00425 iinfo->get_next_data_point == NULL) { 00426 snmp_log(LOG_ERR, 00427 "table_iterator helper called without data accessor functions\n"); 00428 return SNMP_ERR_GENERR; 00429 } 00430 00431 /* preliminary analysis */ 00432 switch (reqinfo->mode) { 00433 case MODE_GET_STASH: 00434 cinfo = netsnmp_extract_stash_cache(reqinfo); 00435 table_reg_info = netsnmp_find_table_registration_info(reginfo); 00436 00437 /* XXX: move this malloc to stash_cache handler? */ 00438 reqtmp = SNMP_MALLOC_TYPEDEF(netsnmp_request_info); 00439 reqtmp->subtree = requests->subtree; 00440 table_info = netsnmp_extract_table_info(requests); 00441 netsnmp_request_add_list_data(reqtmp, 00442 netsnmp_create_data_list 00443 (TABLE_HANDLER_NAME, 00444 (void *) table_info, NULL)); 00445 00446 /* remember the indexes that were originally parsed. */ 00447 old_indexes = table_info->indexes; 00448 break; 00449 00450 case MODE_GETNEXT: 00451 for(request = requests ; request; request = request->next) { 00452 if (request->processed) 00453 continue; 00454 table_info = netsnmp_extract_table_info(request); 00455 if (table_info->colnum < tbl_info->min_column - 1) { 00456 /* XXX: optimize better than this */ 00457 /* for now, just increase to colnum-1 */ 00458 /* we need to jump to the lowest result of the min_column 00459 and take it, comparing to nothing from the request */ 00460 table_info->colnum = tbl_info->min_column - 1; 00461 } else if (table_info->colnum > tbl_info->max_column) { 00462 request->processed = TABLE_ITERATOR_NOTAGAIN; 00463 } 00464 00465 ti_info = 00466 netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); 00467 if (!ti_info) { 00468 ti_info = SNMP_MALLOC_TYPEDEF(ti_cache_info); 00469 netsnmp_request_add_list_data(request, 00470 netsnmp_create_data_list 00471 (TI_REQUEST_CACHE, 00472 ti_info, 00473 netsnmp_free_ti_cache)); 00474 } 00475 00476 /* XXX: if no valid requests, don't even loop below */ 00477 } 00478 break; 00479 } 00480 00481 /* 00482 * collect all information for each needed row 00483 */ 00484 if (reqinfo->mode == MODE_GET || 00485 reqinfo->mode == MODE_GETNEXT || 00486 reqinfo->mode == MODE_GET_STASH || 00487 reqinfo->mode == MODE_SET_RESERVE1) { 00488 /* 00489 * Count the number of request in the list, 00490 * so that we'll know when we're finished 00491 */ 00492 for(request = requests ; request; request = request->next) 00493 if (!request->processed) 00494 request_count++; 00495 notdone = 1; 00496 while(notdone) { 00497 notdone = 0; 00498 00499 /* find first data point */ 00500 if (!index_search) { 00501 if (free_this_index_search) { 00502 /* previously done */ 00503 index_search = free_this_index_search; 00504 } else { 00505 for(request=requests ; request; request=request->next) { 00506 table_info = netsnmp_extract_table_info(request); 00507 if (table_info) 00508 break; 00509 } 00510 if (!table_info) { 00511 snmp_log(LOG_WARNING, 00512 "no valid requests for iterator table %s\n", 00513 reginfo->handlerName); 00514 netsnmp_free_request_data_sets(reqtmp); 00515 SNMP_FREE(reqtmp); 00516 return SNMP_ERR_NOERROR; 00517 } 00518 index_search = snmp_clone_varbind(table_info->indexes); 00519 free_this_index_search = index_search; 00520 00521 /* setup, malloc search data: */ 00522 if (!index_search) { 00523 /* 00524 * hmmm.... invalid table? 00525 */ 00526 snmp_log(LOG_WARNING, 00527 "invalid index list or failed malloc for table %s\n", 00528 reginfo->handlerName); 00529 netsnmp_free_request_data_sets(reqtmp); 00530 SNMP_FREE(reqtmp); 00531 return SNMP_ERR_NOERROR; 00532 } 00533 } 00534 } 00535 00536 /* if sorted, pass in a hint */ 00537 if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) { 00538 callback_loop_context = table_info; 00539 } 00540 index_search = 00541 (iinfo->get_first_data_point) (&callback_loop_context, 00542 &callback_data_context, 00543 index_search, iinfo); 00544 00545 /* loop over each data point */ 00546 while(index_search) { 00547 00548 /* remember to free this later */ 00549 free_this_index_search = index_search; 00550 00551 /* compare against each request*/ 00552 for(request = requests ; request; request = request->next) { 00553 if (request->processed) 00554 continue; 00555 00556 /* XXX: store in an array for faster retrival */ 00557 table_info = netsnmp_extract_table_info(request); 00558 coloid[reginfo->rootoid_len + 1] = table_info->colnum; 00559 00560 ti_info = 00561 netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); 00562 00563 switch(reqinfo->mode) { 00564 case MODE_GET: 00565 case MODE_SET_RESERVE1: 00566 /* looking for exact matches */ 00567 build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, 00568 coloid, coloid_len, index_search); 00569 if (snmp_oid_compare(myname, myname_len, 00570 request->requestvb->name, 00571 request->requestvb->name_length) == 0) { 00572 /* keep this */ 00573 netsnmp_iterator_remember(request, 00574 myname, myname_len, 00575 callback_data_context, 00576 callback_loop_context, iinfo); 00577 request_count--; /* One less to look for */ 00578 } else { 00579 if (iinfo->free_data_context && callback_data_context) { 00580 (iinfo->free_data_context)(callback_data_context, 00581 iinfo); 00582 } 00583 } 00584 break; 00585 00586 case MODE_GET_STASH: 00587 /* collect data for each column for every row */ 00588 build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, 00589 coloid, coloid_len, index_search); 00590 reqinfo->mode = MODE_GET; 00591 if (reqtmp) 00592 ldata = 00593 netsnmp_get_list_node(reqtmp->parent_data, 00594 TABLE_ITERATOR_NAME); 00595 if (!ldata) { 00596 netsnmp_request_add_list_data(reqtmp, 00597 netsnmp_create_data_list 00598 (TABLE_ITERATOR_NAME, 00599 callback_data_context, 00600 NULL)); 00601 } else { 00602 /* may have changed */ 00603 ldata->data = callback_data_context; 00604 } 00605 00606 table_info->indexes = index_search; 00607 for(i = table_reg_info->min_column; 00608 i <= (int)table_reg_info->max_column; i++) { 00609 myname[reginfo->rootoid_len + 1] = i; 00610 table_info->colnum = i; 00611 vb = reqtmp->requestvb = 00612 SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); 00613 vb->type = ASN_NULL; 00614 snmp_set_var_objid(vb, myname, myname_len); 00615 netsnmp_call_next_handler(handler, reginfo, 00616 reqinfo, reqtmp); 00617 reqtmp->requestvb = NULL; 00618 reqtmp->processed = 0; 00619 if (vb->type != ASN_NULL) { /* XXX, not all */ 00620 netsnmp_oid_stash_add_data(cinfo, myname, 00621 myname_len, vb); 00622 } else { 00623 snmp_free_var(vb); 00624 } 00625 } 00626 reqinfo->mode = MODE_GET_STASH; 00627 break; 00628 00629 case MODE_GETNEXT: 00630 /* looking for "next" matches */ 00631 if (netsnmp_check_getnext_reply 00632 (request, coloid, coloid_len, index_search, 00633 &ti_info->results)) { 00634 netsnmp_iterator_remember(request, 00635 ti_info->results->name, 00636 ti_info->results->name_length, 00637 callback_data_context, 00638 callback_loop_context, iinfo); 00639 /* 00640 * If we've been told that the rows are sorted, 00641 * then the first valid one we find 00642 * must be the right one. 00643 */ 00644 if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) 00645 request_count--; 00646 00647 } else { 00648 if (iinfo->free_data_context && callback_data_context) { 00649 (iinfo->free_data_context)(callback_data_context, 00650 iinfo); 00651 } 00652 } 00653 break; 00654 00655 case MODE_SET_RESERVE2: 00656 case MODE_SET_FREE: 00657 case MODE_SET_UNDO: 00658 case MODE_SET_COMMIT: 00659 /* needed processing already done in RESERVE1 */ 00660 break; 00661 00662 default: 00663 snmp_log(LOG_ERR, 00664 "table_iterator called with unsupported mode\n"); 00665 break; /* XXX return */ 00666 00667 } 00668 } 00669 00670 /* Is there any point in carrying on? */ 00671 if (!request_count) 00672 break; 00673 /* get the next search possibility */ 00674 last_loop_context = callback_loop_context; 00675 index_search = 00676 (iinfo->get_next_data_point) (&callback_loop_context, 00677 &callback_data_context, 00678 index_search, iinfo); 00679 if (iinfo->free_loop_context && last_loop_context && 00680 callback_data_context != last_loop_context) { 00681 (iinfo->free_loop_context) (last_loop_context, iinfo); 00682 last_loop_context = NULL; 00683 } 00684 } 00685 00686 /* free loop context before going on */ 00687 if (callback_loop_context && iinfo->free_loop_context_at_end) { 00688 (iinfo->free_loop_context_at_end) (callback_loop_context, 00689 iinfo); 00690 callback_loop_context = NULL; 00691 } 00692 00693 /* decide which (GETNEXT) requests are not yet filled */ 00694 if (reqinfo->mode == MODE_GETNEXT) { 00695 for(request = requests ; request; request = request->next) { 00696 if (request->processed) 00697 continue; 00698 ti_info = 00699 netsnmp_request_get_list_data(request, 00700 TI_REQUEST_CACHE); 00701 if (!ti_info->results) { 00702 int nc; 00703 table_info = netsnmp_extract_table_info(request); 00704 nc = netsnmp_table_next_column(table_info); 00705 if (0 == nc) { 00706 coloid[reginfo->rootoid_len+1] = table_info->colnum+1; 00707 snmp_set_var_objid(request->requestvb, 00708 coloid, reginfo->rootoid_len+2); 00709 request->processed = TABLE_ITERATOR_NOTAGAIN; 00710 break; 00711 } else { 00712 table_info->colnum = nc; 00713 notdone = 1; 00714 } 00715 } 00716 } 00717 } 00718 } 00719 } 00720 00721 if (reqinfo->mode == MODE_GET || 00722 reqinfo->mode == MODE_GETNEXT || 00723 reqinfo->mode == MODE_SET_RESERVE1) { 00724 /* per request last minute processing */ 00725 for(request = requests ; request; request = request->next) { 00726 if (request->processed) 00727 continue; 00728 ti_info = 00729 netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); 00730 table_info = 00731 netsnmp_extract_table_info(request); 00732 00733 if (!ti_info) 00734 continue; 00735 00736 switch(reqinfo->mode) { 00737 00738 case MODE_GETNEXT: 00739 if (ti_info->best_match_len) 00740 snmp_set_var_objid(request->requestvb, ti_info->best_match, 00741 ti_info->best_match_len); 00742 else { 00743 coloid[reginfo->rootoid_len+1] = 00744 netsnmp_table_next_column(table_info); 00745 if (0 == coloid[reginfo->rootoid_len+1]) { 00746 /* out of range. */ 00747 coloid[reginfo->rootoid_len+1] = tbl_info->max_column + 1; 00748 request->processed = TABLE_ITERATOR_NOTAGAIN; 00749 } 00750 snmp_set_var_objid(request->requestvb, 00751 coloid, reginfo->rootoid_len+2); 00752 request->processed = 1; 00753 } 00754 snmp_free_varbind(table_info->indexes); 00755 table_info->indexes = snmp_clone_varbind(ti_info->results); 00756 /* FALL THROUGH */ 00757 00758 case MODE_GET: 00759 case MODE_SET_RESERVE1: 00760 if (ti_info->data_context) 00761 /* we don't add a free pointer, since it's in the 00762 TI_REQUEST_CACHE instead */ 00763 netsnmp_request_add_list_data(request, 00764 netsnmp_create_data_list 00765 (TABLE_ITERATOR_NAME, 00766 ti_info->data_context, 00767 NULL)); 00768 break; 00769 00770 default: 00771 break; 00772 } 00773 } 00774 00775 /* we change all GETNEXT operations into GET operations. 00776 why? because we're just so nice to the lower levels. 00777 maybe someday they'll pay us for it. doubtful though. */ 00778 oldmode = reqinfo->mode; 00779 if (reqinfo->mode == MODE_GETNEXT) { 00780 reqinfo->mode = MODE_GET; 00781 } 00782 } else if (reqinfo->mode == MODE_GET_STASH) { 00783 netsnmp_free_request_data_sets(reqtmp); 00784 SNMP_FREE(reqtmp); 00785 table_info->indexes = old_indexes; 00786 } 00787 00788 00789 /* Finally, we get to call the next handler below us. Boy, wasn't 00790 all that simple? They better be glad they don't have to do it! */ 00791 if (reqinfo->mode != MODE_GET_STASH) { 00792 DEBUGMSGTL(("table_iterator", "call subhandler for mode: %s\n", 00793 se_find_label_in_slist("agent_mode", oldmode))); 00794 ret = 00795 netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); 00796 } 00797 00798 /* reverse the previously saved mode if we were a getnext */ 00799 if (oldmode == MODE_GETNEXT) { 00800 reqinfo->mode = oldmode; 00801 } 00802 00803 /* cleanup */ 00804 if (free_this_index_search) 00805 snmp_free_varbind(free_this_index_search); 00806 00807 return SNMP_ERR_NOERROR; 00808 } 00809 00810 /* ================================== 00811 * 00812 * Iterator API: Row operations 00813 * 00814 * ================================== */ 00815 00816 void * 00817 netsnmp_iterator_row_first( netsnmp_iterator_info *iinfo ) { 00818 netsnmp_variable_list *vp1, *vp2; 00819 void *ctx1, *ctx2; 00820 00821 if (!iinfo) 00822 return NULL; 00823 00824 vp1 = snmp_clone_varbind(iinfo->indexes); 00825 vp2 = iinfo->get_first_data_point( &ctx1, &ctx2, vp1, iinfo ); 00826 00827 if (!vp2) 00828 ctx2 = NULL; 00829 00830 /* free loop context ?? */ 00831 snmp_free_varbind( vp1 ); 00832 return ctx2; /* or *ctx2 ?? */ 00833 } 00834 00835 void * 00836 netsnmp_iterator_row_get( netsnmp_iterator_info *iinfo, void *row ) 00837 { 00838 netsnmp_variable_list *vp1, *vp2; 00839 void *ctx1, *ctx2; 00840 00841 if (!iinfo || !row) 00842 return NULL; 00843 00844 /* 00845 * This routine relies on being able to 00846 * determine the indexes for a given row. 00847 */ 00848 if (!iinfo->get_row_indexes) 00849 return NULL; 00850 00851 vp1 = snmp_clone_varbind(iinfo->indexes); 00852 ctx1 = row; /* Probably only need one of these ... */ 00853 ctx2 = row; 00854 vp2 = iinfo->get_row_indexes( &ctx1, &ctx2, vp1, iinfo ); 00855 00856 ctx2 = NULL; 00857 if (vp2) { 00858 ctx2 = netsnmp_iterator_row_get_byidx( iinfo, vp2 ); 00859 } 00860 snmp_free_varbind( vp1 ); 00861 return ctx2; 00862 } 00863 00864 void * 00865 netsnmp_iterator_row_next( netsnmp_iterator_info *iinfo, void *row ) 00866 { 00867 netsnmp_variable_list *vp1, *vp2; 00868 void *ctx1, *ctx2; 00869 00870 if (!iinfo || !row) 00871 return NULL; 00872 00873 /* 00874 * This routine relies on being able to 00875 * determine the indexes for a given row. 00876 */ 00877 if (!iinfo->get_row_indexes) 00878 return NULL; 00879 00880 vp1 = snmp_clone_varbind(iinfo->indexes); 00881 ctx1 = row; /* Probably only need one of these ... */ 00882 ctx2 = row; 00883 vp2 = iinfo->get_row_indexes( &ctx1, &ctx2, vp1, iinfo ); 00884 00885 ctx2 = NULL; 00886 if (vp2) { 00887 ctx2 = netsnmp_iterator_row_next_byidx( iinfo, vp2 ); 00888 } 00889 snmp_free_varbind( vp1 ); 00890 return ctx2; 00891 } 00892 00893 void * 00894 netsnmp_iterator_row_get_byidx( netsnmp_iterator_info *iinfo, 00895 netsnmp_variable_list *indexes ) 00896 { 00897 oid dummy[] = {0,0}; /* Keep 'build_oid' happy */ 00898 oid instance[MAX_OID_LEN]; 00899 size_t len = MAX_OID_LEN; 00900 00901 if (!iinfo || !indexes) 00902 return NULL; 00903 00904 build_oid_noalloc(instance, MAX_OID_LEN, &len, 00905 dummy, 2, indexes); 00906 return netsnmp_iterator_row_get_byoid( iinfo, instance+2, len-2 ); 00907 } 00908 00909 void * 00910 netsnmp_iterator_row_next_byidx( netsnmp_iterator_info *iinfo, 00911 netsnmp_variable_list *indexes ) 00912 { 00913 oid dummy[] = {0,0}; 00914 oid instance[MAX_OID_LEN]; 00915 size_t len = MAX_OID_LEN; 00916 00917 if (!iinfo || !indexes) 00918 return NULL; 00919 00920 build_oid_noalloc(instance, MAX_OID_LEN, &len, 00921 dummy, 2, indexes); 00922 return netsnmp_iterator_row_next_byoid( iinfo, instance+2, len-2 ); 00923 } 00924 00925 void * 00926 netsnmp_iterator_row_get_byoid( netsnmp_iterator_info *iinfo, 00927 oid *instance, size_t len ) 00928 { 00929 oid dummy[] = {0,0}; 00930 oid this_inst[ MAX_OID_LEN]; 00931 size_t this_len; 00932 netsnmp_variable_list *vp1, *vp2; 00933 void *ctx1, *ctx2; 00934 int n; 00935 00936 if (!iinfo || !iinfo->get_first_data_point 00937 || !iinfo->get_next_data_point ) 00938 return NULL; 00939 00940 if ( !instance || !len ) 00941 return NULL; 00942 00943 vp1 = snmp_clone_varbind(iinfo->indexes); 00944 vp2 = iinfo->get_first_data_point( &ctx1, &ctx2, vp1, iinfo ); 00945 DEBUGMSGTL(("table:iterator:get", "first DP: %x %x %x\n", 00946 ctx1, ctx2, vp2)); 00947 00948 /* XXX - free context ? */ 00949 00950 while ( vp2 ) { 00951 this_len = MAX_OID_LEN; 00952 build_oid_noalloc(this_inst, MAX_OID_LEN, &this_len, dummy, 2, vp2); 00953 n = snmp_oid_compare( instance, len, this_inst+2, this_len-2 ); 00954 if ( n == 0 ) 00955 break; /* Found matching row */ 00956 00957 if (( n > 0) && 00958 (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED)) { 00959 vp2 = NULL; /* Row not present */ 00960 break; 00961 } 00962 00963 vp2 = iinfo->get_next_data_point( &ctx1, &ctx2, vp2, iinfo ); 00964 DEBUGMSGTL(("table:iterator:get", "next DP: %x %x %x\n", 00965 ctx1, ctx2, vp2)); 00966 /* XXX - free context ? */ 00967 } 00968 00969 /* XXX - final free context ? */ 00970 snmp_free_varbind( vp1 ); 00971 00972 return ( vp2 ? ctx2 : NULL ); 00973 } 00974 00975 void * 00976 netsnmp_iterator_row_next_byoid( netsnmp_iterator_info *iinfo, 00977 oid *instance, size_t len ) 00978 { 00979 oid dummy[] = {0,0}; 00980 oid this_inst[ MAX_OID_LEN]; 00981 size_t this_len; 00982 oid best_inst[ MAX_OID_LEN]; 00983 size_t best_len = 0; 00984 netsnmp_variable_list *vp1, *vp2; 00985 void *ctx1, *ctx2; 00986 int n; 00987 00988 if (!iinfo || !iinfo->get_first_data_point 00989 || !iinfo->get_next_data_point ) 00990 return NULL; 00991 00992 vp1 = snmp_clone_varbind(iinfo->indexes); 00993 vp2 = iinfo->get_first_data_point( &ctx1, &ctx2, vp1, iinfo ); 00994 DEBUGMSGTL(("table:iterator:get", "first DP: %x %x %x\n", 00995 ctx1, ctx2, vp2)); 00996 00997 if ( !instance || !len ) { 00998 snmp_free_varbind( vp1 ); 00999 return ( vp2 ? ctx2 : NULL ); /* First entry */ 01000 } 01001 01002 /* XXX - free context ? */ 01003 01004 while ( vp2 ) { 01005 this_len = MAX_OID_LEN; 01006 build_oid_noalloc(this_inst, MAX_OID_LEN, &this_len, dummy, 2, vp2); 01007 n = snmp_oid_compare( instance, len, this_inst+2, this_len-2 ); 01008 01009 /* 01010 * Look for the best-fit candidate for the next row 01011 * (bearing in mind the rows may not be ordered "correctly") 01012 */ 01013 if ( n > 0 ) { 01014 if ( best_len == 0 ) { 01015 memcpy( best_inst, this_inst, sizeof( this_inst )); 01016 best_len = this_len; 01017 if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) 01018 break; 01019 } else { 01020 n = snmp_oid_compare( best_inst, best_len, this_inst, this_len ); 01021 if ( n < 0 ) { 01022 memcpy( best_inst, this_inst, sizeof( this_inst )); 01023 best_len = this_len; 01024 if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) 01025 break; 01026 } 01027 } 01028 } 01029 01030 vp2 = iinfo->get_next_data_point( &ctx1, &ctx2, vp2, iinfo ); 01031 DEBUGMSGTL(("table:iterator:get", "next DP: %x %x %x\n", 01032 ctx1, ctx2, vp2)); 01033 /* XXX - free context ? */ 01034 } 01035 01036 /* XXX - final free context ? */ 01037 snmp_free_varbind( vp1 ); 01038 01039 return ( vp2 ? ctx2 : NULL ); 01040 } 01041 01042 int 01043 netsnmp_iterator_row_count( netsnmp_iterator_info *iinfo ) 01044 { 01045 netsnmp_variable_list *vp1, *vp2; 01046 void *ctx1, *ctx2; 01047 int i=0; 01048 01049 if (!iinfo || !iinfo->get_first_data_point 01050 || !iinfo->get_next_data_point ) 01051 return 0; 01052 01053 vp1 = snmp_clone_varbind(iinfo->indexes); 01054 vp2 = iinfo->get_first_data_point( &ctx1, &ctx2, vp1, iinfo ); 01055 if (!vp2) { 01056 snmp_free_varbind( vp1 ); 01057 return 0; 01058 } 01059 01060 DEBUGMSGTL(("table:iterator:count", "first DP: %x %x %x\n", 01061 ctx1, ctx2, vp2)); 01062 01063 /* XXX - free context ? */ 01064 01065 while (vp2) { 01066 i++; 01067 vp2 = iinfo->get_next_data_point( &ctx1, &ctx2, vp2, iinfo ); 01068 DEBUGMSGTL(("table:iterator:count", "next DP: %x %x %x (%d)\n", 01069 ctx1, ctx2, vp2, i)); 01070 /* XXX - free context ? */ 01071 } 01072 01073 /* XXX - final free context ? */ 01074 snmp_free_varbind( vp1 ); 01075 return i; 01076 } 01077 01078 01079 /* ================================== 01080 * 01081 * Iterator API: Index operations 01082 * 01083 * ================================== */ 01084 01085