| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include <net-snmp/agent/table_data.h> |
| |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| |
| #include <net-snmp/agent/table.h> |
| #include <net-snmp/agent/read_only.h> |
| |
| netsnmp_feature_child_of(table_data_all, mib_helpers) |
| |
| netsnmp_feature_child_of(table_data, table_data_all) |
| netsnmp_feature_child_of(register_read_only_table_data, table_data_all) |
| netsnmp_feature_child_of(extract_table_row_data, table_data_all) |
| netsnmp_feature_child_of(insert_table_row, table_data_all) |
| netsnmp_feature_child_of(table_data_delete_table, table_data_all) |
| |
| netsnmp_feature_child_of(table_data_extras, table_data_all) |
| |
| netsnmp_feature_child_of(table_data_create_table, table_data_extras) |
| netsnmp_feature_child_of(table_data_create_row, table_data_extras) |
| netsnmp_feature_child_of(table_data_copy_row, table_data_extras) |
| netsnmp_feature_child_of(table_data_remove_delete_row, table_data_extras) |
| netsnmp_feature_child_of(table_data_unregister, table_data_extras) |
| netsnmp_feature_child_of(table_data_row_count, table_data_extras) |
| netsnmp_feature_child_of(table_data_row_operations, table_data_extras) |
| netsnmp_feature_child_of(table_data_row_first, table_data_extras) |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA |
| |
| /** @defgroup table_data table_data |
| * Helps you implement a table with datamatted storage. |
| * @ingroup table |
| * |
| * This helper is obsolete. If you are writing a new module, please |
| * consider using the table_tdata helper instead. |
| * |
| * This helper helps you implement a table where all the indexes 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. |
| * |
| * @{ |
| */ |
| |
| /* ================================== |
| * |
| * Table Data API: Table maintenance |
| * |
| * ================================== */ |
| |
| /* |
| * generates the index portion of an table oid from a varlist. |
| */ |
| void |
| netsnmp_table_data_generate_index_oid(netsnmp_table_row *row) |
| { |
| build_oid(&row->index_oid, &row->index_oid_len, NULL, 0, row->indexes); |
| } |
| |
| /** creates and returns a pointer to table data set */ |
| netsnmp_table_data * |
| netsnmp_create_table_data(const char *name) |
| { |
| netsnmp_table_data *table = SNMP_MALLOC_TYPEDEF(netsnmp_table_data); |
| if (name && table) |
| table->name = strdup(name); |
| return table; |
| } |
| |
| /** creates and returns a pointer to table data set */ |
| netsnmp_table_row * |
| netsnmp_create_table_data_row(void) |
| { |
| netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row); |
| return row; |
| } |
| |
| /** clones a data row. DOES NOT CLONE THE CONTAINED DATA. */ |
| netsnmp_table_row * |
| netsnmp_table_data_clone_row(netsnmp_table_row *row) |
| { |
| netsnmp_table_row *newrow = NULL; |
| if (!row) |
| return NULL; |
| |
| newrow = netsnmp_memdup(row, sizeof(netsnmp_table_row)); |
| if (!newrow) |
| return NULL; |
| |
| if (row->indexes) { |
| newrow->indexes = snmp_clone_varbind(newrow->indexes); |
| if (!newrow->indexes) { |
| free(newrow); |
| return NULL; |
| } |
| } |
| |
| if (row->index_oid) { |
| newrow->index_oid = |
| snmp_duplicate_objid(row->index_oid, row->index_oid_len); |
| if (!newrow->index_oid) { |
| free(newrow->indexes); |
| free(newrow); |
| return NULL; |
| } |
| } |
| |
| return newrow; |
| } |
| |
| /** deletes a row's memory. |
| * returns the void data that it doesn't know how to delete. */ |
| void * |
| netsnmp_table_data_delete_row(netsnmp_table_row *row) |
| { |
| void *data; |
| |
| if (!row) |
| return NULL; |
| |
| /* |
| * free the memory we can |
| */ |
| if (row->indexes) |
| snmp_free_varbind(row->indexes); |
| SNMP_FREE(row->index_oid); |
| data = row->data; |
| free(row); |
| |
| /* |
| * return the void * pointer |
| */ |
| return data; |
| } |
| |
| /** |
| * Adds a row of data to a 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_table_data_add_row(netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| int rc, dup = 0; |
| netsnmp_table_row *nextrow = NULL, *prevrow; |
| |
| if (!row || !table) |
| return SNMPERR_GENERR; |
| |
| if (row->indexes) |
| netsnmp_table_data_generate_index_oid(row); |
| |
| /* |
| * we don't store the index info as it |
| * takes up memory. |
| */ |
| if (!table->store_indexes) { |
| snmp_free_varbind(row->indexes); |
| row->indexes = NULL; |
| } |
| |
| if (!row->index_oid) { |
| snmp_log(LOG_ERR, |
| "illegal data attempted to be added to table %s (no index)\n", |
| table->name); |
| return SNMPERR_GENERR; |
| } |
| |
| /* |
| * check for simple append |
| */ |
| if ((prevrow = table->last_row) != NULL) { |
| rc = snmp_oid_compare(prevrow->index_oid, prevrow->index_oid_len, |
| row->index_oid, row->index_oid_len); |
| if (0 == rc) |
| dup = 1; |
| } |
| else |
| rc = 1; |
| |
| /* |
| * if no last row, or newrow < last row, search the table and |
| * insert it into the table in the proper oid-lexographical order |
| */ |
| if (rc > 0) { |
| for (nextrow = table->first_row, prevrow = NULL; |
| nextrow != NULL; prevrow = nextrow, nextrow = nextrow->next) { |
| if (NULL == nextrow->index_oid) { |
| DEBUGMSGT(("table_data_add_data", "row doesn't have index!\n")); |
| /** xxx-rks: remove invalid row? */ |
| continue; |
| } |
| rc = snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len, |
| row->index_oid, row->index_oid_len); |
| if(rc > 0) |
| break; |
| if (0 == rc) { |
| dup = 1; |
| break; |
| } |
| } |
| } |
| |
| if (dup) { |
| /* |
| * exact match. Duplicate entries illegal |
| */ |
| snmp_log(LOG_WARNING, |
| "duplicate table data attempted to be entered. row exists\n"); |
| return SNMPERR_GENERR; |
| } |
| |
| /* |
| * ok, we have the location of where it should go |
| */ |
| /* |
| * (after prevrow, and before nextrow) |
| */ |
| row->next = nextrow; |
| row->prev = prevrow; |
| |
| if (row->next) |
| row->next->prev = row; |
| |
| if (row->prev) |
| row->prev->next = row; |
| |
| if (NULL == row->prev) /* it's the (new) first row */ |
| table->first_row = row; |
| if (NULL == row->next) /* it's the last row */ |
| table->last_row = row; |
| |
| DEBUGMSGTL(("table_data_add_data", "added something...\n")); |
| |
| return SNMPERR_SUCCESS; |
| } |
| |
| /** swaps out origrow with newrow. This does *not* delete/free anything! */ |
| void |
| netsnmp_table_data_replace_row(netsnmp_table_data *table, |
| netsnmp_table_row *origrow, |
| netsnmp_table_row *newrow) |
| { |
| netsnmp_table_data_remove_row(table, origrow); |
| netsnmp_table_data_add_row(table, newrow); |
| } |
| |
| /** |
| * removes a row of data to a given table and returns it (no free's called) |
| * |
| * returns the row pointer itself on successful removing. |
| * or NULL on failure (bad arguments) |
| */ |
| netsnmp_table_row * |
| netsnmp_table_data_remove_row(netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| if (!row || !table) |
| return NULL; |
| |
| if (row->prev) |
| row->prev->next = row->next; |
| else |
| table->first_row = row->next; |
| |
| if (row->next) |
| row->next->prev = row->prev; |
| else |
| table->last_row = row->prev; |
| |
| return row; |
| } |
| |
| /** |
| * removes and frees a row of data to a given table and returns the void * |
| * |
| * returns the void * data on successful deletion. |
| * or NULL on failure (bad arguments) |
| */ |
| void * |
| netsnmp_table_data_remove_and_delete_row(netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| if (!row || !table) |
| return NULL; |
| |
| /* |
| * remove it from the list |
| */ |
| netsnmp_table_data_remove_row(table, row); |
| return netsnmp_table_data_delete_row(row); |
| } |
| |
| /* ===================================== |
| * Generic API - mostly renamed wrappers |
| * ===================================== */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE |
| netsnmp_table_data * |
| netsnmp_table_data_create_table(const char *name, long flags) |
| { |
| return netsnmp_create_table_data( name ); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE |
| void |
| netsnmp_table_data_delete_table( netsnmp_table_data *table ) |
| { |
| netsnmp_table_row *row, *nextrow; |
| |
| if (!table) |
| return; |
| |
| snmp_free_varbind(table->indexes_template); |
| table->indexes_template = NULL; |
| |
| for (row = table->first_row; row; row=nextrow) { |
| nextrow = row->next; |
| row->next = NULL; |
| netsnmp_table_data_delete_row(row); |
| /* Can't delete table-specific entry memory */ |
| } |
| table->first_row = NULL; |
| |
| SNMP_FREE(table->name); |
| SNMP_FREE(table); |
| return; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW |
| netsnmp_table_row * |
| netsnmp_table_data_create_row( void* entry ) |
| { |
| netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row); |
| if (row) |
| row->data = entry; |
| return row; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW */ |
| |
| /* netsnmp_table_data_clone_row() defined above */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW |
| int |
| netsnmp_table_data_copy_row( netsnmp_table_row *old_row, |
| netsnmp_table_row *new_row ) |
| { |
| if (!old_row || !new_row) |
| return -1; |
| |
| memcpy(new_row, old_row, sizeof(netsnmp_table_row)); |
| |
| if (old_row->indexes) |
| new_row->indexes = snmp_clone_varbind(old_row->indexes); |
| if (old_row->index_oid) |
| new_row->index_oid = |
| snmp_duplicate_objid(old_row->index_oid, old_row->index_oid_len); |
| /* XXX - Doesn't copy table-specific row structure */ |
| return 0; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW */ |
| |
| /* |
| * netsnmp_table_data_delete_row() |
| * netsnmp_table_data_add_row() |
| * netsnmp_table_data_replace_row() |
| * netsnmp_table_data_remove_row() |
| * all defined above |
| */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW |
| void * |
| netsnmp_table_data_remove_delete_row(netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| return netsnmp_table_data_remove_and_delete_row(table, row); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW */ |
| |
| |
| /* ================================== |
| * |
| * Table Data API: MIB maintenance |
| * |
| * ================================== */ |
| |
| /** Creates a table_data handler and returns it */ |
| netsnmp_mib_handler * |
| netsnmp_get_table_data_handler(netsnmp_table_data *table) |
| { |
| netsnmp_mib_handler *ret = NULL; |
| |
| if (!table) { |
| snmp_log(LOG_INFO, |
| "netsnmp_get_table_data_handler(NULL) called\n"); |
| return NULL; |
| } |
| |
| ret = |
| netsnmp_create_handler(TABLE_DATA_NAME, |
| netsnmp_table_data_helper_handler); |
| if (ret) { |
| ret->flags |= MIB_HANDLER_AUTO_NEXT; |
| ret->myvoid = (void *) table; |
| } |
| return ret; |
| } |
| |
| /** registers a handler as a data table. |
| * If table_info != NULL, it registers it as a normal table too. */ |
| int |
| netsnmp_register_table_data(netsnmp_handler_registration *reginfo, |
| netsnmp_table_data *table, |
| netsnmp_table_registration_info *table_info) |
| { |
| netsnmp_inject_handler(reginfo, netsnmp_get_table_data_handler(table)); |
| return netsnmp_register_table(reginfo, table_info); |
| } |
| |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA |
| /** registers a handler as a read-only data table |
| * If table_info != NULL, it registers it as a normal table too. */ |
| int |
| netsnmp_register_read_only_table_data(netsnmp_handler_registration *reginfo, |
| netsnmp_table_data *table, |
| netsnmp_table_registration_info *table_info) |
| { |
| netsnmp_inject_handler(reginfo, netsnmp_get_read_only_handler()); |
| return netsnmp_register_table_data(reginfo, table, table_info); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER |
| int |
| netsnmp_unregister_table_data(netsnmp_handler_registration *reginfo) |
| { |
| /* free table; */ |
| return netsnmp_unregister_table(reginfo); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER */ |
| |
| /* |
| * The helper handler that takes care of passing a specific row of |
| * data down to the lower handler(s). It sets request->processed if |
| * the request should not be handled. |
| */ |
| int |
| netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler, |
| netsnmp_handler_registration *reginfo, |
| netsnmp_agent_request_info *reqinfo, |
| netsnmp_request_info *requests) |
| { |
| netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid; |
| netsnmp_request_info *request; |
| int valid_request = 0; |
| netsnmp_table_row *row; |
| netsnmp_table_request_info *table_info; |
| netsnmp_table_registration_info *table_reg_info = |
| netsnmp_find_table_registration_info(reginfo); |
| int result, regresult; |
| int oldmode; |
| |
| for (request = requests; request; request = request->next) { |
| if (request->processed) |
| continue; |
| |
| table_info = netsnmp_extract_table_info(request); |
| if (!table_info) |
| continue; /* ack */ |
| switch (reqinfo->mode) { |
| case MODE_GET: |
| case MODE_GETNEXT: |
| #ifndef NETSNMP_NO_WRITE_SUPPORT |
| case MODE_SET_RESERVE1: |
| #endif /* NETSNMP_NO_WRITE_SUPPORT */ |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list( |
| TABLE_DATA_TABLE, table, NULL)); |
| } |
| |
| /* |
| * find the row in question |
| */ |
| switch (reqinfo->mode) { |
| case MODE_GETNEXT: |
| case MODE_GETBULK: /* XXXWWW */ |
| if (request->requestvb->type != ASN_NULL) |
| continue; |
| /* |
| * loop through data till we find the next row |
| */ |
| result = snmp_oid_compare(request->requestvb->name, |
| request->requestvb->name_length, |
| reginfo->rootoid, |
| reginfo->rootoid_len); |
| regresult = snmp_oid_compare(request->requestvb->name, |
| SNMP_MIN(request->requestvb-> |
| name_length, |
| reginfo->rootoid_len), |
| reginfo->rootoid, |
| reginfo->rootoid_len); |
| if (regresult == 0 |
| && request->requestvb->name_length < reginfo->rootoid_len) |
| regresult = -1; |
| |
| if (result < 0 || 0 == result) { |
| /* |
| * before us entirely, return the first |
| */ |
| row = table->first_row; |
| table_info->colnum = table_reg_info->min_column; |
| } else if (regresult == 0 && request->requestvb->name_length == |
| reginfo->rootoid_len + 1 && |
| /* entry node must be 1, but any column is ok */ |
| request->requestvb->name[reginfo->rootoid_len] == 1) { |
| /* |
| * exactly to the entry |
| */ |
| row = table->first_row; |
| table_info->colnum = table_reg_info->min_column; |
| } else if (regresult == 0 && request->requestvb->name_length == |
| reginfo->rootoid_len + 2 && |
| /* entry node must be 1, but any column is ok */ |
| request->requestvb->name[reginfo->rootoid_len] == 1) { |
| /* |
| * exactly to the column |
| */ |
| row = table->first_row; |
| } else { |
| /* |
| * loop through all rows looking for the first one |
| * that is equal to the request or greater than it |
| */ |
| for (row = table->first_row; row; row = row->next) { |
| /* |
| * compare the index of the request to the row |
| */ |
| result = |
| snmp_oid_compare(row->index_oid, |
| row->index_oid_len, |
| request->requestvb->name + 2 + |
| reginfo->rootoid_len, |
| request->requestvb->name_length - |
| 2 - reginfo->rootoid_len); |
| if (result == 0) { |
| /* |
| * equal match, return the next row |
| */ |
| row = row->next; |
| break; |
| } else if (result > 0) { |
| /* |
| * the current row is greater than the |
| * request, use it |
| */ |
| break; |
| } |
| } |
| } |
| if (!row) { |
| table_info->colnum++; |
| if (table_info->colnum <= table_reg_info->max_column) { |
| row = table->first_row; |
| } |
| } |
| if (row) { |
| valid_request = 1; |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list |
| (TABLE_DATA_ROW, row, |
| NULL)); |
| /* |
| * Set the name appropriately, so we can pass this |
| * request on as a simple GET request |
| */ |
| netsnmp_table_data_build_result(reginfo, reqinfo, request, |
| row, |
| table_info->colnum, |
| ASN_NULL, NULL, 0); |
| } else { /* no decent result found. Give up. It's beyond us. */ |
| request->processed = 1; |
| } |
| break; |
| |
| case MODE_GET: |
| if (request->requestvb->type != ASN_NULL) |
| continue; |
| /* |
| * find the row in question |
| */ |
| if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */ |
| /* |
| * request too short |
| */ |
| netsnmp_set_request_error(reqinfo, request, |
| SNMP_NOSUCHINSTANCE); |
| break; |
| } else if (NULL == |
| (row = |
| netsnmp_table_data_get_from_oid(table, |
| request-> |
| requestvb->name + |
| reginfo-> |
| rootoid_len + 2, |
| request-> |
| requestvb-> |
| name_length - |
| reginfo-> |
| rootoid_len - |
| 2))) { |
| /* |
| * no such row |
| */ |
| netsnmp_set_request_error(reqinfo, request, |
| SNMP_NOSUCHINSTANCE); |
| break; |
| } else { |
| valid_request = 1; |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list |
| (TABLE_DATA_ROW, row, |
| NULL)); |
| } |
| break; |
| |
| #ifndef NETSNMP_NO_WRITE_SUPPORT |
| case MODE_SET_RESERVE1: |
| valid_request = 1; |
| if (NULL != |
| (row = |
| netsnmp_table_data_get_from_oid(table, |
| request->requestvb->name + |
| reginfo->rootoid_len + 2, |
| request->requestvb-> |
| name_length - |
| reginfo->rootoid_len - |
| 2))) { |
| netsnmp_request_add_list_data(request, |
| netsnmp_create_data_list |
| (TABLE_DATA_ROW, row, |
| NULL)); |
| } |
| break; |
| |
| case MODE_SET_RESERVE2: |
| case MODE_SET_ACTION: |
| case MODE_SET_COMMIT: |
| case MODE_SET_FREE: |
| case MODE_SET_UNDO: |
| valid_request = 1; |
| #endif /* NETSNMP_NO_WRITE_SUPPORT */ |
| |
| } |
| } |
| |
| if (valid_request && |
| (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK)) { |
| /* |
| * If this is a GetNext or GetBulk request, then we've identified |
| * the row that ought to include the appropriate next instance. |
| * Convert the request into a Get request, so that the lower-level |
| * handlers don't need to worry about skipping on, and call these |
| * handlers ourselves (so we can undo this again afterwards). |
| */ |
| oldmode = reqinfo->mode; |
| reqinfo->mode = MODE_GET; |
| result = netsnmp_call_next_handler(handler, reginfo, reqinfo, |
| requests); |
| reqinfo->mode = oldmode; |
| handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; |
| return result; |
| } |
| else |
| /* next handler called automatically - 'AUTO_NEXT' */ |
| return SNMP_ERR_NOERROR; |
| } |
| |
| /** extracts the table being accessed passed from the table_data helper */ |
| netsnmp_table_data * |
| netsnmp_extract_table(netsnmp_request_info *request) |
| { |
| return (netsnmp_table_data *) |
| netsnmp_request_get_list_data(request, TABLE_DATA_TABLE); |
| } |
| |
| /** extracts the row being accessed passed from the table_data helper */ |
| netsnmp_table_row * |
| netsnmp_extract_table_row(netsnmp_request_info *request) |
| { |
| return (netsnmp_table_row *) netsnmp_request_get_list_data(request, |
| TABLE_DATA_ROW); |
| } |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA |
| /** extracts the data from the row being accessed passed from the |
| * table_data helper */ |
| void * |
| netsnmp_extract_table_row_data(netsnmp_request_info *request) |
| { |
| netsnmp_table_row *row; |
| row = (netsnmp_table_row *) netsnmp_extract_table_row(request); |
| if (row) |
| return row->data; |
| else |
| return NULL; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW |
| /** inserts a newly created table_data row into a request */ |
| void |
| netsnmp_insert_table_row(netsnmp_request_info *request, |
| netsnmp_table_row *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) { |
| 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_DATA_ROW, row, NULL)); |
| } |
| } |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW */ |
| |
| /* builds a result given a row, a varbind to set and the data */ |
| int |
| netsnmp_table_data_build_result(netsnmp_handler_registration *reginfo, |
| netsnmp_agent_request_info *reqinfo, |
| netsnmp_request_info *request, |
| netsnmp_table_row *row, |
| int column, |
| u_char type, |
| u_char * result_data, |
| size_t result_data_len) |
| { |
| oid build_space[MAX_OID_LEN]; |
| |
| if (!reginfo || !reqinfo || !request) |
| return SNMPERR_GENERR; |
| |
| if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) { |
| /* |
| * only need to do this for getnext type cases where oid is changing |
| */ |
| memcpy(build_space, reginfo->rootoid, /* registered oid */ |
| reginfo->rootoid_len * sizeof(oid)); |
| build_space[reginfo->rootoid_len] = 1; /* entry */ |
| build_space[reginfo->rootoid_len + 1] = column; /* column */ |
| memcpy(build_space + reginfo->rootoid_len + 2, /* index data */ |
| row->index_oid, row->index_oid_len * sizeof(oid)); |
| snmp_set_var_objid(request->requestvb, build_space, |
| reginfo->rootoid_len + 2 + row->index_oid_len); |
| } |
| snmp_set_var_typed_value(request->requestvb, type, |
| result_data, result_data_len); |
| return SNMPERR_SUCCESS; /* WWWXXX: check for bounds */ |
| } |
| |
| |
| /* ================================== |
| * |
| * Table Data API: Row operations |
| * (table-independent rows) |
| * |
| * ================================== */ |
| |
| /** returns the first row in the table */ |
| netsnmp_table_row * |
| netsnmp_table_data_get_first_row(netsnmp_table_data *table) |
| { |
| if (!table) |
| return NULL; |
| return table->first_row; |
| } |
| |
| /** returns the next row in the table */ |
| netsnmp_table_row * |
| netsnmp_table_data_get_next_row(netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| if (!row) |
| return NULL; |
| return row->next; |
| } |
| |
| /** finds the data in "datalist" stored at "indexes" */ |
| netsnmp_table_row * |
| netsnmp_table_data_get(netsnmp_table_data *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_table_data_get_from_oid(table, searchfor, |
| searchfor_len); |
| } |
| |
| /** finds the data in "datalist" stored at the searchfor oid */ |
| netsnmp_table_row * |
| netsnmp_table_data_get_from_oid(netsnmp_table_data *table, |
| oid * searchfor, size_t searchfor_len) |
| { |
| netsnmp_table_row *row; |
| if (!table) |
| return NULL; |
| |
| for (row = table->first_row; row != NULL; row = row->next) { |
| if (row->index_oid && |
| snmp_oid_compare(searchfor, searchfor_len, |
| row->index_oid, row->index_oid_len) == 0) |
| return row; |
| } |
| return NULL; |
| } |
| |
| int |
| netsnmp_table_data_num_rows(netsnmp_table_data *table) |
| { |
| int i=0; |
| netsnmp_table_row *row; |
| if (!table) |
| return 0; |
| for (row = table->first_row; row; row = row->next) { |
| i++; |
| } |
| return i; |
| } |
| |
| /* ===================================== |
| * Generic API - mostly renamed wrappers |
| * ===================================== */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST |
| netsnmp_table_row * |
| netsnmp_table_data_row_first(netsnmp_table_data *table) |
| { |
| return netsnmp_table_data_get_first_row(table); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST */ |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_get( netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| if (!table || !row) |
| return NULL; |
| return netsnmp_table_data_get_from_oid(table, row->index_oid, |
| row->index_oid_len); |
| } |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_next( netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| return netsnmp_table_data_get_next_row(table, row); |
| } |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_get_byoid( netsnmp_table_data *table, |
| oid *instance, size_t len) |
| { |
| return netsnmp_table_data_get_from_oid(table, instance, len); |
| } |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_next_byoid(netsnmp_table_data *table, |
| oid *instance, size_t len) |
| { |
| netsnmp_table_row *row; |
| |
| if (!table || !instance) |
| return NULL; |
| |
| for (row = table->first_row; row; row = row->next) { |
| if (snmp_oid_compare(row->index_oid, |
| row->index_oid_len, |
| instance, len) > 0) |
| return row; |
| } |
| return NULL; |
| } |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_get_byidx( netsnmp_table_data *table, |
| netsnmp_variable_list *indexes) |
| { |
| return netsnmp_table_data_get(table, indexes); |
| } |
| |
| netsnmp_table_row * |
| netsnmp_table_data_row_next_byidx(netsnmp_table_data *table, |
| netsnmp_variable_list *indexes) |
| { |
| oid instance[MAX_OID_LEN]; |
| size_t len = MAX_OID_LEN; |
| |
| if (!table || !indexes) |
| return NULL; |
| |
| build_oid_noalloc(instance, MAX_OID_LEN, &len, NULL, 0, indexes); |
| return netsnmp_table_data_row_next_byoid(table, instance, len); |
| } |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT |
| int |
| netsnmp_table_data_row_count(netsnmp_table_data *table) |
| { |
| return netsnmp_table_data_num_rows(table); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT */ |
| |
| |
| /* ================================== |
| * |
| * Table Data API: Row operations |
| * (table-specific rows) |
| * |
| * ================================== */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS |
| void * |
| netsnmp_table_data_entry_first(netsnmp_table_data *table) |
| { |
| netsnmp_table_row *row = |
| netsnmp_table_data_get_first_row(table); |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_get( netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_next( netsnmp_table_data *table, |
| netsnmp_table_row *row) |
| { |
| row = |
| netsnmp_table_data_row_next(table, row); |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_get_byidx( netsnmp_table_data *table, |
| netsnmp_variable_list *indexes) |
| { |
| netsnmp_table_row *row = |
| netsnmp_table_data_row_get_byidx(table, indexes); |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_next_byidx(netsnmp_table_data *table, |
| netsnmp_variable_list *indexes) |
| { |
| netsnmp_table_row *row = |
| netsnmp_table_data_row_next_byidx(table, indexes); |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_get_byoid( netsnmp_table_data *table, |
| oid *instance, size_t len) |
| { |
| netsnmp_table_row *row = |
| netsnmp_table_data_row_get_byoid(table, instance, len); |
| return (row ? row->data : NULL ); |
| } |
| |
| void * |
| netsnmp_table_data_entry_next_byoid(netsnmp_table_data *table, |
| oid *instance, size_t len) |
| { |
| netsnmp_table_row *row = |
| netsnmp_table_data_row_next_byoid(table, instance, len); |
| return (row ? row->data : NULL ); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS */ |
| |
| /* ===================================== |
| * Generic API - mostly renamed wrappers |
| * ===================================== */ |
| |
| /* ================================== |
| * |
| * Table Data API: Index operations |
| * |
| * ================================== */ |
| |
| #else /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */ |
| netsnmp_feature_unused(table_data); |
| #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */ |
| |
| /** @} |
| */ |