net-snmp
5.4.1
|
00001 /* 00002 * table_container.c 00003 * $Id: table_container.c 14169 2006-01-25 16:28:12Z dts12 $ 00004 */ 00005 00006 #include <net-snmp/net-snmp-config.h> 00007 00008 #if HAVE_STRING_H 00009 #include <string.h> 00010 #else 00011 #include <strings.h> 00012 #endif 00013 00014 #include <net-snmp/net-snmp-includes.h> 00015 #include <net-snmp/agent/net-snmp-agent-includes.h> 00016 00017 #include <net-snmp/agent/table.h> 00018 #include <net-snmp/agent/table_container.h> 00019 #include <net-snmp/library/container.h> 00020 #include <net-snmp/library/snmp_assert.h> 00021 00022 /* 00023 * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN -1 00024 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1 0 00025 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2 1 00026 * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION 2 00027 * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT 3 00028 * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE 4 00029 * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO 5 00030 */ 00031 00032 /* 00033 * PRIVATE structure for holding important info for each table. 00034 */ 00035 typedef struct container_table_data_s { 00036 00038 netsnmp_table_registration_info *tblreg_info; 00039 00041 netsnmp_container *table; 00042 00043 /* 00044 * mutex_type lock; 00045 */ 00046 00047 /* what type of key do we want? */ 00048 char key_type; 00049 00050 } container_table_data; 00051 00135 static int 00136 _container_table_handler(netsnmp_mib_handler *handler, 00137 netsnmp_handler_registration *reginfo, 00138 netsnmp_agent_request_info *agtreq_info, 00139 netsnmp_request_info *requests); 00140 00141 static void * 00142 _find_next_row(netsnmp_container *c, 00143 netsnmp_table_request_info *tblreq, 00144 void * key); 00145 00146 /********************************************************************** 00147 ********************************************************************** 00148 * * 00149 * * 00150 * PUBLIC Registration functions * 00151 * * 00152 * * 00153 ********************************************************************** 00154 **********************************************************************/ 00155 00156 /* ================================== 00157 * 00158 * Container Table API: Table maintenance 00159 * 00160 * ================================== */ 00161 00162 container_table_data * 00163 netsnmp_tcontainer_create_table( const char *name, 00164 netsnmp_container *container, long flags ) 00165 { 00166 container_table_data *table; 00167 00168 table = SNMP_MALLOC_TYPEDEF(container_table_data); 00169 if (!table) 00170 return NULL; 00171 if (container) 00172 table->table = container; 00173 else { 00174 table->table = netsnmp_container_find("table_container"); 00175 if (!table->table) { 00176 SNMP_FREE(table); 00177 return NULL; 00178 } 00179 } 00180 00181 if (flags) 00182 table->key_type = flags & 0x03; /* Use lowest two bits */ 00183 else 00184 table->key_type = TABLE_CONTAINER_KEY_NETSNMP_INDEX; 00185 00186 if (!table->table->compare) 00187 table->table->compare = netsnmp_compare_netsnmp_index; 00188 if (!table->table->ncompare) 00189 table->table->ncompare = netsnmp_ncompare_netsnmp_index; 00190 00191 return table; 00192 } 00193 00194 void 00195 netsnmp_tcontainer_delete_table( container_table_data *table ) 00196 { 00197 if (!table) 00198 return; 00199 00200 if (table->table) 00201 CONTAINER_FREE(table->table); 00202 00203 SNMP_FREE(table); 00204 return; 00205 } 00206 00207 /* 00208 * The various standalone row operation routines 00209 * (create/clone/copy/delete) 00210 * will be specific to a particular table, 00211 * so can't be implemented here. 00212 */ 00213 00214 int 00215 netsnmp_tcontainer_add_row( container_table_data *table, netsnmp_index *row ) 00216 { 00217 if (!table || !table->table || !row) 00218 return -1; 00219 CONTAINER_INSERT( table->table, row ); 00220 return 0; 00221 } 00222 00223 netsnmp_index * 00224 netsnmp_tcontainer_remove_row( container_table_data *table, netsnmp_index *row ) 00225 { 00226 if (!table || !table->table || !row) 00227 return NULL; 00228 CONTAINER_REMOVE( table->table, row ); 00229 return NULL; 00230 } 00231 00232 int 00233 netsnmp_tcontainer_replace_row( container_table_data *table, 00234 netsnmp_index *old_row, netsnmp_index *new_row ) 00235 { 00236 if (!table || !table->table || !old_row || !new_row) 00237 return -1; 00238 netsnmp_tcontainer_remove_row( table, old_row ); 00239 netsnmp_tcontainer_add_row( table, new_row ); 00240 return 0; 00241 } 00242 00243 /* netsnmp_tcontainer_remove_delete_row() will be table-specific too */ 00244 00245 00246 /* ================================== 00247 * 00248 * Container Table API: MIB maintenance 00249 * 00250 * ================================== */ 00251 00253 netsnmp_mib_handler * 00254 netsnmp_container_table_handler_get(netsnmp_table_registration_info *tabreg, 00255 netsnmp_container *container, char key_type) 00256 { 00257 container_table_data *tad; 00258 netsnmp_mib_handler *handler; 00259 00260 if (NULL == tabreg) { 00261 snmp_log(LOG_ERR, "bad param in netsnmp_container_table_register\n"); 00262 return NULL; 00263 } 00264 00265 tad = SNMP_MALLOC_TYPEDEF(container_table_data); 00266 handler = netsnmp_create_handler("table_container", 00267 _container_table_handler); 00268 if((NULL == tad) || (NULL == handler)) { 00269 if(tad) free(tad); /* SNMP_FREE wasted on locals */ 00270 if(handler) free(handler); /* SNMP_FREE wasted on locals */ 00271 snmp_log(LOG_ERR, 00272 "malloc failure in netsnmp_container_table_register\n"); 00273 return NULL; 00274 } 00275 00276 tad->tblreg_info = tabreg; /* we need it too, but it really is not ours */ 00277 if(key_type) 00278 tad->key_type = key_type; 00279 else 00280 tad->key_type = TABLE_CONTAINER_KEY_NETSNMP_INDEX; 00281 00282 if(NULL == container) 00283 container = netsnmp_container_find("table_container"); 00284 tad->table = container; 00285 00286 if (NULL==container->compare) 00287 container->compare = netsnmp_compare_netsnmp_index; 00288 if (NULL==container->ncompare) 00289 container->ncompare = netsnmp_ncompare_netsnmp_index; 00290 00291 handler->myvoid = (void*)tad; 00292 handler->flags |= MIB_HANDLER_AUTO_NEXT; 00293 00294 return handler; 00295 } 00296 00297 int 00298 netsnmp_container_table_register(netsnmp_handler_registration *reginfo, 00299 netsnmp_table_registration_info *tabreg, 00300 netsnmp_container *container, char key_type ) 00301 { 00302 netsnmp_mib_handler *handler; 00303 00304 if ((NULL == reginfo) || (NULL == reginfo->handler) || (NULL == tabreg)) { 00305 snmp_log(LOG_ERR, "bad param in netsnmp_container_table_register\n"); 00306 return SNMPERR_GENERR; 00307 } 00308 00309 if (NULL==container) 00310 container = netsnmp_container_find(reginfo->handlerName); 00311 00312 handler = netsnmp_container_table_handler_get(tabreg, container, key_type); 00313 netsnmp_inject_handler(reginfo, handler ); 00314 00315 return netsnmp_register_table(reginfo, tabreg); 00316 } 00317 00319 netsnmp_container* 00320 netsnmp_container_table_container_extract(netsnmp_request_info *request) 00321 { 00322 return (netsnmp_container *) 00323 netsnmp_request_get_list_data(request, TABLE_CONTAINER_CONTAINER); 00324 } 00325 00326 #ifndef NETSNMP_USE_INLINE 00327 00328 void * 00329 netsnmp_container_table_row_extract(netsnmp_request_info *request) 00330 { 00331 /* 00332 * NOTE: this function must match in table_container.c and table_container.h. 00333 * if you change one, change them both! 00334 */ 00335 return netsnmp_request_get_list_data(request, TABLE_CONTAINER_ROW); 00336 } 00338 void * 00339 netsnmp_container_table_extract_context(netsnmp_request_info *request) 00340 { 00341 /* 00342 * NOTE: this function must match in table_container.c and table_container.h. 00343 * if you change one, change them both! 00344 */ 00345 return netsnmp_request_get_list_data(request, TABLE_CONTAINER_ROW); 00346 } 00347 #endif /* inline */ 00348 00350 void 00351 netsnmp_container_table_row_insert(netsnmp_request_info *request, 00352 netsnmp_index *row) 00353 { 00354 netsnmp_request_info *req; 00355 netsnmp_table_request_info *table_info = NULL; 00356 netsnmp_variable_list *this_index = NULL; 00357 netsnmp_variable_list *that_index = NULL; 00358 oid base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */ 00359 oid this_oid[MAX_OID_LEN]; 00360 oid that_oid[MAX_OID_LEN]; 00361 size_t this_oid_len, that_oid_len; 00362 00363 if (!request) 00364 return; 00365 00366 /* 00367 * We'll add the new row information to any request 00368 * structure with the same index values as the request 00369 * passed in (which includes that one!). 00370 * 00371 * So construct an OID based on these index values. 00372 */ 00373 00374 table_info = netsnmp_extract_table_info(request); 00375 this_index = table_info->indexes; 00376 build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len, 00377 base_oid, 2, this_index); 00378 00379 /* 00380 * We need to look through the whole of the request list 00381 * (as received by the current handler), as there's no 00382 * guarantee that this routine will be called by the first 00383 * varbind that refers to this row. 00384 * In particular, a RowStatus controlled row creation 00385 * may easily occur later in the variable list. 00386 * 00387 * So first, we rewind to the head of the list.... 00388 */ 00389 for (req=request; req->prev; req=req->prev) 00390 ; 00391 00392 /* 00393 * ... and then start looking for matching indexes 00394 * (by constructing OIDs from these index values) 00395 */ 00396 for (; req; req=req->next) { 00397 if (req->processed) 00398 continue; 00399 00400 table_info = netsnmp_extract_table_info(req); 00401 that_index = table_info->indexes; 00402 build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len, 00403 base_oid, 2, that_index); 00404 00405 /* 00406 * This request has the same index values, 00407 * so add the newly-created row information. 00408 */ 00409 if (snmp_oid_compare(this_oid, this_oid_len, 00410 that_oid, that_oid_len) == 0) { 00411 netsnmp_request_add_list_data(req, 00412 netsnmp_create_data_list(TABLE_CONTAINER_ROW, row, NULL)); 00413 } 00414 } 00415 } 00416 00418 /********************************************************************** 00419 ********************************************************************** 00420 * * 00421 * * 00422 * DATA LOOKUP functions * 00423 * * 00424 * * 00425 ********************************************************************** 00426 **********************************************************************/ 00427 NETSNMP_STATIC_INLINE void 00428 _set_key( container_table_data * tad, netsnmp_request_info *request, 00429 netsnmp_table_request_info *tblreq_info, 00430 void **key, netsnmp_index *index ) 00431 { 00432 if (TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) { 00433 index->oids = tblreq_info->index_oid; 00434 index->len = tblreq_info->index_oid_len; 00435 *key = index; 00436 } 00437 else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) { 00438 *key = tblreq_info->indexes; 00439 } 00440 #if 0 00441 else if (TABLE_CONTAINER_KEY_VARBIND_RAW == tad->key_type) { 00442 *key = request->requestvb; 00443 } 00444 #endif 00445 else 00446 *key = NULL; 00447 } 00448 00449 00450 NETSNMP_STATIC_INLINE void 00451 _data_lookup(netsnmp_handler_registration *reginfo, 00452 netsnmp_agent_request_info *agtreq_info, 00453 netsnmp_request_info *request, container_table_data * tad) 00454 { 00455 netsnmp_index *row = NULL; 00456 netsnmp_table_request_info *tblreq_info; 00457 netsnmp_variable_list *var; 00458 netsnmp_index index; 00459 void *key; 00460 00461 var = request->requestvb; 00462 00463 DEBUGIF("table_container") { 00464 DEBUGMSGTL(("table_container", " data_lookup oid:")); 00465 DEBUGMSGOID(("table_container", var->name, var->name_length)); 00466 DEBUGMSG(("table_container", "\n")); 00467 } 00468 00469 /* 00470 * Get pointer to the table information for this request. This 00471 * information was saved by table_helper_handler. 00472 */ 00473 tblreq_info = netsnmp_extract_table_info(request); 00475 netsnmp_assert((NULL != tblreq_info) && 00476 (tblreq_info->colnum <= tad->tblreg_info->max_column)); 00477 00478 if ((agtreq_info->mode == MODE_GETNEXT) || 00479 (agtreq_info->mode == MODE_GETBULK)) { 00480 /* 00481 * find the row. This will automatically move to the next 00482 * column, if necessary. 00483 */ 00484 _set_key( tad, request, tblreq_info, &key, &index ); 00485 row = _find_next_row(tad->table, tblreq_info, key); 00486 if (row) { 00487 /* 00488 * update indexes in tblreq_info (index & varbind), 00489 * then update request varbind oid 00490 */ 00491 if(TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) { 00492 tblreq_info->index_oid_len = row->len; 00493 memcpy(tblreq_info->index_oid, row->oids, 00494 row->len * sizeof(oid)); 00495 netsnmp_update_variable_list_from_index(tblreq_info); 00496 } 00497 else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) { 00500 netsnmp_update_indexes_from_variable_list(tblreq_info); 00501 } 00502 00503 if (TABLE_CONTAINER_KEY_VARBIND_RAW != tad->key_type) { 00504 netsnmp_table_build_oid_from_index(reginfo, request, 00505 tblreq_info); 00506 } 00507 } 00508 else { 00509 /* 00510 * no results found. Flag the request so lower handlers will 00511 * ignore it, but it is not an error - getnext will move 00512 * on to another handler to process this request. 00513 */ 00514 netsnmp_set_request_error(agtreq_info, request, SNMP_ENDOFMIBVIEW); 00515 DEBUGMSGTL(("table_container", "no row found\n")); 00516 } 00517 } 00518 else { 00519 00520 _set_key( tad, request, tblreq_info, &key, &index ); 00521 row = CONTAINER_FIND(tad->table, key); 00522 if (NULL == row) { 00523 /* 00524 * not results found. For a get, that is an error 00525 */ 00526 DEBUGMSGTL(("table_container", "no row found\n")); 00527 if((agtreq_info->mode != MODE_SET_RESERVE1) || /* get */ 00528 (reginfo->modes & HANDLER_CAN_NOT_CREATE)) { /* no create */ 00529 netsnmp_set_request_error(agtreq_info, request, 00530 SNMP_NOSUCHINSTANCE); 00531 } 00532 } 00533 } 00535 /* 00536 * save the data and table in the request. 00537 */ 00538 if (SNMP_ENDOFMIBVIEW != request->requestvb->type) { 00539 if (NULL != row) 00540 netsnmp_request_add_list_data(request, 00541 netsnmp_create_data_list 00542 (TABLE_CONTAINER_ROW, 00543 row, NULL)); 00544 netsnmp_request_add_list_data(request, 00545 netsnmp_create_data_list 00546 (TABLE_CONTAINER_CONTAINER, 00547 tad->table, NULL)); 00548 } 00549 } 00550 00551 /********************************************************************** 00552 ********************************************************************** 00553 * * 00554 * * 00555 * netsnmp_table_container_helper_handler() * 00556 * * 00557 * * 00558 ********************************************************************** 00559 **********************************************************************/ 00560 static int 00561 _container_table_handler(netsnmp_mib_handler *handler, 00562 netsnmp_handler_registration *reginfo, 00563 netsnmp_agent_request_info *agtreq_info, 00564 netsnmp_request_info *requests) 00565 { 00566 int rc = SNMP_ERR_NOERROR; 00567 int oldmode, need_processing = 0; 00568 container_table_data *tad; 00569 00571 netsnmp_assert((NULL != handler) && (NULL != handler->myvoid)); 00572 netsnmp_assert((NULL != reginfo) && (NULL != agtreq_info)); 00573 00574 DEBUGMSGTL(("table_container", "Mode %s, Got request:\n", 00575 se_find_label_in_slist("agent_mode",agtreq_info->mode))); 00576 00577 /* 00578 * First off, get our pointer from the handler. This 00579 * lets us get to the table registration information we 00580 * saved in get_table_container_handler(), as well as the 00581 * container where the actual table data is stored. 00582 */ 00583 tad = (container_table_data *)handler->myvoid; 00584 00585 /* 00586 * only do data lookup for first pass 00587 * 00588 * xxx-rks: this should really be handled up one level. we should 00589 * be able to say what modes we want to be called for during table 00590 * registration. 00591 */ 00592 oldmode = agtreq_info->mode; 00593 if(MODE_IS_GET(oldmode) || (MODE_SET_RESERVE1 == oldmode)) { 00594 netsnmp_request_info *curr_request; 00595 /* 00596 * Loop through each of the requests, and 00597 * try to find the appropriate row from the container. 00598 */ 00599 for (curr_request = requests; curr_request; curr_request = curr_request->next) { 00600 /* 00601 * skip anything that doesn't need processing. 00602 */ 00603 if (curr_request->processed != 0) { 00604 DEBUGMSGTL(("table_container", "already processed\n")); 00605 continue; 00606 } 00607 00608 /* 00609 * find data for this request 00610 */ 00611 _data_lookup(reginfo, agtreq_info, curr_request, tad); 00612 00613 if(curr_request->processed) 00614 continue; 00615 00616 ++need_processing; 00617 } 00618 } 00619 00620 /* 00621 * send GET instead of GETNEXT to sub-handlers 00622 * xxx-rks: again, this should be handled further up. 00623 */ 00624 if ((oldmode == MODE_GETNEXT) && (handler->next)) { 00625 /* 00626 * tell agent handlder not to auto call next handler 00627 */ 00628 handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; 00629 00630 /* 00631 * if we found rows to process, pretend to be a get request 00632 * and call handler below us. 00633 */ 00634 if(need_processing > 0) { 00635 agtreq_info->mode = MODE_GET; 00636 rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, 00637 requests); 00638 if (rc != SNMP_ERR_NOERROR) { 00639 DEBUGMSGTL(("table_container", 00640 "next handler returned %d\n", rc)); 00641 } 00642 00643 agtreq_info->mode = oldmode; /* restore saved mode */ 00644 } 00645 } 00646 00647 return rc; 00648 } 00652 /* ================================== 00653 * 00654 * Container Table API: Row operations 00655 * 00656 * ================================== */ 00657 00658 static void * 00659 _find_next_row(netsnmp_container *c, 00660 netsnmp_table_request_info *tblreq, 00661 void * key) 00662 { 00663 void *row = NULL; 00664 00665 if (!c || !tblreq || !tblreq->reg_info ) { 00666 snmp_log(LOG_ERR,"_find_next_row param error\n"); 00667 return NULL; 00668 } 00669 00670 /* 00671 * table helper should have made sure we aren't below our minimum column 00672 */ 00673 netsnmp_assert(tblreq->colnum >= tblreq->reg_info->min_column); 00674 00675 /* 00676 * if no indexes then use first row. 00677 */ 00678 if(tblreq->number_indexes == 0) { 00679 row = CONTAINER_FIRST(c); 00680 } else { 00681 00682 if(NULL == key) { 00683 netsnmp_index index; 00684 index.oids = tblreq->index_oid; 00685 index.len = tblreq->index_oid_len; 00686 row = CONTAINER_NEXT(c, &index); 00687 } 00688 else 00689 row = CONTAINER_NEXT(c, key); 00690 00691 /* 00692 * we don't have a row, but we might be at the end of a 00693 * column, so try the next column. 00694 */ 00695 if (NULL == row) { 00696 /* 00697 * don't set tblreq next_col unless we know there is one, 00698 * so we don't mess up table handler sparse table processing. 00699 */ 00700 oid next_col = netsnmp_table_next_column(tblreq); 00701 if (0 != next_col) { 00702 tblreq->colnum = next_col; 00703 row = CONTAINER_FIRST(c); 00704 } 00705 } 00706 } 00707 00708 return row; 00709 } 00710 00720 netsnmp_index * 00721 netsnmp_table_index_find_next_row(netsnmp_container *c, 00722 netsnmp_table_request_info *tblreq) 00723 { 00724 return _find_next_row(c, tblreq, NULL ); 00725 } 00726 00727 /* ================================== 00728 * 00729 * Container Table API: Index operations 00730 * 00731 * ================================== */ 00732