Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

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

Generated on Fri Dec 30 13:47:50 2005 for net-snmp by  doxygen 1.3.9.1

Valid CSS!


Last modified: Thursday, 01-Mar-2007 16:20:00 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.