net-snmp 5.7
table_iterator.c
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