net-snmp  5.4.1
table.c
00001 /*
00002  * table.c 
00003  */
00004 
00005 /* Portions of this file are subject to the following copyright(s).  See
00006  * the Net-SNMP's COPYING file for more details and other copyrights
00007  * that may apply:
00008  */
00009 /*
00010  * Portions of this file are copyrighted by:
00011  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00012  * Use is subject to license terms specified in the COPYING file
00013  * distributed with the Net-SNMP package.
00014  */
00015 
00016 #include <net-snmp/net-snmp-config.h>
00017 
00018 #if HAVE_STRING_H
00019 #include <string.h>
00020 #else
00021 #include <strings.h>
00022 #endif
00023 
00024 
00025 #include <net-snmp/net-snmp-includes.h>
00026 #include <net-snmp/agent/net-snmp-agent-includes.h>
00027 
00028 #include <net-snmp/agent/table.h>
00029 #include <net-snmp/library/snmp_assert.h>
00030 
00031 static void     table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
00032                                      netsnmp_request_info *request,
00033                                      int status);
00034 static void     table_data_free_func(void *data);
00035 static int
00036 sparse_table_helper_handler(netsnmp_mib_handler *handler,
00037                             netsnmp_handler_registration *reginfo,
00038                             netsnmp_agent_request_info *reqinfo,
00039                             netsnmp_request_info *requests);
00040 
00084 netsnmp_mib_handler *
00085 netsnmp_get_table_handler(netsnmp_table_registration_info *tabreq)
00086 {
00087     netsnmp_mib_handler *ret = NULL;
00088 
00089     if (!tabreq) {
00090         snmp_log(LOG_INFO, "netsnmp_get_table_handler(NULL) called\n");
00091         return NULL;
00092     }
00093 
00094     ret = netsnmp_create_handler(TABLE_HANDLER_NAME, table_helper_handler);
00095     if (ret) {
00096         ret->myvoid = (void *) tabreq;
00097         tabreq->number_indexes = count_varbinds(tabreq->indexes);
00098     }
00099     return ret;
00100 }
00101 
00102 
00107 int
00108 netsnmp_register_table(netsnmp_handler_registration *reginfo,
00109                        netsnmp_table_registration_info *tabreq)
00110 {
00111     netsnmp_inject_handler(reginfo, netsnmp_get_table_handler(tabreq));
00112     return netsnmp_register_handler(reginfo);
00113 }
00114 
00124 NETSNMP_INLINE netsnmp_table_request_info *
00125 netsnmp_extract_table_info(netsnmp_request_info *request)
00126 {
00127     return (netsnmp_table_request_info *)
00128         netsnmp_request_get_list_data(request, TABLE_HANDLER_NAME);
00129 }
00130 
00133 netsnmp_table_registration_info *
00134 netsnmp_find_table_registration_info(netsnmp_handler_registration *reginfo)
00135 {
00136     return (netsnmp_table_registration_info *)
00137         netsnmp_find_handler_data_by_name(reginfo, TABLE_HANDLER_NAME);
00138 }
00139 
00141 int
00142 table_helper_handler(netsnmp_mib_handler *handler,
00143                      netsnmp_handler_registration *reginfo,
00144                      netsnmp_agent_request_info *reqinfo,
00145                      netsnmp_request_info *requests)
00146 {
00147 
00148     netsnmp_request_info *request;
00149     netsnmp_table_registration_info *tbl_info;
00150     int             oid_index_pos;
00151     unsigned int    oid_column_pos;
00152     unsigned int    tmp_idx;
00153     size_t          tmp_len;
00154     int             incomplete, out_of_range, cleaned_up = 0;
00155     int             status = SNMP_ERR_NOERROR, need_processing = 0;
00156     oid            *tmp_name;
00157     netsnmp_table_request_info *tbl_req_info;
00158     netsnmp_variable_list *vb;
00159 
00160     if (!reginfo || !handler)
00161         return SNMPERR_GENERR;
00162 
00163     oid_index_pos  = reginfo->rootoid_len + 2;
00164     oid_column_pos = reginfo->rootoid_len + 1;
00165     tbl_info = (netsnmp_table_registration_info *) handler->myvoid;
00166 
00167     if ((!handler->myvoid) || (!tbl_info->indexes)) {
00168         snmp_log(LOG_ERR, "improperly registered table found\n");
00169         snmp_log(LOG_ERR, "name: %s, table info: %p, indexes: %p\n",
00170                  handler->handler_name, handler->myvoid, tbl_info->indexes);
00171 
00172         /*
00173          * XXX-rks: unregister table? 
00174          */
00175         return SNMP_ERR_GENERR;
00176     }
00177 
00178     DEBUGIF("helper:table:req") {
00179         DEBUGMSGTL(("helper:table:req",
00180                     "Got request for handler %s: base oid:",
00181                     handler->handler_name));
00182         DEBUGMSGOID(("helper:table:req", reginfo->rootoid,
00183                      reginfo->rootoid_len));
00184         DEBUGMSG(("helper:table:req", "\n"));
00185     }
00186     
00187     /*
00188      * if the agent request info has a state reference, then this is a 
00189      * later pass of a set request and we can skip all the lookup stuff.
00190      *
00191      * xxx-rks: this might break for handlers which only handle one varbind
00192      * at a time... those handlers should not save data by their handler_name
00193      * in the netsnmp_agent_request_info. 
00194      */
00195     if (netsnmp_agent_get_list_data(reqinfo, handler->next->handler_name)) {
00196         if (MODE_IS_SET(reqinfo->mode)) {
00197             return netsnmp_call_next_handler(handler, reginfo, reqinfo,
00198                                              requests);
00199         } else {
00201             netsnmp_free_agent_data_sets(reqinfo);
00202         }
00203     }
00204 
00205     if ( MODE_IS_SET(reqinfo->mode) &&
00206          (reqinfo->mode != MODE_SET_RESERVE1)) {
00207         /*
00208          * for later set modes, we can skip all the index parsing,
00209          * and we always need to let child handlers have a chance
00210          * to clean up, if they were called in the first place (i.e. have
00211          * a valid table info pointer).
00212          */
00213         if(NULL == netsnmp_extract_table_info(requests)) {
00214             DEBUGMSGTL(("table:helper","no table info for set - skipping\n"));
00215         }
00216         else
00217             need_processing = 1;
00218     }
00219     else {
00220         /*
00221          * for RESERVE1 and GETS, only continue if we have at least
00222          * one valid request.
00223          */
00224            
00225     /*
00226      * loop through requests
00227      */
00228 
00229     for (request = requests; request; request = request->next) {
00230         netsnmp_variable_list *var = request->requestvb;
00231 
00232         DEBUGMSGOID(("verbose:table", var->name, var->name_length));
00233         DEBUGMSG(("verbose:table", "\n"));
00234 
00235         if (request->processed) {
00236             DEBUGMSG(("verbose:table", "already processed\n"));
00237             continue;
00238         }
00239         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
00240 
00241         /*
00242          * this should probably be handled further up 
00243          */
00244         if ((reqinfo->mode == MODE_GET) && (var->type != ASN_NULL)) {
00245             /*
00246              * valid request if ASN_NULL 
00247              */
00248             DEBUGMSGTL(("helper:table",
00249                         "  GET var type is not ASN_NULL\n"));
00250             netsnmp_set_request_error(reqinfo, request,
00251                                       SNMP_ERR_WRONGTYPE);
00252             continue;
00253         }
00254 
00255         if (reqinfo->mode == MODE_SET_RESERVE1) {
00256             DEBUGIF("helper:table:set") {
00257                 u_char         *buf = NULL;
00258                 size_t          buf_len = 0, out_len = 0;
00259                 DEBUGMSGTL(("helper:table:set", " SET_REQUEST for OID: "));
00260                 DEBUGMSGOID(("helper:table:set", var->name, var->name_length));
00261                 out_len = 0;
00262                 if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
00263                                            var, 0, 0, 0)) {
00264                     DEBUGMSG(("helper:table:set"," type=%d(%02x), value=%s\n",
00265                               var->type, var->type, buf));
00266                 } else {
00267                     if (buf != NULL) {
00268                         DEBUGMSG(("helper:table:set",
00269                                   " type=%d(%02x), value=%s [TRUNCATED]\n",
00270                                   var->type, var->type, buf));
00271                     } else {
00272                         DEBUGMSG(("helper:table:set",
00273                                   " type=%d(%02x), value=[NIL] [TRUNCATED]\n",
00274                                   var->type, var->type));
00275                     }
00276                 }
00277                 if (buf != NULL) {
00278                     free(buf);
00279                 }
00280             }
00281         }
00282 
00283         /*
00284          * check to make sure its in table range 
00285          */
00286 
00287         out_of_range = 0;
00288         /*
00289          * if our root oid is > var->name and this is not a GETNEXT, 
00290          * then the oid is out of range. (only compare up to shorter 
00291          * length) 
00292          */
00293         if (reginfo->rootoid_len > var->name_length)
00294             tmp_len = var->name_length;
00295         else
00296             tmp_len = reginfo->rootoid_len;
00297         if (snmp_oid_compare(reginfo->rootoid, reginfo->rootoid_len,
00298                              var->name, tmp_len) > 0) {
00299             if (reqinfo->mode == MODE_GETNEXT) {
00300                 if (var->name != var->name_loc)
00301                     SNMP_FREE(var->name);
00302                 snmp_set_var_objid(var, reginfo->rootoid,
00303                                    reginfo->rootoid_len);
00304             } else {
00305                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
00306                 out_of_range = 1;
00307             }
00308         }
00309         /*
00310          * if var->name is longer than the root, make sure it is 
00311          * table.1 (table.ENTRY).  
00312          */
00313         else if ((var->name_length > reginfo->rootoid_len) &&
00314                  (var->name[reginfo->rootoid_len] != 1)) {
00315             if ((var->name[reginfo->rootoid_len] < 1) &&
00316                 (reqinfo->mode == MODE_GETNEXT)) {
00317                 var->name[reginfo->rootoid_len] = 1;
00318                 var->name_length = reginfo->rootoid_len;
00319             } else {
00320                 out_of_range = 1;
00321                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
00322             }
00323         }
00324         /*
00325          * if it is not in range, then mark it in the request list 
00326          * because we can't process it, and set an error so
00327          * nobody else wastes time trying to process it either.  
00328          */
00329         if (out_of_range) {
00330             DEBUGMSGTL(("helper:table", "  Not processed: "));
00331             DEBUGMSGOID(("helper:table", var->name, var->name_length));
00332             DEBUGMSG(("helper:table", "\n"));
00333 
00334             /*
00335              *  Reject requests of the form 'myTable.N'   (N != 1)
00336              */
00337             if (reqinfo->mode == MODE_SET_RESERVE1)
00338                 table_helper_cleanup(reqinfo, request,
00339                                      SNMP_ERR_NOTWRITABLE);
00340             else if (reqinfo->mode == MODE_GET)
00341                 table_helper_cleanup(reqinfo, request,
00342                                      SNMP_NOSUCHOBJECT);
00343             continue;
00344         }
00345 
00346 
00347         /*
00348          * Check column ranges; set-up to pull out indexes from OID. 
00349          */
00350 
00351         incomplete = 0;
00352         tbl_req_info = netsnmp_extract_table_info(request);
00353         if (NULL == tbl_req_info) {
00354             tbl_req_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_request_info);
00355             tbl_req_info->reg_info = tbl_info;
00356             tbl_req_info->indexes = snmp_clone_varbind(tbl_info->indexes);
00357             tbl_req_info->number_indexes = 0;       /* none yet */
00358             netsnmp_request_add_list_data(request,
00359                                           netsnmp_create_data_list
00360                                           (TABLE_HANDLER_NAME,
00361                                            (void *) tbl_req_info,
00362                                            table_data_free_func));
00363         } else {
00364             DEBUGMSGTL(("helper:table", "  using existing tbl_req_info\n "));
00365         }
00366 
00367         /*
00368          * do we have a column?
00369          */
00370         if (var->name_length > oid_column_pos) {
00371             /*
00372              * oid is long enough to contain COLUMN info
00373              */
00374             DEBUGMSGTL(("helper:table:col", "  have at least a column (%d)\n",
00375                         var->name[oid_column_pos]));
00376             if (var->name[oid_column_pos] < tbl_info->min_column) {
00377                 DEBUGMSGTL(("helper:table:col",
00378                             "    but it's less than min (%d)\n",
00379                             tbl_info->min_column));
00380                 if (reqinfo->mode == MODE_GETNEXT) {
00381                     /*
00382                      * fix column, truncate useless column info 
00383                      */
00384                     var->name_length = oid_column_pos;
00385                     tbl_req_info->colnum = tbl_info->min_column;
00386                 } else
00387                     out_of_range = 1;
00388             } else if (var->name[oid_column_pos] > tbl_info->max_column)
00389                 out_of_range = 1;
00390             else
00391                 tbl_req_info->colnum = var->name[oid_column_pos];
00392 
00393             if (out_of_range) {
00394                 /*
00395                  * this is out of range...  remove from requests, free
00396                  * memory 
00397                  */
00398                 DEBUGMSGTL(("helper:table",
00399                             "    oid is out of range. Not processed: "));
00400                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
00401                 DEBUGMSG(("helper:table", "\n"));
00402 
00403                 /*
00404                  *  Reject requests of the form 'myEntry.N'   (invalid N)
00405                  */
00406                 if (reqinfo->mode == MODE_SET_RESERVE1)
00407                     table_helper_cleanup(reqinfo, request,
00408                                          SNMP_ERR_NOTWRITABLE);
00409                 else if (reqinfo->mode == MODE_GET)
00410                     table_helper_cleanup(reqinfo, request,
00411                                          SNMP_NOSUCHOBJECT);
00412                 continue;
00413             }
00414             /*
00415              * use column verification 
00416              */
00417             else if (tbl_info->valid_columns) {
00418                 tbl_req_info->colnum =
00419                     netsnmp_closest_column(var->name[oid_column_pos],
00420                                            tbl_info->valid_columns);
00421                 DEBUGMSGTL(("helper:table:col", "    closest column is %d\n",
00422                             tbl_req_info->colnum));
00423                 /*
00424                  * xxx-rks: document why the continue...
00425                  */
00426                 if (tbl_req_info->colnum == 0)
00427                     continue;
00428                 if (tbl_req_info->colnum != var->name[oid_column_pos]) {
00429                     DEBUGMSGTL(("helper:table:col",
00430                                 "    which doesn't match req %d - truncating index info\n",
00431                                    var->name[oid_column_pos]));
00432                     /*
00433                      * different column! truncate useless index info 
00434                      */
00435                     var->name_length = oid_column_pos + 1; /* pos is 0 based */
00436                 }
00437             }
00438             /*
00439              * var->name_length may have changed - check again 
00440              */
00441             if ((int)var->name_length <= oid_index_pos) { /* pos is 0 based */
00442                 DEBUGMSGTL(("helper:table", "    not enough for indexes\n"));
00443                 tbl_req_info->index_oid_len = 0; 
00444             } else {
00445                 /*
00446                  * oid is long enough to contain INDEX info
00447                  */
00448                 tbl_req_info->index_oid_len =
00449                     var->name_length - oid_index_pos;
00450                 DEBUGMSGTL(("helper:table", "    have %d bytes of index\n",
00451                             tbl_req_info->index_oid_len));
00452                 netsnmp_assert(tbl_req_info->index_oid_len < MAX_OID_LEN);
00453                 memcpy(tbl_req_info->index_oid, &var->name[oid_index_pos],
00454                        tbl_req_info->index_oid_len * sizeof(oid));
00455                 tmp_name = tbl_req_info->index_oid;
00456             }
00457         } else if (reqinfo->mode == MODE_GETNEXT ||
00458                    reqinfo->mode == MODE_GETBULK) {
00459             /*
00460              * oid is NOT long enough to contain column or index info, so start
00461              * at the minimum column. Set index oid len to 0 because we don't
00462              * have any index info in the OID.
00463              */
00464             DEBUGMSGTL(("helper:table", "  no column/index in request\n"));
00465             tbl_req_info->index_oid_len = 0;
00466             tbl_req_info->colnum = tbl_info->min_column;
00467         } else {
00468             /*
00469              * oid is NOT long enough to contain index info,
00470              * so we can't do anything with it.
00471              *
00472              * Reject requests of the form 'myTable' or 'myEntry'
00473              */
00474             if (reqinfo->mode == MODE_GET ) {
00475                 table_helper_cleanup(reqinfo, request, SNMP_NOSUCHOBJECT);
00476             } else if (reqinfo->mode == MODE_SET_RESERVE1 ) {
00477                 table_helper_cleanup(reqinfo, request, SNMP_ERR_NOTWRITABLE);
00478             }
00479             continue;
00480         }
00481 
00482         /*
00483          * set up tmp_len to be the number of OIDs we have beyond the column;
00484          * these should be the index(s) for the table. If the index_oid_len
00485          * is 0, set tmp_len to -1 so that when we try to parse the index below,
00486          * we just zero fill everything.
00487          */
00488         if (tbl_req_info->index_oid_len == 0) {
00489             incomplete = 1;
00490             tmp_len = -1;
00491         } else
00492             tmp_len = tbl_req_info->index_oid_len;
00493 
00494 
00495         /*
00496          * for each index type, try to extract the index from var->name
00497          */
00498         DEBUGMSGTL(("helper:table", "  looking for %d indexes\n",
00499                     tbl_info->number_indexes));
00500         for (tmp_idx = 0, vb = tbl_req_info->indexes;
00501              tmp_idx < tbl_info->number_indexes;
00502              ++tmp_idx, vb = vb->next_variable) {
00503             if (incomplete && tmp_len) {
00504                 /*
00505                  * incomplete/illegal OID, set up dummy 0 to parse 
00506                  */
00507                 DEBUGMSGTL(("helper:table",
00508                             "  oid indexes not complete: "));
00509                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
00510                 DEBUGMSG(("helper:table", "\n"));
00511 
00512                 /*
00513                  * no sense in trying anymore if this is a GET/SET. 
00514                  *
00515                  * Reject requests of the form 'myObject'   (no instance)
00516                  */
00517                 if (reqinfo->mode != MODE_GETNEXT) {
00518                     table_helper_cleanup(reqinfo, requests,
00519                                          SNMP_NOSUCHINSTANCE);
00520                     cleaned_up = 1;
00521                 }
00522                 tmp_len = 0;
00523                 tmp_name = (oid *) & tmp_len;
00524                 break;
00525             }
00526             /*
00527              * try and parse current index 
00528              */
00529             if (parse_one_oid_index(&tmp_name, &tmp_len,
00530                                     vb, 1) != SNMPERR_SUCCESS) {
00531                 incomplete = 1;
00532                 tmp_len = -1;   /* is this necessary? Better safe than
00533                                  * sorry */
00534             } else {
00535                 /*
00536                  * do not count incomplete indexes 
00537                  */
00538                 DEBUGMSGTL(("helper:table", "  got 1 (incomplete=%d)\n",
00539                             incomplete));
00540                 if (incomplete)
00541                     continue;
00542                 ++tbl_req_info->number_indexes; 
00543                 if (tmp_len <= 0) {
00544                     incomplete = 1;
00545                     tmp_len = -1;       /* is this necessary? Better safe
00546                                          * than sorry */
00547                 }
00548             }
00549         }                       
00551         DEBUGIF("helper:table:results") {
00552             DEBUGMSGTL(("helper:table:results", "  found %d indexes\n",
00553                         tbl_req_info->number_indexes));
00554             if (!cleaned_up) {
00555                 unsigned int    count;
00556                 u_char         *buf = NULL;
00557                 size_t          buf_len = 0, out_len = 0;
00558                 DEBUGMSGTL(("helper:table:results",
00559                             "  column: %d, indexes: %d",
00560                             tbl_req_info->colnum,
00561                             tbl_req_info->number_indexes));
00562                 for (vb = tbl_req_info->indexes, count = 0;
00563                      vb && count < tbl_req_info->number_indexes;
00564                      count++, vb = vb->next_variable) {
00565                     out_len = 0;
00566                     if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
00567                                                vb, 0, 0, 0)) {
00568                         DEBUGMSG(("helper:table:results",
00569                                   "   index: type=%d(%02x), value=%s",
00570                                   vb->type, vb->type, buf));
00571                     } else {
00572                         if (buf != NULL) {
00573                             DEBUGMSG(("helper:table:results",
00574                                       "   index: type=%d(%02x), value=%s [TRUNCATED]",
00575                                       vb->type, vb->type, buf));
00576                         } else {
00577                             DEBUGMSG(("helper:table:results",
00578                                       "   index: type=%d(%02x), value=[NIL] [TRUNCATED]",
00579                                       vb->type, vb->type));
00580                         }
00581                     }
00582                 }
00583                 if (buf != NULL) {
00584                     free(buf);
00585                 }
00586                 DEBUGMSG(("helper:table:results", "\n"));
00587             }
00588         }
00589 
00590 
00591         /*
00592          * do we have sufficent index info to continue?
00593          */
00594 
00595         if ((reqinfo->mode != MODE_GETNEXT) &&
00596             ((tbl_req_info->number_indexes != tbl_info->number_indexes) ||
00597              (tmp_len != -1))) {
00598             DEBUGMSGTL(("helper:table",
00599                         "invalid index(es) for table - skipping\n"));
00600             table_helper_cleanup(reqinfo, request, SNMP_NOSUCHINSTANCE);
00601             continue;
00602         }
00603         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
00604         
00605         ++need_processing;
00606 
00607     }                           /* for each request */
00608     }
00609 
00610     /*
00611      * bail if there is nothing for our child handlers
00612      */
00613     if (0 == need_processing)
00614         return status;
00615 
00616     /*
00617      * call our child access function 
00618      */
00619     status =
00620         netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
00621 
00622     /*
00623      * check for sparse tables
00624      */
00625     if (reqinfo->mode == MODE_GETNEXT)
00626         sparse_table_helper_handler( handler, reginfo, reqinfo, requests );
00627 
00628     return status;
00629 }
00630 
00631 #define SPARSE_TABLE_HANDLER_NAME "sparse_table"
00632 
00641 static int
00642 sparse_table_helper_handler(netsnmp_mib_handler *handler,
00643                      netsnmp_handler_registration *reginfo,
00644                      netsnmp_agent_request_info *reqinfo,
00645                      netsnmp_request_info *requests)
00646 {
00647     int             status = SNMP_ERR_NOERROR;
00648     netsnmp_request_info *request;
00649     oid             coloid[MAX_OID_LEN];
00650     netsnmp_table_request_info *table_info;
00651 
00652     /*
00653      * since we don't call child handlers, warn if one was registered
00654      * beneath us. A special exception for the table helper, which calls
00655      * the handler directly. Use handle custom flag to only log once.
00656      */
00657     if((table_helper_handler != handler->access_method) &&
00658        (NULL != handler->next)) {
00659         /*
00660          * always warn if called without our own handler. If we
00661          * have our own handler, use custom bit 1 to only log once.
00662          */
00663         if((sparse_table_helper_handler != handler->access_method) ||
00664            !(handler->flags & MIB_HANDLER_CUSTOM1)) {
00665             snmp_log(LOG_WARNING, "handler (%s) registered after sparse table "
00666                      "hander will not be called\n",
00667                      handler->next->handler_name ?
00668                      handler->next->handler_name : "" );
00669             if(sparse_table_helper_handler == handler->access_method)
00670                 handler->flags |= MIB_HANDLER_CUSTOM1;
00671         }
00672     }
00673 
00674     if (reqinfo->mode == MODE_GETNEXT) {
00675         for(request = requests ; request; request = request->next) {
00676             if ((request->requestvb->type == ASN_NULL && request->processed) ||
00677                 request->delegated)
00678                 continue;
00679             if (request->requestvb->type == SNMP_NOSUCHINSTANCE) {
00680                 /*
00681                  * get next skipped this value for this column, we
00682                  * need to keep searching forward 
00683                  */
00684                 DEBUGMSGT(("sparse", "retry for NOSUCHINSTANCE\n"));
00685                 request->requestvb->type = ASN_PRIV_RETRY;
00686             }
00687             if (request->requestvb->type == SNMP_NOSUCHOBJECT ||
00688                 request->requestvb->type == SNMP_ENDOFMIBVIEW) {
00689                 /*
00690                  * get next has completely finished with this column,
00691                  * so we need to try with the next column (if any)
00692                  */
00693                 DEBUGMSGT(("sparse", "retry for NOSUCHOBJECT\n"));
00694                 table_info = netsnmp_extract_table_info(request);
00695                 table_info->colnum = netsnmp_table_next_column(table_info);
00696                 if (0 != table_info->colnum) {
00697                     memcpy(coloid, reginfo->rootoid,
00698                            reginfo->rootoid_len * sizeof(oid));
00699                     coloid[reginfo->rootoid_len]   = 1;   /* table.entry node */
00700                     coloid[reginfo->rootoid_len+1] = table_info->colnum;
00701                     snmp_set_var_objid(request->requestvb,
00702                                        coloid, reginfo->rootoid_len + 2);
00703                     
00704                     request->requestvb->type = ASN_PRIV_RETRY;
00705                 }
00706                 else {
00707                     /*
00708                      * If we don't have column info, reset to null so
00709                      * the agent will move on to the next table.
00710                      */
00711                     request->requestvb->type = ASN_NULL;
00712                 }
00713             }
00714         }
00715     }
00716     return status;
00717 }
00718 
00721 netsnmp_mib_handler *
00722 netsnmp_sparse_table_handler_get(void)
00723 {
00724     return netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
00725                                   sparse_table_helper_handler);
00726 }
00727 
00732 int
00733 netsnmp_sparse_table_register(netsnmp_handler_registration *reginfo,
00734                        netsnmp_table_registration_info *tabreq)
00735 {
00736     netsnmp_inject_handler(reginfo,
00737         netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
00738                                sparse_table_helper_handler));
00739     netsnmp_inject_handler(reginfo, netsnmp_get_table_handler(tabreq));
00740     return netsnmp_register_handler(reginfo);
00741 }
00742 
00743 
00750 int
00751 netsnmp_table_build_result(netsnmp_handler_registration *reginfo,
00752                            netsnmp_request_info *reqinfo,
00753                            netsnmp_table_request_info *table_info,
00754                            u_char type, u_char * result, size_t result_len)
00755 {
00756 
00757     netsnmp_variable_list *var;
00758 
00759     if (!reqinfo || !table_info)
00760         return SNMPERR_GENERR;
00761 
00762     var = reqinfo->requestvb;
00763 
00764     if (var->name != var->name_loc)
00765         free(var->name);
00766     var->name = NULL;
00767 
00768     if (netsnmp_table_build_oid(reginfo, reqinfo, table_info) !=
00769         SNMPERR_SUCCESS)
00770         return SNMPERR_GENERR;
00771 
00772     snmp_set_var_typed_value(var, type, result, result_len);
00773 
00774     return SNMPERR_SUCCESS;
00775 }
00776 
00777 
00783 int
00784 netsnmp_table_build_oid(netsnmp_handler_registration *reginfo,
00785                         netsnmp_request_info *reqinfo,
00786                         netsnmp_table_request_info *table_info)
00787 {
00788     oid             tmpoid[MAX_OID_LEN];
00789     netsnmp_variable_list *var;
00790 
00791     if (!reginfo || !reqinfo || !table_info)
00792         return SNMPERR_GENERR;
00793 
00794     /*
00795      * xxx-rks: inefficent. we do a copy here, then build_oid does it
00796      *          again. either come up with a new utility routine, or
00797      *          do some hijinks here to eliminate extra copy.
00798      *          Probably could make sure all callers have the
00799      *          index & variable list updated, and use
00800      *          netsnmp_table_build_oid_from_index() instead of all this.
00801      */
00802     memcpy(tmpoid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
00803     tmpoid[reginfo->rootoid_len] = 1;   
00804     tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 
00806     var = reqinfo->requestvb;
00807     if (build_oid(&var->name, &var->name_length,
00808                   tmpoid, reginfo->rootoid_len + 2, table_info->indexes)
00809         != SNMPERR_SUCCESS)
00810         return SNMPERR_GENERR;
00811 
00812     return SNMPERR_SUCCESS;
00813 }
00814 
00820 int
00821 netsnmp_table_build_oid_from_index(netsnmp_handler_registration *reginfo,
00822                                    netsnmp_request_info *reqinfo,
00823                                    netsnmp_table_request_info *table_info)
00824 {
00825     oid             tmpoid[MAX_OID_LEN];
00826     netsnmp_variable_list *var;
00827     int             len;
00828 
00829     if (!reginfo || !reqinfo || !table_info)
00830         return SNMPERR_GENERR;
00831 
00832     var = reqinfo->requestvb;
00833     len = reginfo->rootoid_len;
00834     memcpy(tmpoid, reginfo->rootoid, len * sizeof(oid));
00835     tmpoid[len++] = 1;          /* .Entry */
00836     tmpoid[len++] = table_info->colnum; /* .column */
00837     memcpy(&tmpoid[len], table_info->index_oid,
00838            table_info->index_oid_len * sizeof(oid));
00839     len += table_info->index_oid_len;
00840     if (var->name && var->name != var->name_loc)
00841         SNMP_FREE(var->name);
00842     snmp_clone_mem((void **) &var->name, tmpoid, len * sizeof(oid));
00843     var->name_length = len;
00844 
00845     return SNMPERR_SUCCESS;
00846 }
00847 
00849 int
00850 netsnmp_update_variable_list_from_index(netsnmp_table_request_info *tri)
00851 {
00852     if (!tri)
00853         return SNMPERR_GENERR;
00854 
00855     /*
00856      * free any existing allocated memory, then parse oid into varbinds
00857      */
00858     snmp_reset_var_buffers( tri->indexes);
00859 
00860     return parse_oid_indexes(tri->index_oid, tri->index_oid_len,
00861                              tri->indexes);
00862 }
00863 
00865 int
00866 netsnmp_update_indexes_from_variable_list(netsnmp_table_request_info *tri)
00867 {
00868     if (!tri)
00869         return SNMPERR_GENERR;
00870 
00871     return build_oid_noalloc(tri->index_oid, sizeof(tri->index_oid),
00872                              &tri->index_oid_len, NULL, 0, tri->indexes);
00873 }
00874 
00883 int
00884 netsnmp_check_getnext_reply(netsnmp_request_info *request,
00885                             oid * prefix,
00886                             size_t prefix_len,
00887                             netsnmp_variable_list * newvar,
00888                             netsnmp_variable_list ** outvar)
00889 {
00890     oid      myname[MAX_OID_LEN];
00891     size_t   myname_len;
00892 
00893     build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
00894                       prefix, prefix_len, newvar);
00895     /*
00896      * is the build of the new indexes less than our current result 
00897      */
00898     if ((!(*outvar) || snmp_oid_compare(myname + prefix_len,
00899                                         myname_len - prefix_len,
00900                                         (*outvar)->name + prefix_len,
00901                                         (*outvar)->name_length -
00902                                         prefix_len) < 0)) {
00903         /*
00904          * and greater than the requested oid 
00905          */
00906         if (snmp_oid_compare(myname, myname_len,
00907                              request->requestvb->name,
00908                              request->requestvb->name_length) > 0) {
00909             /*
00910              * the new result must be better than the old 
00911              */
00912 #ifdef ONLY_WORKS_WITH_ONE_VARBIND
00913             if (!*outvar)
00914                 *outvar = snmp_clone_varbind(newvar);
00915             else
00916                 /* 
00917                  * TODO: walk the full varbind list, setting
00918                  *       *all* the values - not just the first.
00919                  */
00920                 snmp_set_var_typed_value(*outvar, newvar->type,
00921                                 newvar->val.string, newvar->val_len);
00922 #else  /* Interim replacement approach - less efficient, but it works! */
00923             if (*outvar)
00924                 snmp_free_varbind(*outvar);
00925             *outvar = snmp_clone_varbind(newvar);
00926 #endif
00927             snmp_set_var_objid(*outvar, myname, myname_len);
00928 
00929             return 1;
00930         }
00931     }
00932     return 0;
00933 }
00934 
00937 /*
00938  * internal routines 
00939  */
00940 void
00941 table_data_free_func(void *data)
00942 {
00943     netsnmp_table_request_info *info = (netsnmp_table_request_info *) data;
00944     if (!info)
00945         return;
00946     snmp_free_varbind(info->indexes);
00947     free(info);
00948 }
00949 
00950 
00951 
00952 static void
00953 table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
00954                      netsnmp_request_info *request, int status)
00955 {
00956     netsnmp_set_request_error(reqinfo, request, status);
00957     netsnmp_free_request_data_sets(request);
00958     if (!request)
00959         return;
00960     request->parent_data = NULL;
00961 }
00962 
00963 
00964 /*
00965  * find the closest column to current (which may be current).
00966  *
00967  * called when a table runs out of rows for column X. This
00968  * function is called with current = X + 1, to verify that
00969  * X + 1 is a valid column, or find the next closest column if not.
00970  *
00971  * All list types should be sorted, lowest to highest.
00972  */
00973 unsigned int
00974 netsnmp_closest_column(unsigned int current,
00975                        netsnmp_column_info *valid_columns)
00976 {
00977     unsigned int    closest = 0;
00978     int             idx;
00979 
00980     if (valid_columns == NULL)
00981         return 0;
00982 
00983     for( ; valid_columns; valid_columns = valid_columns->next) {
00984 
00985         if (valid_columns->isRange) {
00986             /*
00987              * if current < low range, it might be closest.
00988              * otherwise, if it's < high range, current is in
00989              * the range, and thus is an exact match.
00990              */
00991             if (current < valid_columns->details.range[0]) {
00992                 if ( (valid_columns->details.range[0] < closest) ||
00993                      (0 == closest)) {
00994                     closest = valid_columns->details.range[0];
00995                 }
00996             } else if (current <= valid_columns->details.range[1]) {
00997                 closest = current;
00998                 break;       /* can not get any closer! */
00999             }
01000 
01001         } /* range */
01002         else {                  /* list */
01003             /*
01004              * if current < first item, no need to iterate over list.
01005              * that item is either closest, or not.
01006              */
01007             if (current < valid_columns->details.list[0]) {
01008                 if ((valid_columns->details.list[0] < closest) ||
01009                     (0 == closest))
01010                     closest = valid_columns->details.list[0];
01011                 continue;
01012             }
01013 
01015             if (current >
01016                 valid_columns->details.list[(int)valid_columns->list_count - 1])
01017                 continue;       /* not in list range. */
01018 
01020             for (idx = 0; valid_columns->details.list[idx] < current; ++idx)
01021                 ;
01022             
01024             if (current == valid_columns->details.list[idx]) {
01025                 closest = current;
01026                 break;      /* can not get any closer! */
01027             }
01028             
01030             if ((valid_columns->details.list[idx] < closest) ||
01031                 (0 == closest))
01032                 closest = valid_columns->details.list[idx];
01033 
01034         }                       /* list */
01035     }                           /* for */
01036 
01037     return closest;
01038 }
01039 
01059 void
01060 #if HAVE_STDARG_H
01061 netsnmp_table_helper_add_indexes(netsnmp_table_registration_info *tinfo,
01062                                  ...)
01063 #else
01064 netsnmp_table_helper_add_indexes(va_alist)
01065      va_dcl
01066 #endif
01067 {
01068     va_list         debugargs;
01069     int             type;
01070 
01071 #if HAVE_STDARG_H
01072     va_start(debugargs, tinfo);
01073 #else
01074     netsnmp_table_registration_info *tinfo;
01075 
01076     va_start(debugargs);
01077     tinfo = va_arg(debugargs, netsnmp_table_registration_info *);
01078 #endif
01079 
01080     while ((type = va_arg(debugargs, int)) != 0) {
01081         netsnmp_table_helper_add_index(tinfo, type);
01082     }
01083 
01084     va_end(debugargs);
01085 }
01086 
01087 static void
01088 _row_stash_data_list_free(void *ptr) {
01089     netsnmp_oid_stash_node **tmp = (netsnmp_oid_stash_node **)ptr;
01090     netsnmp_oid_stash_free(tmp, NULL);
01091     free(ptr);
01092 }
01093 
01096 netsnmp_oid_stash_node **
01097 netsnmp_table_get_or_create_row_stash(netsnmp_agent_request_info *reqinfo,
01098                                       const u_char * storage_name)
01099 {
01100     netsnmp_oid_stash_node **stashp = NULL;
01101     stashp = (netsnmp_oid_stash_node **)
01102         netsnmp_agent_get_list_data(reqinfo, storage_name);
01103 
01104     if (!stashp) {
01105         /*
01106          * hasn't be created yet.  we create it here. 
01107          */
01108         stashp = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node *);
01109 
01110         if (!stashp)
01111             return NULL;        /* ack. out of mem */
01112 
01113         netsnmp_agent_add_list_data(reqinfo,
01114                                     netsnmp_create_data_list(storage_name,
01115                                                              stashp,
01116                                                              _row_stash_data_list_free));
01117     }
01118     return stashp;
01119 }
01120 
01121 /*
01122  * advance the table info colnum to the next column, or 0 if there are no more
01123  *
01124  * @return new column, or 0 if there are no more
01125  */
01126 unsigned int
01127 netsnmp_table_next_column(netsnmp_table_request_info *table_info)
01128 {
01129     if (NULL == table_info)
01130         return 0;
01131 
01132     /*
01133      * try and validate next column
01134      */
01135     if (table_info->reg_info->valid_columns)
01136         return netsnmp_closest_column(table_info->colnum + 1,
01137                                       table_info->reg_info->valid_columns);
01138     
01139     /*
01140      * can't validate. assume 1..max_column are valid
01141      */
01142     if (table_info->colnum < table_info->reg_info->max_column)
01143         return table_info->colnum + 1;
01144     
01145     return 0; /* out of range */
01146 }