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