summaryrefslogtreecommitdiff
path: root/agent/helpers/table_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/helpers/table_data.c')
-rw-r--r--agent/helpers/table_data.c1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/agent/helpers/table_data.c b/agent/helpers/table_data.c
new file mode 100644
index 0000000..7e83fe9
--- /dev/null
+++ b/agent/helpers/table_data.c
@@ -0,0 +1,1009 @@
+#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_data.h>
+#include <net-snmp/agent/read_only.h>
+
+/** @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;
+
+ memdup((u_char **) & newrow, (u_char *) 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) {
+ memdup((u_char **) & newrow->index_oid,
+ (u_char *) row->index_oid,
+ row->index_oid_len * sizeof(oid));
+ if (!newrow->index_oid) {
+ 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! */
+NETSNMP_INLINE 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
+ * ===================================== */
+
+netsnmp_table_data *
+netsnmp_table_data_create_table(const char *name, long flags)
+{
+ return netsnmp_create_table_data( name );
+}
+
+void
+netsnmp_table_data_delete_table( netsnmp_table_data *table )
+{
+ netsnmp_table_row *row, *nextrow;
+
+ if (!table)
+ return;
+
+ 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;
+
+ if (table->name) {
+ SNMP_FREE(table->name);
+ table->name = NULL;
+ }
+ SNMP_FREE(table);
+ return;
+}
+
+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;
+}
+
+ /* netsnmp_table_data_clone_row() defined above */
+
+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)
+ memdup((u_char **) & new_row->index_oid,
+ (u_char *) old_row->index_oid,
+ old_row->index_oid_len * sizeof(oid));
+ /* XXX - Doesn't copy table-specific row structure */
+ return 0;
+}
+
+ /*
+ * netsnmp_table_data_delete_row()
+ * netsnmp_table_data_add_row()
+ * netsnmp_table_data_replace_row()
+ * netsnmp_table_data_remove_row()
+ * all defined above
+ */
+
+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);
+}
+
+
+/* ==================================
+ *
+ * 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);
+}
+
+/** 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);
+}
+
+/*
+ * 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:
+ case MODE_SET_RESERVE1:
+ 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
+ */
+ if (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;
+
+ 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;
+
+ }
+ }
+
+ 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);
+}
+
+/** 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;
+}
+
+/** inserts a newly created table_data row into a request */
+NETSNMP_INLINE 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));
+ }
+ }
+}
+
+/* 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
+ * ===================================== */
+
+netsnmp_table_row *
+netsnmp_table_data_row_first(netsnmp_table_data *table)
+{
+ return netsnmp_table_data_get_first_row(table);
+}
+
+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);
+}
+
+int
+netsnmp_table_data_row_count(netsnmp_table_data *table)
+{
+ return netsnmp_table_data_num_rows(table);
+}
+
+
+/* ==================================
+ *
+ * Table Data API: Row operations
+ * (table-specific rows)
+ *
+ * ================================== */
+
+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 );
+}
+
+ /* =====================================
+ * Generic API - mostly renamed wrappers
+ * ===================================== */
+
+/* ==================================
+ *
+ * Table Data API: Index operations
+ *
+ * ================================== */
+
+/** @}
+ */