/* * Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #include #include #if HAVE_STRING_H #include #else #include #endif #include #include 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_mib_handler *handler = netsnmp_get_table_data_handler(table); if (!table || !handler || (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) { snmp_log(LOG_ERR, "could not create table data handler\n"); netsnmp_handler_free(handler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } 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_mib_handler *handler = netsnmp_get_read_only_handler(); if (!handler || (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) { snmp_log(LOG_ERR, "could not create read only table data handler\n"); netsnmp_handler_free(handler); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } 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 */ /** @} */