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 }
1.3.9.1
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.