diff options
Diffstat (limited to 'agent/helpers/table_container.c')
-rw-r--r-- | agent/helpers/table_container.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/agent/helpers/table_container.c b/agent/helpers/table_container.c new file mode 100644 index 0000000..acb698c --- /dev/null +++ b/agent/helpers/table_container.c @@ -0,0 +1,733 @@ +/* + * table_container.c + * $Id: table_container.c 14169 2006-01-25 16:28:12Z dts12 $ + */ + +#include <net-snmp/net-snmp-config.h> + +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include <net-snmp/agent/table.h> +#include <net-snmp/agent/table_container.h> +#include <net-snmp/library/container.h> +#include <net-snmp/library/snmp_assert.h> + +/* + * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN -1 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1 0 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2 1 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION 2 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT 3 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE 4 + * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO 5 + */ + +/* + * PRIVATE structure for holding important info for each table. + */ +typedef struct container_table_data_s { + + /** registration info for the table */ + netsnmp_table_registration_info *tblreg_info; + + /** container for the table rows */ + netsnmp_container *table; + + /* + * mutex_type lock; + */ + + /* what type of key do we want? */ + char key_type; + +} container_table_data; + +/** @defgroup table_container table_container + * Helps you implement a table when data can be found via a netsnmp_container. + * @ingroup table + * + * The table_container handler is used (automatically) in conjuntion + * with the @link table table@endlink handler. + * + * This handler will use the index information provided by + * the @link table @endlink handler to find the row needed to process + * the request. + * + * The container must use one of 3 key types. It is the sub-handler's + * responsibility to ensure that the container and key type match (unless + * neither is specified, in which case a default will be used.) + * + * The current key types are: + * + * TABLE_CONTAINER_KEY_NETSNMP_INDEX + * The container should do comparisons based on a key that may be cast + * to a netsnmp index (netsnmp_index *). This index contains only the + * index portion of the OID, not the entire OID. + * + * TABLE_CONTAINER_KEY_VARBIND_INDEX + * The container should do comparisons based on a key that may be cast + * to a netsnmp variable list (netsnmp_variable_list *). This variable + * list will contain one varbind for each index component. + * + * TABLE_CONTAINER_KEY_VARBIND_RAW (NOTE: unimplemented) + * While not yet implemented, future plans include passing the request + * varbind with the full OID to a container. + * + * If a key type is not specified at registration time, the default key type + * of TABLE_CONTAINER_KEY_NETSNMP_INDEX will be used. If a container is + * provided, or the handler name is aliased to a container type, the container + * must use a netsnmp index. + * + * If no container is provided, a lookup will be made based on the + * sub-handler's name, or if that isn't found, "table_container". The + * table_container key type will be netsnmp_index. + * + * The container must, at a minimum, implement find and find_next. If a NULL + * key is passed to the container, it must return the first item, if any. + * All containers provided by net-snmp fulfil this requirement. + * + * This handler will only register to process 'data lookup' modes. In + * traditional net-snmp modes, that is any GET-like mode (GET, GET-NEXT, + * GET-BULK) or the first phase of a SET (RESERVE1). In the new baby-steps + * mode, DATA_LOOKUP is it's own mode, and is a pre-cursor to other modes. + * + * When called, the handler will call the appropriate container method + * with the appropriate key type. If a row was not found, the result depends + * on the mode. + * + * GET Processing + * An exact match must be found. If one is not, the error NOSUCHINSTANCE + * is set. + * + * GET-NEXT / GET-BULK + * If no row is found, the column number will be increased (using any + * valid_columns structure that may have been provided), and the first row + * will be retrieved. If no first row is found, the processed flag will be + * set, so that the sub-handler can skip any processing related to the + * request. The agent will notice this unsatisfied request, and attempt to + * pass it to the next appropriate handler. + * + * SET + * If the hander did not register with the HANDLER_CAN_NOT_CREATE flag + * set in the registration modes, it is assumed that this is a row + * creation request and a NULL row is added to the request's data list. + * The sub-handler is responsbile for dealing with any row creation + * contraints and inserting any newly created rows into the container + * and the request's data list. + * + * If a row is found, it will be inserted into + * the request's data list. The sub-handler may retrieve it by calling + * netsnmp_container_table_extract_context(request); * + * NOTE NOTE NOTE: + * + * This helper and it's API are still being tested and are subject to change. + * + * @{ + */ + +static int +_container_table_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests); + +static void * +_find_next_row(netsnmp_container *c, + netsnmp_table_request_info *tblreq, + void * key); + +/********************************************************************** + ********************************************************************** + * * + * * + * PUBLIC Registration functions * + * * + * * + ********************************************************************** + **********************************************************************/ + +/* ================================== + * + * Container Table API: Table maintenance + * + * ================================== */ + +container_table_data * +netsnmp_tcontainer_create_table( const char *name, + netsnmp_container *container, long flags ) +{ + container_table_data *table; + + table = SNMP_MALLOC_TYPEDEF(container_table_data); + if (!table) + return NULL; + if (container) + table->table = container; + else { + table->table = netsnmp_container_find("table_container"); + if (!table->table) { + SNMP_FREE(table); + return NULL; + } + } + + if (flags) + table->key_type = flags & 0x03; /* Use lowest two bits */ + else + table->key_type = TABLE_CONTAINER_KEY_NETSNMP_INDEX; + + if (!table->table->compare) + table->table->compare = netsnmp_compare_netsnmp_index; + if (!table->table->ncompare) + table->table->ncompare = netsnmp_ncompare_netsnmp_index; + + return table; +} + +void +netsnmp_tcontainer_delete_table( container_table_data *table ) +{ + if (!table) + return; + + if (table->table) + CONTAINER_FREE(table->table); + + SNMP_FREE(table); + return; +} + + /* + * The various standalone row operation routines + * (create/clone/copy/delete) + * will be specific to a particular table, + * so can't be implemented here. + */ + +int +netsnmp_tcontainer_add_row( container_table_data *table, netsnmp_index *row ) +{ + if (!table || !table->table || !row) + return -1; + CONTAINER_INSERT( table->table, row ); + return 0; +} + +netsnmp_index * +netsnmp_tcontainer_remove_row( container_table_data *table, netsnmp_index *row ) +{ + if (!table || !table->table || !row) + return NULL; + CONTAINER_REMOVE( table->table, row ); + return NULL; +} + +int +netsnmp_tcontainer_replace_row( container_table_data *table, + netsnmp_index *old_row, netsnmp_index *new_row ) +{ + if (!table || !table->table || !old_row || !new_row) + return -1; + netsnmp_tcontainer_remove_row( table, old_row ); + netsnmp_tcontainer_add_row( table, new_row ); + return 0; +} + + /* netsnmp_tcontainer_remove_delete_row() will be table-specific too */ + + +/* ================================== + * + * Container Table API: MIB maintenance + * + * ================================== */ + +/** returns a netsnmp_mib_handler object for the table_container helper */ +netsnmp_mib_handler * +netsnmp_container_table_handler_get(netsnmp_table_registration_info *tabreg, + netsnmp_container *container, char key_type) +{ + container_table_data *tad; + netsnmp_mib_handler *handler; + + if (NULL == tabreg) { + snmp_log(LOG_ERR, "bad param in netsnmp_container_table_register\n"); + return NULL; + } + + tad = SNMP_MALLOC_TYPEDEF(container_table_data); + handler = netsnmp_create_handler("table_container", + _container_table_handler); + if((NULL == tad) || (NULL == handler)) { + if(tad) free(tad); /* SNMP_FREE wasted on locals */ + if(handler) free(handler); /* SNMP_FREE wasted on locals */ + snmp_log(LOG_ERR, + "malloc failure in netsnmp_container_table_register\n"); + return NULL; + } + + tad->tblreg_info = tabreg; /* we need it too, but it really is not ours */ + if(key_type) + tad->key_type = key_type; + else + tad->key_type = TABLE_CONTAINER_KEY_NETSNMP_INDEX; + + if(NULL == container) + container = netsnmp_container_find("table_container"); + tad->table = container; + + if (NULL==container->compare) + container->compare = netsnmp_compare_netsnmp_index; + if (NULL==container->ncompare) + container->ncompare = netsnmp_ncompare_netsnmp_index; + + handler->myvoid = (void*)tad; + handler->flags |= MIB_HANDLER_AUTO_NEXT; + + return handler; +} + +int +netsnmp_container_table_register(netsnmp_handler_registration *reginfo, + netsnmp_table_registration_info *tabreg, + netsnmp_container *container, char key_type ) +{ + netsnmp_mib_handler *handler; + + if ((NULL == reginfo) || (NULL == reginfo->handler) || (NULL == tabreg)) { + snmp_log(LOG_ERR, "bad param in netsnmp_container_table_register\n"); + return SNMPERR_GENERR; + } + + if (NULL==container) + container = netsnmp_container_find(reginfo->handlerName); + + handler = netsnmp_container_table_handler_get(tabreg, container, key_type); + netsnmp_inject_handler(reginfo, handler ); + + return netsnmp_register_table(reginfo, tabreg); +} + +/** retrieve the container used by the table_container helper */ +netsnmp_container* +netsnmp_container_table_container_extract(netsnmp_request_info *request) +{ + return (netsnmp_container *) + netsnmp_request_get_list_data(request, TABLE_CONTAINER_CONTAINER); +} + +#ifndef NETSNMP_USE_INLINE +/** find the context data used by the table_container helper */ +void * +netsnmp_container_table_row_extract(netsnmp_request_info *request) +{ + /* + * NOTE: this function must match in table_container.c and table_container.h. + * if you change one, change them both! + */ + return netsnmp_request_get_list_data(request, TABLE_CONTAINER_ROW); +} +/** find the context data used by the table_container helper */ +void * +netsnmp_container_table_extract_context(netsnmp_request_info *request) +{ + /* + * NOTE: this function must match in table_container.c and table_container.h. + * if you change one, change them both! + */ + return netsnmp_request_get_list_data(request, TABLE_CONTAINER_ROW); +} +#endif /* inline */ + +/** inserts a newly created table_container entry into a request list */ +void +netsnmp_container_table_row_insert(netsnmp_request_info *request, + netsnmp_index *row) +{ + netsnmp_request_info *req; + netsnmp_table_request_info *table_info = NULL; + netsnmp_variable_list *this_index = NULL; + netsnmp_variable_list *that_index = NULL; + oid base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */ + oid this_oid[MAX_OID_LEN]; + oid that_oid[MAX_OID_LEN]; + size_t this_oid_len, that_oid_len; + + if (!request) + return; + + /* + * We'll add the new row information to any request + * structure with the same index values as the request + * passed in (which includes that one!). + * + * So construct an OID based on these index values. + */ + + table_info = netsnmp_extract_table_info(request); + this_index = table_info->indexes; + build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len, + base_oid, 2, this_index); + + /* + * We need to look through the whole of the request list + * (as received by the current handler), as there's no + * guarantee that this routine will be called by the first + * varbind that refers to this row. + * In particular, a RowStatus controlled row creation + * may easily occur later in the variable list. + * + * So first, we rewind to the head of the list.... + */ + for (req=request; req->prev; req=req->prev) + ; + + /* + * ... and then start looking for matching indexes + * (by constructing OIDs from these index values) + */ + for (; req; req=req->next) { + if (req->processed) + continue; + + table_info = netsnmp_extract_table_info(req); + that_index = table_info->indexes; + build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len, + base_oid, 2, that_index); + + /* + * This request has the same index values, + * so add the newly-created row information. + */ + if (snmp_oid_compare(this_oid, this_oid_len, + that_oid, that_oid_len) == 0) { + netsnmp_request_add_list_data(req, + netsnmp_create_data_list(TABLE_CONTAINER_ROW, row, NULL)); + } + } +} + +/** @cond */ +/********************************************************************** + ********************************************************************** + * * + * * + * DATA LOOKUP functions * + * * + * * + ********************************************************************** + **********************************************************************/ +NETSNMP_STATIC_INLINE void +_set_key( container_table_data * tad, netsnmp_request_info *request, + netsnmp_table_request_info *tblreq_info, + void **key, netsnmp_index *index ) +{ + if (TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) { + index->oids = tblreq_info->index_oid; + index->len = tblreq_info->index_oid_len; + *key = index; + } + else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) { + *key = tblreq_info->indexes; + } +#if 0 + else if (TABLE_CONTAINER_KEY_VARBIND_RAW == tad->key_type) { + *key = request->requestvb; + } +#endif + else + *key = NULL; +} + + +NETSNMP_STATIC_INLINE void +_data_lookup(netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *request, container_table_data * tad) +{ + netsnmp_index *row = NULL; + netsnmp_table_request_info *tblreq_info; + netsnmp_variable_list *var; + netsnmp_index index; + void *key; + + var = request->requestvb; + + DEBUGIF("table_container") { + DEBUGMSGTL(("table_container", " data_lookup oid:")); + DEBUGMSGOID(("table_container", var->name, var->name_length)); + DEBUGMSG(("table_container", "\n")); + } + + /* + * Get pointer to the table information for this request. This + * information was saved by table_helper_handler. + */ + tblreq_info = netsnmp_extract_table_info(request); + /** the table_helper_handler should enforce column boundaries. */ + netsnmp_assert((NULL != tblreq_info) && + (tblreq_info->colnum <= tad->tblreg_info->max_column)); + + if ((agtreq_info->mode == MODE_GETNEXT) || + (agtreq_info->mode == MODE_GETBULK)) { + /* + * find the row. This will automatically move to the next + * column, if necessary. + */ + _set_key( tad, request, tblreq_info, &key, &index ); + row = _find_next_row(tad->table, tblreq_info, key); + if (row) { + /* + * update indexes in tblreq_info (index & varbind), + * then update request varbind oid + */ + if(TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) { + tblreq_info->index_oid_len = row->len; + memcpy(tblreq_info->index_oid, row->oids, + row->len * sizeof(oid)); + netsnmp_update_variable_list_from_index(tblreq_info); + } + else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) { + /** xxx-rks: shouldn't tblreq_info->indexes be updated + before we call this?? */ + netsnmp_update_indexes_from_variable_list(tblreq_info); + } + + if (TABLE_CONTAINER_KEY_VARBIND_RAW != tad->key_type) { + netsnmp_table_build_oid_from_index(reginfo, request, + tblreq_info); + } + } + else { + /* + * no results found. Flag the request so lower handlers will + * ignore it, but it is not an error - getnext will move + * on to another handler to process this request. + */ + netsnmp_set_request_error(agtreq_info, request, SNMP_ENDOFMIBVIEW); + DEBUGMSGTL(("table_container", "no row found\n")); + } + } /** GETNEXT/GETBULK */ + else { + + _set_key( tad, request, tblreq_info, &key, &index ); + row = CONTAINER_FIND(tad->table, key); + if (NULL == row) { + /* + * not results found. For a get, that is an error + */ + DEBUGMSGTL(("table_container", "no row found\n")); + if((agtreq_info->mode != MODE_SET_RESERVE1) || /* get */ + (reginfo->modes & HANDLER_CAN_NOT_CREATE)) { /* no create */ + netsnmp_set_request_error(agtreq_info, request, + SNMP_NOSUCHINSTANCE); + } + } + } /** GET/SET */ + + /* + * save the data and table in the request. + */ + if (SNMP_ENDOFMIBVIEW != request->requestvb->type) { + if (NULL != row) + netsnmp_request_add_list_data(request, + netsnmp_create_data_list + (TABLE_CONTAINER_ROW, + row, NULL)); + netsnmp_request_add_list_data(request, + netsnmp_create_data_list + (TABLE_CONTAINER_CONTAINER, + tad->table, NULL)); + } +} + +/********************************************************************** + ********************************************************************** + * * + * * + * netsnmp_table_container_helper_handler() * + * * + * * + ********************************************************************** + **********************************************************************/ +static int +_container_table_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc = SNMP_ERR_NOERROR; + int oldmode, need_processing = 0; + container_table_data *tad; + + /** sanity checks */ + netsnmp_assert((NULL != handler) && (NULL != handler->myvoid)); + netsnmp_assert((NULL != reginfo) && (NULL != agtreq_info)); + + DEBUGMSGTL(("table_container", "Mode %s, Got request:\n", + se_find_label_in_slist("agent_mode",agtreq_info->mode))); + + /* + * First off, get our pointer from the handler. This + * lets us get to the table registration information we + * saved in get_table_container_handler(), as well as the + * container where the actual table data is stored. + */ + tad = (container_table_data *)handler->myvoid; + + /* + * only do data lookup for first pass + * + * xxx-rks: this should really be handled up one level. we should + * be able to say what modes we want to be called for during table + * registration. + */ + oldmode = agtreq_info->mode; + if(MODE_IS_GET(oldmode) || (MODE_SET_RESERVE1 == oldmode)) { + netsnmp_request_info *curr_request; + /* + * Loop through each of the requests, and + * try to find the appropriate row from the container. + */ + for (curr_request = requests; curr_request; curr_request = curr_request->next) { + /* + * skip anything that doesn't need processing. + */ + if (curr_request->processed != 0) { + DEBUGMSGTL(("table_container", "already processed\n")); + continue; + } + + /* + * find data for this request + */ + _data_lookup(reginfo, agtreq_info, curr_request, tad); + + if(curr_request->processed) + continue; + + ++need_processing; + } /** for ( ... requests ... ) */ + } + + /* + * send GET instead of GETNEXT to sub-handlers + * xxx-rks: again, this should be handled further up. + */ + if ((oldmode == MODE_GETNEXT) && (handler->next)) { + /* + * tell agent handlder not to auto call next handler + */ + handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; + + /* + * if we found rows to process, pretend to be a get request + * and call handler below us. + */ + if(need_processing > 0) { + agtreq_info->mode = MODE_GET; + rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, + requests); + if (rc != SNMP_ERR_NOERROR) { + DEBUGMSGTL(("table_container", + "next handler returned %d\n", rc)); + } + + agtreq_info->mode = oldmode; /* restore saved mode */ + } + } + + return rc; +} +/** @endcond */ + + +/* ================================== + * + * Container Table API: Row operations + * + * ================================== */ + +static void * +_find_next_row(netsnmp_container *c, + netsnmp_table_request_info *tblreq, + void * key) +{ + void *row = NULL; + + if (!c || !tblreq || !tblreq->reg_info ) { + snmp_log(LOG_ERR,"_find_next_row param error\n"); + return NULL; + } + + /* + * table helper should have made sure we aren't below our minimum column + */ + netsnmp_assert(tblreq->colnum >= tblreq->reg_info->min_column); + + /* + * if no indexes then use first row. + */ + if(tblreq->number_indexes == 0) { + row = CONTAINER_FIRST(c); + } else { + + if(NULL == key) { + netsnmp_index index; + index.oids = tblreq->index_oid; + index.len = tblreq->index_oid_len; + row = CONTAINER_NEXT(c, &index); + } + else + row = CONTAINER_NEXT(c, key); + + /* + * we don't have a row, but we might be at the end of a + * column, so try the next column. + */ + if (NULL == row) { + /* + * don't set tblreq next_col unless we know there is one, + * so we don't mess up table handler sparse table processing. + */ + oid next_col = netsnmp_table_next_column(tblreq); + if (0 != next_col) { + tblreq->colnum = next_col; + row = CONTAINER_FIRST(c); + } + } + } + + return row; +} + +/** + * deprecated, backwards compatability only + * + * expected impact to remove: none + * - used between helpers, shouldn't have been used by end users + * + * replacement: none + * - never should have been a public method in the first place + */ +netsnmp_index * +netsnmp_table_index_find_next_row(netsnmp_container *c, + netsnmp_table_request_info *tblreq) +{ + return _find_next_row(c, tblreq, NULL ); +} + +/* ================================== + * + * Container Table API: Index operations + * + * ================================== */ + +/** @} */ |