net-snmp  5.4.1
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 #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