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