| #include <net-snmp/net-snmp-config.h> |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include <net-snmp/agent/table_tdata.h> |
| |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| |
| #include <net-snmp/agent/table.h> |
| #include <net-snmp/agent/table_container.h> |
| #include <net-snmp/agent/read_only.h> |
| |
| #if HAVE_DMALLOC_H |
| #include <dmalloc.h> |
| #endif |
| |
| /** @defgroup tdata tdata |
| * Implement a table with datamatted storage. |
| * @ingroup table |
| * |
| * This helper helps you implement a table where all the rows are |
| * expected to be stored within the agent itself and not in some |
| * external storage location. It can be used to store a list of |
| * rows, where a row consists of the indexes to the table and a |
| * generic data pointer. You can then implement a subhandler which |
| * is passed the exact row definition and data it must return data |
| * for or accept data for. Complex GETNEXT handling is greatly |
| * simplified in this case. |
| * |
| * @{ |
| */ |
| |
| /* ================================== |
| * |
| * TData API: Table maintenance |
| * |
| * ================================== */ |
| |
| /* |
| * generates the index portion of an table oid from a varlist. |
| */ |
| void |
| _netsnmp_tdata_generate_index_oid(netsnmp_tdata_row *row) |
| { |
| build_oid(&row->oid_index.oids, &row->oid_index.len, NULL, 0, row->indexes); |
| } |
| |
| /** creates and returns a 'tdata' table data structure */ |
| netsnmp_tdata * |
| netsnmp_tdata_create_table(const char *name, long flags) |
| { |
| netsnmp_tdata *table = SNMP_MALLOC_TYPEDEF(netsnmp_tdata); |
| if ( !table ) |
| return NULL; |
| |
| table->flags = flags; |
| if (name) |
| table->name = strdup(name); |
| |
| if (!(table->flags & TDATA_FLAG_NO_CONTAINER)) { |
| table->container = netsnmp_container_find( name ); |
| if (!table->container) |
| table->container = netsnmp_container_find( "table_container" ); |
| if (table->container) |
| table->container->container_name = strdup(name); |
| } |
| return table; |
| } |
| |
| /** creates and returns a 'tdata' table data structure */ |
| void |
| netsnmp_tdata_delete_table(netsnmp_tdata *table) |
| { |
| if (!table) |
| return; |
| |
| if (table->name) |
| free(table->name); |
| if (table->container) |
| CONTAINER_FREE(table->container); |
| |
| SNMP_FREE(table); |
| return; |
| } |
| |
| /** creates and returns a pointer to new row data structure */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_create_row(void) |
| { |
| netsnmp_tdata_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_tdata_row); |
| return row; |
| } |
| |
| /** clones a 'tdata' row. DOES NOT CLONE THE TABLE-SPECIFIC ENTRY DATA. */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_clone_row(netsnmp_tdata_row *row) |
| { |
| netsnmp_tdata_row *newrow = NULL; |
| if (!row) |
| return NULL; |
| |
| newrow = netsnmp_memdup(row, sizeof(netsnmp_tdata_row)); |
| if (!newrow) |
| return NULL; |
| |
| if (row->indexes) { |
| newrow->indexes = snmp_clone_varbind(newrow->indexes); |
| if (!newrow->indexes) { |
| SNMP_FREE(newrow); |
| return NULL; |
| } |
| } |
| |
| if (row->oid_index.oids) { |
| newrow->oid_index.oids = |
| snmp_duplicate_objid(row->oid_index.oids, row->oid_index.len); |
| if (!newrow->oid_index.oids) { |
| if (newrow->indexes) |
| snmp_free_varbind(newrow->indexes); |
| SNMP_FREE(newrow); |
| return NULL; |
| } |
| } |
| |
| return newrow; |
| } |
| |
| /** copy the contents of a 'tdata' row. |
| DOES NOT COPY THE TABLE-SPECIFIC ENTRY DATA. */ |
| int |
| netsnmp_tdata_copy_row(netsnmp_tdata_row *dst_row, netsnmp_tdata_row *src_row) |
| { |
| if ( !src_row || !dst_row ) |
| return -1; |
| |
| memcpy((u_char *) dst_row, (u_char *) src_row, |
| sizeof(netsnmp_tdata_row)); |
| if (src_row->indexes) { |
| dst_row->indexes = snmp_clone_varbind(src_row->indexes); |
| if (!dst_row->indexes) |
| return -1; |
| } |
| |
| if (src_row->oid_index.oids) { |
| dst_row->oid_index.oids = snmp_duplicate_objid(src_row->oid_index.oids, |
| src_row->oid_index.len); |
| if (!dst_row->oid_index.oids) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** deletes the memory used by the specified row |
| * returns the table-specific entry data |
| * (that it doesn't know how to delete) */ |
| void * |
| netsnmp_tdata_delete_row(netsnmp_tdata_row *row) |
| { |
| void *data; |
| |
| if (!row) |
| return NULL; |
| |
| /* |
| * free the memory we can |
| */ |
| if (row->indexes) |
| snmp_free_varbind(row->indexes); |
| SNMP_FREE(row->oid_index.oids); |
| data = row->data; |
| free(row); |
| |
| /* |
| * return the void * pointer |
| */ |
| return data; |
| } |
| |
| /** |
| * Adds a row to the given table (stored in proper lexographical order). |
| * |
| * returns SNMPERR_SUCCESS on successful addition. |
| * or SNMPERR_GENERR on failure (E.G., indexes already existed) |
| */ |
| int |
| netsnmp_tdata_add_row(netsnmp_tdata *table, |
| netsnmp_tdata_row *row) |
| { |
| if (!row || !table) |
| return SNMPERR_GENERR; |
| |
| if (row->indexes) |
| _netsnmp_tdata_generate_index_oid(row); |
| |
| if (!row->oid_index.oids) { |
| snmp_log(LOG_ERR, |
| "illegal data attempted to be added to table %s (no index)\n", |
| table->name); |
| return SNMPERR_GENERR; |
| } |
| |
| /* |
| * The individual index values probably won't be needed, |
| * so this memory can be released. |
| * Note that this is purely internal to the helper. |
| * The calling application can set this flag as |
| * a hint to the helper that these values aren't |
| * required, but it's up to the helper as to |
| * whether it takes any notice or not! |
| */ |
| if (table->flags & TDATA_FLAG_NO_STORE_INDEXES) { |
| snmp_free_varbind(row->indexes); |
| row->indexes = NULL; |
| } |
| |
| /* |
| * add this row to the stored table |
| */ |
| if (CONTAINER_INSERT( table->container, row ) != 0) |
| return SNMPERR_GENERR; |
| |
| DEBUGMSGTL(("tdata_add_row", "added row (%p)\n", row)); |
| |
| return SNMPERR_SUCCESS; |
| } |
| |
| /** swaps out origrow with newrow. This does *not* delete/free anything! */ |
| void |
| netsnmp_tdata_replace_row(netsnmp_tdata *table, |
| netsnmp_tdata_row *origrow, |
| netsnmp_tdata_row *newrow) |
| { |
| netsnmp_tdata_remove_row(table, origrow); |
| netsnmp_tdata_add_row(table, newrow); |
| } |
| |
| /** |
| * removes a row from the given table and returns it (no free's called) |
| * |
| * returns the row pointer itself on successful removing. |
| * or NULL on failure (bad arguments) |
| */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_remove_row(netsnmp_tdata *table, |
| netsnmp_tdata_row *row) |
| { |
| if (!row || !table) |
| return NULL; |
| |
| CONTAINER_REMOVE( table->container, row ); |
| return row; |
| } |
| |
| /** |
| * removes and frees a row of the given table and |
| * returns the table-specific entry data |
| * |
| * returns the void * pointer on successful deletion. |
| * or NULL on failure (bad arguments) |
| */ |
| void * |
| netsnmp_tdata_remove_and_delete_row(netsnmp_tdata *table, |
| netsnmp_tdata_row *row) |
| { |
| if (!row || !table) |
| return NULL; |
| |
| /* |
| * remove it from the list |
| */ |
| netsnmp_tdata_remove_row(table, row); |
| return netsnmp_tdata_delete_row(row); |
| } |
| |
| |
| /* ================================== |
| * |
| * TData API: MIB maintenance |
| * |
| * ================================== */ |
| |
| Netsnmp_Node_Handler _netsnmp_tdata_helper_handler; |
| |
| /** Creates a tdata handler and returns it */ |
| netsnmp_mib_handler * |
| netsnmp_get_tdata_handler(netsnmp_tdata *table) |
| { |
| netsnmp_mib_handler *ret = NULL; |
| |
| if (!table) { |
| snmp_log(LOG_INFO, |
| "netsnmp_get_tdata_handler(NULL) called\n"); |
| return NULL; |
| } |
| |
| ret = netsnmp_create_handler(TABLE_TDATA_NAME, |
| _netsnmp_tdata_helper_handler); |
| if (ret) { |
| ret->flags |= MIB_HANDLER_AUTO_NEXT; |
| ret->myvoid = (void *) table; |
| } |
| return ret; |
| } |
| |
| /* |
| * The helper handler that takes care of passing a specific row of |
| * data down to the lower handler(s). The table_container helper |
| * has already taken care of identifying the appropriate row of the |
| * table (and converting GETNEXT requests into an equivalent GET request) |
| * So all we need to do here is make sure that the row is accessible |
| * using tdata-style retrieval techniques as well. |
| */ |
| int |
| _netsnmp_tdata_helper_handler(netsnmp_mib_handler *handler, |
| netsnmp_handler_registration *reginfo, |
| netsnmp_agent_request_info *reqinfo, |
| netsnmp_request_info *requests) |
| { |
| netsnmp_tdata *table = (netsnmp_tdata *) handler->myvoid; |
| netsnmp_request_info *request; |
| netsnmp_table_request_info *table_info; |
| netsnmp_tdata_row *row; |
| int need_processing = 1; |
| |
| switch ( reqinfo->mode ) { |
| case MODE_GET: |
| need_processing = 0; /* only need processing if some vars found */ |
| /** Fall through */ |
| |
| case MODE_SET_RESERVE1: |
| |
| for (request = requests; request; request = request->next) { |
| if (request->processed) |
| continue; |
| |
| table_info = netsnmp_extract_table_info(request); |
| if (!table_info) { |
| netsnmp_assert(table_info); /* yes, this will always hit */ |
| netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); |
| continue; /* eek */ |
| } |
| row = (netsnmp_tdata_row*)netsnmp_container_table_row_extract( request ); |
| if (!row && (reqinfo->mode == MODE_GET)) { |
| netsnmp_assert(row); /* yes, this will always hit */ |
| netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); |
| continue; /* eek */ |
| } |
| ++need_processing; |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list( |
| TABLE_TDATA_TABLE, table, NULL)); |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list( |
| TABLE_TDATA_ROW, row, NULL)); |
| } |
| /** skip next handler if processing not needed */ |
| if (!need_processing) |
| handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; |
| } |
| |
| /* next handler called automatically - 'AUTO_NEXT' */ |
| return SNMP_ERR_NOERROR; |
| } |
| |
| |
| /** registers a tdata-based MIB table */ |
| int |
| netsnmp_tdata_register(netsnmp_handler_registration *reginfo, |
| netsnmp_tdata *table, |
| netsnmp_table_registration_info *table_info) |
| { |
| netsnmp_inject_handler(reginfo, netsnmp_get_tdata_handler(table)); |
| return netsnmp_container_table_register(reginfo, table_info, |
| table->container, TABLE_CONTAINER_KEY_NETSNMP_INDEX); |
| } |
| |
| int |
| netsnmp_tdata_unregister(netsnmp_handler_registration *reginfo) |
| { |
| /* free table; */ |
| return netsnmp_container_table_unregister(reginfo); |
| } |
| |
| /** extracts the tdata table from the request structure */ |
| netsnmp_tdata * |
| netsnmp_tdata_extract_table(netsnmp_request_info *request) |
| { |
| return (netsnmp_tdata *) netsnmp_request_get_list_data(request, |
| TABLE_TDATA_TABLE); |
| } |
| |
| /** extracts the tdata container from the request structure */ |
| netsnmp_container * |
| netsnmp_tdata_extract_container(netsnmp_request_info *request) |
| { |
| netsnmp_tdata *tdata = (netsnmp_tdata*) |
| netsnmp_request_get_list_data(request, TABLE_TDATA_TABLE); |
| return ( tdata ? tdata->container : NULL ); |
| } |
| |
| /** extracts the tdata row being accessed from the request structure */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_extract_row(netsnmp_request_info *request) |
| { |
| return (netsnmp_tdata_row *) netsnmp_container_table_row_extract(request); |
| } |
| |
| /** extracts the (table-specific) entry being accessed from the |
| * request structure */ |
| void * |
| netsnmp_tdata_extract_entry(netsnmp_request_info *request) |
| { |
| netsnmp_tdata_row *row = |
| (netsnmp_tdata_row *) netsnmp_tdata_extract_row(request); |
| if (row) |
| return row->data; |
| else |
| return NULL; |
| } |
| |
| /** inserts a newly created tdata row into a request */ |
| NETSNMP_INLINE void |
| netsnmp_insert_tdata_row(netsnmp_request_info *request, |
| netsnmp_tdata_row *row) |
| { |
| netsnmp_container_table_row_insert(request, (netsnmp_index *)row); |
| } |
| |
| /** inserts a newly created tdata row into a request */ |
| NETSNMP_INLINE void |
| netsnmp_remove_tdata_row(netsnmp_request_info *request, |
| netsnmp_tdata_row *row) |
| { |
| netsnmp_container_table_row_remove(request, (netsnmp_index *)row); |
| } |
| |
| |
| /* ================================== |
| * |
| * Generic API: Row operations |
| * |
| * ================================== */ |
| |
| /** returns the (table-specific) entry data for a given row */ |
| void * |
| netsnmp_tdata_row_entry( netsnmp_tdata_row *row ) |
| { |
| if (row) |
| return row->data; |
| else |
| return NULL; |
| } |
| |
| /** returns the first row in the table */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_first(netsnmp_tdata *table) |
| { |
| return (netsnmp_tdata_row *)CONTAINER_FIRST( table->container ); |
| } |
| |
| /** finds a row in the 'tdata' table given another row */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_get( netsnmp_tdata *table, |
| netsnmp_tdata_row *row) |
| { |
| return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, row ); |
| } |
| |
| /** returns the next row in the table */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_next( netsnmp_tdata *table, |
| netsnmp_tdata_row *row) |
| { |
| return (netsnmp_tdata_row *)CONTAINER_NEXT( table->container, row ); |
| } |
| |
| /** finds a row in the 'tdata' table given the index values */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_get_byidx(netsnmp_tdata *table, |
| netsnmp_variable_list *indexes) |
| { |
| oid searchfor[ MAX_OID_LEN]; |
| size_t searchfor_len = MAX_OID_LEN; |
| |
| build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, |
| indexes); |
| return netsnmp_tdata_row_get_byoid(table, searchfor, searchfor_len); |
| } |
| |
| /** finds a row in the 'tdata' table given the index OID */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_get_byoid(netsnmp_tdata *table, |
| oid * searchfor, size_t searchfor_len) |
| { |
| netsnmp_index index; |
| if (!table) |
| return NULL; |
| |
| index.oids = searchfor; |
| index.len = searchfor_len; |
| return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, &index ); |
| } |
| |
| /** finds the lexically next row in the 'tdata' table |
| given the index values */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_next_byidx(netsnmp_tdata *table, |
| netsnmp_variable_list *indexes) |
| { |
| oid searchfor[ MAX_OID_LEN]; |
| size_t searchfor_len = MAX_OID_LEN; |
| |
| build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, |
| indexes); |
| return netsnmp_tdata_row_next_byoid(table, searchfor, searchfor_len); |
| } |
| |
| /** finds the lexically next row in the 'tdata' table |
| given the index OID */ |
| netsnmp_tdata_row * |
| netsnmp_tdata_row_next_byoid(netsnmp_tdata *table, |
| oid * searchfor, size_t searchfor_len) |
| { |
| netsnmp_index index; |
| if (!table) |
| return NULL; |
| |
| index.oids = searchfor; |
| index.len = searchfor_len; |
| return (netsnmp_tdata_row*)CONTAINER_NEXT( table->container, &index ); |
| } |
| |
| int |
| netsnmp_tdata_row_count(netsnmp_tdata *table) |
| { |
| if (!table) |
| return 0; |
| return CONTAINER_SIZE( table->container ); |
| } |
| |
| /* ================================== |
| * |
| * Generic API: Index operations on a 'tdata' table |
| * |
| * ================================== */ |
| |
| |
| /** compare a row with the given index values */ |
| int |
| netsnmp_tdata_compare_idx(netsnmp_tdata_row *row, |
| netsnmp_variable_list *indexes) |
| { |
| oid searchfor[ MAX_OID_LEN]; |
| size_t searchfor_len = MAX_OID_LEN; |
| |
| build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, |
| indexes); |
| return netsnmp_tdata_compare_oid(row, searchfor, searchfor_len); |
| } |
| |
| /** compare a row with the given index OID */ |
| int |
| netsnmp_tdata_compare_oid(netsnmp_tdata_row *row, |
| oid * compareto, size_t compareto_len) |
| { |
| netsnmp_index *index = (netsnmp_index *)row; |
| return snmp_oid_compare( index->oids, index->len, |
| compareto, compareto_len); |
| } |
| |
| int |
| netsnmp_tdata_compare_subtree_idx(netsnmp_tdata_row *row, |
| netsnmp_variable_list *indexes) |
| { |
| oid searchfor[ MAX_OID_LEN]; |
| size_t searchfor_len = MAX_OID_LEN; |
| |
| build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, |
| indexes); |
| return netsnmp_tdata_compare_subtree_oid(row, searchfor, searchfor_len); |
| } |
| |
| int |
| netsnmp_tdata_compare_subtree_oid(netsnmp_tdata_row *row, |
| oid * compareto, size_t compareto_len) |
| { |
| netsnmp_index *index = (netsnmp_index *)row; |
| return snmp_oidtree_compare( index->oids, index->len, |
| compareto, compareto_len); |
| } |
| |
| /** @} |
| */ |