net-snmp/agent/helpers/table_tdata.c

670 lines
20 KiB
C

/*
* 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 <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_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
netsnmp_feature_child_of(table_tdata_all, mib_helpers)
netsnmp_feature_child_of(table_tdata, table_tdata_all)
netsnmp_feature_child_of(table_tdata_delete_table, table_tdata_all)
netsnmp_feature_child_of(table_tdata_extract_table, table_tdata_all)
netsnmp_feature_child_of(table_tdata_remove_row, table_tdata_all)
netsnmp_feature_child_of(table_tdata_insert_row, table_tdata_all)
#ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA
netsnmp_feature_require(table_container_row_insert)
#ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW
netsnmp_feature_require(table_container_row_remove)
#endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW */
#endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA
/** @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 && name)
table->container->container_name = strdup(name);
}
return table;
}
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_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;
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_TABLE */
/** 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_feature_child_of(tdata_clone_row, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW
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;
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW */
/** copy the contents of a 'tdata' row.
DOES NOT COPY THE TABLE-SPECIFIC ENTRY DATA. */
netsnmp_feature_child_of(tdata_copy_row, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW
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;
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW */
/** 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! */
netsnmp_feature_child_of(tdata_replace_row, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW
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);
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW */
/**
* 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;
switch ( reqinfo->mode ) {
case MODE_GET:
#ifndef NETSNMP_NO_WRITE_SUPPORT
case MODE_SET_RESERVE1:
#endif /* NETSNMP_NO_WRITE_SUPPORT */
need_processing = reqinfo->mode == MODE_GET ? 0 : 1;
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_mib_handler *handler = netsnmp_get_tdata_handler(table);
if (!reginfo || !table || !table_info || !handler ||
(netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
snmp_log(LOG_ERR, "could not create tdata handler\n");
netsnmp_handler_free(handler);
netsnmp_handler_registration_free(reginfo);
return SNMP_ERR_GENERR;
}
return netsnmp_container_table_register(reginfo, table_info,
table->container, TABLE_CONTAINER_KEY_NETSNMP_INDEX);
}
netsnmp_feature_child_of(tdata_unregister, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER
int
netsnmp_tdata_unregister(netsnmp_handler_registration *reginfo)
{
/* free table; */
return netsnmp_container_table_unregister(reginfo);
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE
/** 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);
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE */
/** extracts the tdata container from the request structure */
netsnmp_feature_child_of(tdata_extract_container, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER
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 );
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER */
/** 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;
}
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW
/** 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);
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_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);
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_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 );
}
netsnmp_feature_child_of(tdata_row_count, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT
int
netsnmp_tdata_row_count(netsnmp_tdata *table)
{
if (!table)
return 0;
return CONTAINER_SIZE( table->container );
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT */
/* ==================================
*
* Generic API: Index operations on a 'tdata' table
*
* ================================== */
/** compare a row with the given index values */
netsnmp_feature_child_of(tdata_compare_idx, table_tdata_all)
#ifndef NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX
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);
}
#endif /* NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX */
/** 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);
}
#else /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */
netsnmp_feature_unused(table_tdata);
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */
/** @}
*/