1499 lines
50 KiB
C
1499 lines
50 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-features.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
|
|
#include <net-snmp/agent/table_dataset.h>
|
|
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
netsnmp_feature_child_of(table_dataset_all, mib_helpers)
|
|
netsnmp_feature_child_of(table_dataset, table_dataset_all)
|
|
netsnmp_feature_child_of(table_dataset_remove_row, table_dataset_all)
|
|
netsnmp_feature_child_of(table_data_set_column, table_dataset_all)
|
|
netsnmp_feature_child_of(table_dataset_get_newrow, table_dataset_all)
|
|
netsnmp_feature_child_of(table_set_add_indexes, table_dataset_all)
|
|
netsnmp_feature_child_of(delete_table_data_set, table_dataset_all)
|
|
netsnmp_feature_child_of(table_set_multi_add_default_row, table_dataset_all)
|
|
netsnmp_feature_child_of(table_dataset_unregister_auto_data_table, table_dataset_all)
|
|
|
|
#ifdef NETSNMP_FEATURE_REQUIRE_TABLE_DATASET
|
|
netsnmp_feature_require(table_get_or_create_row_stash)
|
|
netsnmp_feature_require(table_data_delete_table)
|
|
netsnmp_feature_require(table_data)
|
|
netsnmp_feature_require(oid_stash_get_data)
|
|
netsnmp_feature_require(oid_stash_add_data)
|
|
#endif /* NETSNMP_FEATURE_REQUIRE_TABLE_DATASET */
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
netsnmp_feature_require(oid_stash)
|
|
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
|
netsnmp_feature_require(mib_to_asn_type)
|
|
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
|
|
|
static netsnmp_data_list *auto_tables;
|
|
|
|
typedef struct data_set_tables_s {
|
|
netsnmp_table_data_set *table_set;
|
|
} data_set_tables;
|
|
|
|
typedef struct data_set_cache_s {
|
|
void *data;
|
|
size_t data_len;
|
|
} data_set_cache;
|
|
|
|
#define STATE_ACTION 1
|
|
#define STATE_COMMIT 2
|
|
#define STATE_UNDO 3
|
|
#define STATE_FREE 4
|
|
|
|
typedef struct newrow_stash_s {
|
|
netsnmp_table_row *newrow;
|
|
int state;
|
|
int created;
|
|
int deleted;
|
|
} newrow_stash;
|
|
|
|
/** @defgroup table_dataset table_dataset
|
|
* Helps you implement a table with automatted storage.
|
|
* @ingroup table_data
|
|
*
|
|
* This handler helps you implement a table where all the data is
|
|
* expected to be stored within the agent itself and not in some
|
|
* external storage location. It handles all MIB requests including
|
|
* GETs, GETNEXTs and SETs. It's possible to simply create a table
|
|
* without actually ever defining a handler to be called when SNMP
|
|
* requests come in. To use the data, you can either attach a
|
|
* sub-handler that merely uses/manipulates the data further when
|
|
* requests come in, or you can loop through it externally when it's
|
|
* actually needed. This handler is most useful in cases where a
|
|
* table is holding configuration data for something which gets
|
|
* triggered via another event.
|
|
*
|
|
* NOTE NOTE NOTE: This helper isn't complete and is likely to change
|
|
* somewhat over time. Specifically, the way it stores data
|
|
* internally may change drastically.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
void
|
|
netsnmp_init_table_dataset(void) {
|
|
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
|
register_app_config_handler("table",
|
|
netsnmp_config_parse_table_set, NULL,
|
|
"tableoid");
|
|
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
|
register_app_config_handler("add_row", netsnmp_config_parse_add_row,
|
|
NULL, "table_name indexes... values...");
|
|
}
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Table maintenance
|
|
*
|
|
* ================================== */
|
|
|
|
/** deletes a single dataset table data.
|
|
* returns the (possibly still good) next pointer of the deleted data object.
|
|
*/
|
|
NETSNMP_STATIC_INLINE netsnmp_table_data_set_storage *
|
|
netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
|
|
{
|
|
netsnmp_table_data_set_storage *nextPtr = NULL;
|
|
if (data) {
|
|
nextPtr = data->next;
|
|
SNMP_FREE(data->data.voidp);
|
|
}
|
|
SNMP_FREE(data);
|
|
return nextPtr;
|
|
}
|
|
|
|
/** deletes all the data from this node and beyond in the linked list */
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
|
|
{
|
|
|
|
while (data) {
|
|
data = netsnmp_table_dataset_delete_data(data);
|
|
}
|
|
}
|
|
|
|
/** deletes all the data from this node and beyond in the linked list */
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
|
|
{
|
|
netsnmp_table_data_set_storage *data;
|
|
|
|
if (!row)
|
|
return;
|
|
|
|
data = (netsnmp_table_data_set_storage*)netsnmp_table_data_delete_row(row);
|
|
netsnmp_table_dataset_delete_all_data(data);
|
|
}
|
|
|
|
/** adds a new row to a dataset table */
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
|
|
netsnmp_table_row *row)
|
|
{
|
|
if (!table)
|
|
return;
|
|
netsnmp_table_data_add_row(table->table, row);
|
|
}
|
|
|
|
/** adds a new row to a dataset table */
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
|
|
netsnmp_table_row *origrow,
|
|
netsnmp_table_row *newrow)
|
|
{
|
|
if (!table)
|
|
return;
|
|
netsnmp_table_data_replace_row(table->table, origrow, newrow);
|
|
}
|
|
|
|
/** removes a row from the table, but doesn't delete/free the column values */
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
|
|
netsnmp_table_row *row)
|
|
{
|
|
if (!table)
|
|
return;
|
|
|
|
netsnmp_table_data_remove_and_delete_row(table->table, row);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW */
|
|
|
|
/** removes a row from the table and then deletes it (and all its data) */
|
|
NETSNMP_INLINE void
|
|
netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
|
|
netsnmp_table_row *row)
|
|
{
|
|
netsnmp_table_data_set_storage *data;
|
|
|
|
if (!table)
|
|
return;
|
|
|
|
data = (netsnmp_table_data_set_storage *)
|
|
netsnmp_table_data_remove_and_delete_row(table->table, row);
|
|
|
|
netsnmp_table_dataset_delete_all_data(data);
|
|
}
|
|
|
|
/** Create a netsnmp_table_data_set structure given a table_data definition */
|
|
netsnmp_table_data_set *
|
|
netsnmp_create_table_data_set(const char *table_name)
|
|
{
|
|
netsnmp_table_data_set *table_set =
|
|
SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
|
|
if (!table_set)
|
|
return NULL;
|
|
table_set->table = netsnmp_create_table_data(table_name);
|
|
return table_set;
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET
|
|
void netsnmp_delete_table_data_set(netsnmp_table_data_set *table_set)
|
|
{
|
|
netsnmp_table_data_set_storage *ptr, *next;
|
|
netsnmp_table_row *prow, *pnextrow;
|
|
|
|
for (ptr = table_set->default_row; ptr; ptr = next) {
|
|
next = ptr->next;
|
|
free(ptr);
|
|
}
|
|
table_set->default_row = NULL;
|
|
for (prow = table_set->table->first_row; prow; prow = pnextrow) {
|
|
pnextrow = prow->next;
|
|
netsnmp_table_dataset_remove_and_delete_row(table_set, prow);
|
|
}
|
|
table_set->table->first_row = NULL;
|
|
netsnmp_table_data_delete_table(table_set->table);
|
|
free(table_set);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET */
|
|
|
|
/** clones a dataset row, including all data. */
|
|
netsnmp_table_row *
|
|
netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
|
|
{
|
|
netsnmp_table_data_set_storage *data, **newrowdata;
|
|
netsnmp_table_row *newrow;
|
|
|
|
if (!row)
|
|
return NULL;
|
|
|
|
newrow = netsnmp_table_data_clone_row(row);
|
|
if (!newrow)
|
|
return NULL;
|
|
|
|
data = (netsnmp_table_data_set_storage *) row->data;
|
|
|
|
if (data) {
|
|
for (newrowdata =
|
|
(netsnmp_table_data_set_storage **) &(newrow->data); data;
|
|
newrowdata = &((*newrowdata)->next), data = data->next) {
|
|
|
|
*newrowdata = netsnmp_memdup(data,
|
|
sizeof(netsnmp_table_data_set_storage));
|
|
if (!*newrowdata) {
|
|
netsnmp_table_dataset_delete_row(newrow);
|
|
return NULL;
|
|
}
|
|
|
|
if (data->data.voidp) {
|
|
(*newrowdata)->data.voidp =
|
|
netsnmp_memdup(data->data.voidp, data->data_len);
|
|
if (!(*newrowdata)->data.voidp) {
|
|
netsnmp_table_dataset_delete_row(newrow);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return newrow;
|
|
}
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Default row operations
|
|
*
|
|
* ================================== */
|
|
|
|
/** creates a new row from an existing defined default set */
|
|
netsnmp_table_row *
|
|
netsnmp_table_data_set_create_row_from_defaults
|
|
(netsnmp_table_data_set_storage *defrow)
|
|
{
|
|
netsnmp_table_row *row;
|
|
row = netsnmp_create_table_data_row();
|
|
if (!row)
|
|
return NULL;
|
|
for (; defrow; defrow = defrow->next) {
|
|
netsnmp_set_row_column(row, defrow->column, defrow->type,
|
|
defrow->data.voidp, defrow->data_len);
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if (defrow->writable)
|
|
netsnmp_mark_row_column_writable(row, defrow->column, 1);
|
|
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
|
}
|
|
return row;
|
|
}
|
|
|
|
/** adds a new default row to a table_set.
|
|
* Arguments should be the table_set, column number, variable type and
|
|
* finally a 1 if it is allowed to be writable, or a 0 if not. If the
|
|
* default_value field is not NULL, it will be used to populate new
|
|
* valuse in that column fro newly created rows. It is copied into the
|
|
* storage template (free your calling argument).
|
|
*
|
|
* returns SNMPERR_SUCCESS or SNMPERR_FAILURE
|
|
*/
|
|
int
|
|
netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
|
|
unsigned int column,
|
|
int type, int writable,
|
|
void *default_value,
|
|
size_t default_value_len)
|
|
{
|
|
netsnmp_table_data_set_storage *new_col, *ptr, *pptr;
|
|
|
|
if (!table_set)
|
|
return SNMPERR_GENERR;
|
|
|
|
/*
|
|
* double check
|
|
*/
|
|
new_col =
|
|
netsnmp_table_data_set_find_column(table_set->default_row, column);
|
|
if (new_col != NULL) {
|
|
if (new_col->type == type && new_col->writable == writable)
|
|
return SNMPERR_SUCCESS;
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
|
|
if (new_col == NULL)
|
|
return SNMPERR_GENERR;
|
|
new_col->type = type;
|
|
new_col->writable = writable;
|
|
new_col->column = column;
|
|
if (default_value) {
|
|
new_col->data.voidp = netsnmp_memdup(default_value, default_value_len);
|
|
new_col->data_len = default_value_len;
|
|
}
|
|
if (table_set->default_row == NULL)
|
|
table_set->default_row = new_col;
|
|
else {
|
|
/* sort in order just because (needed for add_row support) */
|
|
for (ptr = table_set->default_row, pptr = NULL;
|
|
ptr;
|
|
pptr = ptr, ptr = ptr->next) {
|
|
if (ptr->column > column) {
|
|
new_col->next = ptr;
|
|
if (pptr)
|
|
pptr->next = new_col;
|
|
else
|
|
table_set->default_row = new_col;
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
}
|
|
if (pptr)
|
|
pptr->next = new_col;
|
|
else
|
|
snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row");
|
|
}
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
/** adds multiple data column definitions to each row. Functionally,
|
|
* this is a wrapper around calling netsnmp_table_set_add_default_row
|
|
* repeatedly for you.
|
|
*/
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW
|
|
void
|
|
netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
|
|
{
|
|
va_list debugargs;
|
|
unsigned int column;
|
|
int type, writable;
|
|
void *data;
|
|
size_t data_len;
|
|
|
|
va_start(debugargs, tset);
|
|
|
|
while ((column = va_arg(debugargs, unsigned int)) != 0) {
|
|
type = va_arg(debugargs, int);
|
|
writable = va_arg(debugargs, int);
|
|
data = va_arg(debugargs, void *);
|
|
data_len = va_arg(debugargs, size_t);
|
|
netsnmp_table_set_add_default_row(tset, column, type, writable,
|
|
data, data_len);
|
|
}
|
|
|
|
va_end(debugargs);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW */
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: MIB maintenance
|
|
*
|
|
* ================================== */
|
|
|
|
/** Given a netsnmp_table_data_set definition, create a handler for it */
|
|
netsnmp_mib_handler *
|
|
netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
|
|
{
|
|
netsnmp_mib_handler *ret = NULL;
|
|
|
|
if (!data_set) {
|
|
snmp_log(LOG_INFO,
|
|
"netsnmp_get_table_data_set_handler(NULL) called\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret =
|
|
netsnmp_create_handler(TABLE_DATA_SET_NAME,
|
|
netsnmp_table_data_set_helper_handler);
|
|
if (ret) {
|
|
ret->flags |= MIB_HANDLER_AUTO_NEXT;
|
|
ret->myvoid = (void *) data_set;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/** register a given data_set at a given oid (specified in the
|
|
netsnmp_handler_registration pointer). The
|
|
reginfo->handler->access_method *may* be null if the call doesn't
|
|
ever want to be called for SNMP operations.
|
|
*/
|
|
int
|
|
netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
|
|
netsnmp_table_data_set *data_set,
|
|
netsnmp_table_registration_info *table_info)
|
|
{
|
|
netsnmp_mib_handler *handler;
|
|
int ret;
|
|
|
|
if (NULL == table_info) {
|
|
/*
|
|
* allocate the table if one wasn't allocated
|
|
*/
|
|
table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
|
|
if (table_info == NULL)
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
if (NULL == table_info->indexes && data_set->table->indexes_template) {
|
|
/*
|
|
* copy the indexes in
|
|
*/
|
|
table_info->indexes =
|
|
snmp_clone_varbind(data_set->table->indexes_template);
|
|
}
|
|
|
|
if ((!table_info->min_column || !table_info->max_column) &&
|
|
(data_set->default_row)) {
|
|
/*
|
|
* determine min/max columns
|
|
*/
|
|
unsigned int mincol = 0xffffffff, maxcol = 0;
|
|
netsnmp_table_data_set_storage *row;
|
|
|
|
for (row = data_set->default_row; row; row = row->next) {
|
|
mincol = SNMP_MIN(mincol, row->column);
|
|
maxcol = SNMP_MAX(maxcol, row->column);
|
|
}
|
|
if (!table_info->min_column)
|
|
table_info->min_column = mincol;
|
|
if (!table_info->max_column)
|
|
table_info->max_column = maxcol;
|
|
}
|
|
|
|
handler = netsnmp_get_table_data_set_handler(data_set);
|
|
if (!handler ||
|
|
(netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
|
|
snmp_log(LOG_ERR, "could not create table data set handler\n");
|
|
netsnmp_handler_free(handler);
|
|
netsnmp_handler_registration_free(reginfo);
|
|
return MIB_REGISTRATION_FAILED;
|
|
}
|
|
|
|
ret = netsnmp_register_table_data(reginfo, data_set->table,
|
|
table_info);
|
|
if (ret == SNMPERR_SUCCESS && reginfo->handler)
|
|
netsnmp_handler_owns_table_info(reginfo->handler->next);
|
|
return ret;
|
|
}
|
|
|
|
newrow_stash *
|
|
netsnmp_table_data_set_create_newrowstash
|
|
(netsnmp_table_data_set *datatable,
|
|
netsnmp_table_request_info *table_info)
|
|
{
|
|
newrow_stash *newrowstash = NULL;
|
|
netsnmp_table_row *newrow = NULL;
|
|
|
|
newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
|
|
|
|
if (newrowstash != NULL) {
|
|
newrowstash->created = 1;
|
|
newrow = netsnmp_table_data_set_create_row_from_defaults
|
|
(datatable->default_row);
|
|
newrow->indexes = snmp_clone_varbind(table_info->indexes);
|
|
newrowstash->newrow = newrow;
|
|
}
|
|
|
|
return newrowstash;
|
|
}
|
|
|
|
/* implements the table data helper. This is the routine that takes
|
|
* care of all SNMP requests coming into the table. */
|
|
int
|
|
netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
|
|
netsnmp_handler_registration
|
|
*reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
netsnmp_table_data_set_storage *data = NULL;
|
|
netsnmp_table_request_info *table_info;
|
|
netsnmp_request_info *request;
|
|
netsnmp_table_row *row = NULL;
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
netsnmp_oid_stash_node **stashp = NULL;
|
|
netsnmp_table_row *newrow = NULL;
|
|
newrow_stash *newrowstash = NULL;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
if (!handler)
|
|
return SNMPERR_GENERR;
|
|
|
|
DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n"));
|
|
for (request = requests; request; request = request->next) {
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
netsnmp_table_data_set *datatable =
|
|
(netsnmp_table_data_set *) handler->myvoid;
|
|
const oid * const suffix =
|
|
requests->requestvb->name + reginfo->rootoid_len + 2;
|
|
const size_t suffix_len =
|
|
requests->requestvb->name_length - (reginfo->rootoid_len + 2);
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
if (request->processed)
|
|
continue;
|
|
|
|
/*
|
|
* extract our stored data and table info
|
|
*/
|
|
row = netsnmp_extract_table_row(request);
|
|
table_info = netsnmp_extract_table_info(request);
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if (MODE_IS_SET(reqinfo->mode)) {
|
|
|
|
/*
|
|
* use a cached copy of the row for modification
|
|
*/
|
|
|
|
/*
|
|
* cache location: may have been created already by other
|
|
* SET requests in the same master request.
|
|
*/
|
|
stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo,
|
|
datatable,
|
|
table_info);
|
|
if (NULL == stashp) {
|
|
netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
|
|
continue;
|
|
}
|
|
|
|
newrowstash
|
|
= (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
|
|
|
|
if (!newrowstash) {
|
|
if (!row) {
|
|
if (datatable->allow_creation) {
|
|
/*
|
|
* entirely new row. Create the row from the template
|
|
*/
|
|
newrowstash =
|
|
netsnmp_table_data_set_create_newrowstash(
|
|
datatable, table_info);
|
|
newrow = newrowstash->newrow;
|
|
} else if (datatable->rowstatus_column == 0) {
|
|
/*
|
|
* A RowStatus object may be used to control the
|
|
* creation of a new row. But if this object
|
|
* isn't declared (and the table isn't marked as
|
|
* 'auto-create'), then we can't create a new row.
|
|
*/
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_NOCREATION);
|
|
continue;
|
|
}
|
|
} else {
|
|
/*
|
|
* existing row that needs to be modified
|
|
*/
|
|
newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
|
|
if (newrowstash == NULL) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_GENERR);
|
|
continue;
|
|
}
|
|
newrow = netsnmp_table_data_set_clone_row(row);
|
|
newrowstash->newrow = newrow;
|
|
}
|
|
netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
|
|
newrowstash);
|
|
} else {
|
|
newrow = newrowstash->newrow;
|
|
}
|
|
/*
|
|
* all future SET data modification operations use this
|
|
* temp pointer
|
|
*/
|
|
if (reqinfo->mode == MODE_SET_RESERVE1 ||
|
|
reqinfo->mode == MODE_SET_RESERVE2)
|
|
row = newrow;
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
if (row)
|
|
data = (netsnmp_table_data_set_storage *) row->data;
|
|
|
|
if (!row || !table_info || !data) {
|
|
if (!table_info
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
|| !MODE_IS_SET(reqinfo->mode)
|
|
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
|
) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_NOSUCHINSTANCE);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
data =
|
|
netsnmp_table_data_set_find_column(data, table_info->colnum);
|
|
|
|
switch (reqinfo->mode) {
|
|
case MODE_GET:
|
|
case MODE_GETNEXT:
|
|
case MODE_GETBULK: /* XXXWWW */
|
|
if (!data || data->type == SNMP_NOSUCHINSTANCE) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_NOSUCHINSTANCE);
|
|
} else {
|
|
/*
|
|
* Note: data->data.voidp can be NULL, e.g. when a zero-length
|
|
* octet string has been stored in the table cache.
|
|
*/
|
|
netsnmp_table_data_build_result(reginfo, reqinfo, request,
|
|
row,
|
|
table_info->colnum,
|
|
data->type,
|
|
(u_char*)data->data.voidp,
|
|
data->data_len);
|
|
}
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case MODE_SET_RESERVE1:
|
|
if (data) {
|
|
/*
|
|
* Can we modify the existing row?
|
|
*/
|
|
if (!data->writable) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_NOTWRITABLE);
|
|
} else if (request->requestvb->type != data->type) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_WRONGTYPE);
|
|
}
|
|
} else if (datatable->rowstatus_column == table_info->colnum) {
|
|
/*
|
|
* Otherwise, this is where we create a new row using
|
|
* the RowStatus object (essentially duplicating the
|
|
* steps followed earlier in the 'allow_creation' case)
|
|
*/
|
|
switch (*(request->requestvb->val.integer)) {
|
|
case RS_CREATEANDGO:
|
|
case RS_CREATEANDWAIT:
|
|
newrowstash =
|
|
netsnmp_table_data_set_create_newrowstash(
|
|
datatable, table_info);
|
|
newrow = newrowstash->newrow;
|
|
row = newrow;
|
|
netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
|
|
newrowstash);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_RESERVE2:
|
|
/*
|
|
* If the agent receives a SET request for an object in a non-existant
|
|
* row, then the RESERVE1 pass will create the row automatically.
|
|
*
|
|
* But since the row doesn't exist at that point, the test for whether
|
|
* the object is writable or not will be skipped. So we need to check
|
|
* for this possibility again here.
|
|
*
|
|
* Similarly, if row creation is under the control of the RowStatus
|
|
* object (i.e. allow_creation == 0), but this particular request
|
|
* doesn't include such an object, then the row won't have been created,
|
|
* and the writable check will also have been skipped. Again - check here.
|
|
*/
|
|
if (data && data->writable == 0) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_NOTWRITABLE);
|
|
continue;
|
|
}
|
|
if (datatable->rowstatus_column == table_info->colnum) {
|
|
switch (*(request->requestvb->val.integer)) {
|
|
case RS_ACTIVE:
|
|
case RS_NOTINSERVICE:
|
|
/*
|
|
* Can only operate on pre-existing rows.
|
|
*/
|
|
if (!newrowstash || newrowstash->created) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_INCONSISTENTVALUE);
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case RS_CREATEANDGO:
|
|
case RS_CREATEANDWAIT:
|
|
/*
|
|
* Can only operate on newly created rows.
|
|
*/
|
|
if (!(newrowstash && newrowstash->created)) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_INCONSISTENTVALUE);
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case RS_DESTROY:
|
|
/*
|
|
* Can operate on new or pre-existing rows.
|
|
*/
|
|
break;
|
|
|
|
case RS_NOTREADY:
|
|
default:
|
|
/*
|
|
* Not a valid value to Set
|
|
*/
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_WRONGVALUE);
|
|
continue;
|
|
}
|
|
}
|
|
if (!data ) {
|
|
netsnmp_set_request_error(reqinfo, request,
|
|
SNMP_ERR_NOCREATION);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* modify row and set new value
|
|
*/
|
|
SNMP_FREE(data->data.string);
|
|
data->data.string = (u_char *)
|
|
netsnmp_strdup_and_null(request->requestvb->val.string,
|
|
request->requestvb->val_len);
|
|
if (!data->data.string) {
|
|
netsnmp_set_request_error(reqinfo, requests,
|
|
SNMP_ERR_RESOURCEUNAVAILABLE);
|
|
}
|
|
data->data_len = request->requestvb->val_len;
|
|
|
|
if (datatable->rowstatus_column == table_info->colnum) {
|
|
switch (*(request->requestvb->val.integer)) {
|
|
case RS_CREATEANDGO:
|
|
/*
|
|
* XXX: check legality
|
|
*/
|
|
*(data->data.integer) = RS_ACTIVE;
|
|
break;
|
|
|
|
case RS_CREATEANDWAIT:
|
|
/*
|
|
* XXX: check legality
|
|
*/
|
|
*(data->data.integer) = RS_NOTINSERVICE;
|
|
break;
|
|
|
|
case RS_DESTROY:
|
|
newrowstash->deleted = 1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_ACTION:
|
|
|
|
/*
|
|
* Install the new row into the stored table.
|
|
* Do this only *once* per row ....
|
|
*/
|
|
if (newrowstash->state != STATE_ACTION) {
|
|
newrowstash->state = STATE_ACTION;
|
|
if (newrowstash->created) {
|
|
netsnmp_table_dataset_add_row(datatable, newrow);
|
|
} else {
|
|
netsnmp_table_dataset_replace_row(datatable,
|
|
row, newrow);
|
|
}
|
|
}
|
|
/*
|
|
* ... but every (relevant) varbind in the request will
|
|
* need to know about this new row, so update the
|
|
* per-request row information regardless
|
|
*/
|
|
if (newrowstash->created) {
|
|
netsnmp_request_add_list_data(request,
|
|
netsnmp_create_data_list(TABLE_DATA_NAME,
|
|
newrow, NULL));
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_UNDO:
|
|
/*
|
|
* extract the new row, replace with the old or delete
|
|
*/
|
|
if (newrowstash->state != STATE_UNDO) {
|
|
newrowstash->state = STATE_UNDO;
|
|
if (newrowstash->created) {
|
|
netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
|
|
} else {
|
|
netsnmp_table_dataset_replace_row(datatable,
|
|
newrow, row);
|
|
netsnmp_table_dataset_delete_row(newrow);
|
|
}
|
|
newrow = NULL;
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_COMMIT:
|
|
if (newrowstash->state != STATE_COMMIT) {
|
|
newrowstash->state = STATE_COMMIT;
|
|
if (!newrowstash->created) {
|
|
netsnmp_request_info *req;
|
|
netsnmp_table_dataset_delete_row(row);
|
|
|
|
/* Walk the request list to update the reference to the old row w/ th new one */
|
|
for (req = requests; req; req=req->next) {
|
|
|
|
/*
|
|
* For requests that have the old row values,
|
|
* so add the newly-created row information.
|
|
*/
|
|
if ((netsnmp_table_row *) netsnmp_extract_table_row(req) == row) {
|
|
netsnmp_request_remove_list_data(req, TABLE_DATA_ROW);
|
|
netsnmp_request_add_list_data(req,
|
|
netsnmp_create_data_list(TABLE_DATA_ROW, newrow, NULL));
|
|
}
|
|
}
|
|
|
|
row = NULL;
|
|
}
|
|
if (newrowstash->deleted) {
|
|
netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
|
|
newrow = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MODE_SET_FREE:
|
|
if (newrowstash && newrowstash->state != STATE_FREE) {
|
|
newrowstash->state = STATE_FREE;
|
|
netsnmp_table_dataset_delete_row(newrow);
|
|
newrow = NULL;
|
|
}
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
snmp_log(LOG_ERR,
|
|
"table_dataset: unknown mode passed into the handler\n");
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
}
|
|
|
|
/* next handler called automatically - 'AUTO_NEXT' */
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/**
|
|
* extracts a netsnmp_table_data_set pointer from a given request
|
|
*/
|
|
NETSNMP_INLINE netsnmp_table_data_set *
|
|
netsnmp_extract_table_data_set(netsnmp_request_info *request)
|
|
{
|
|
return (netsnmp_table_data_set *)
|
|
netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
|
|
}
|
|
|
|
/**
|
|
* extracts a netsnmp_table_data_set pointer from a given request
|
|
*/
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN
|
|
netsnmp_table_data_set_storage *
|
|
netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
|
|
unsigned int column)
|
|
{
|
|
netsnmp_table_data_set_storage *data =
|
|
(netsnmp_table_data_set_storage*)netsnmp_extract_table_row_data( request );
|
|
if (data) {
|
|
data = netsnmp_table_data_set_find_column(data, column);
|
|
}
|
|
return data;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN */
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Config-based operation
|
|
*
|
|
* ================================== */
|
|
|
|
/** registers a table_dataset so that the "add_row" snmpd.conf token
|
|
* can be used to add data to this table. If registration_name is
|
|
* NULL then the name used when the table was created will be used
|
|
* instead.
|
|
*
|
|
* @todo create a properly free'ing registeration pointer for the
|
|
* datalist, and get the datalist freed at shutdown.
|
|
*/
|
|
void
|
|
netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
|
|
char *registration_name)
|
|
{
|
|
data_set_tables *tables;
|
|
tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
|
|
if (!tables)
|
|
return;
|
|
tables->table_set = table_set;
|
|
if (!registration_name) {
|
|
registration_name = table_set->table->name;
|
|
}
|
|
netsnmp_add_list_data(&auto_tables,
|
|
netsnmp_create_data_list(registration_name,
|
|
tables, free)); /* XXX */
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE
|
|
/** Undo the effect of netsnmp_register_auto_data_table().
|
|
*/
|
|
void
|
|
netsnmp_unregister_auto_data_table(netsnmp_table_data_set *table_set,
|
|
char *registration_name)
|
|
{
|
|
netsnmp_remove_list_node(&auto_tables, registration_name
|
|
? registration_name : table_set->table->name);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE */
|
|
|
|
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
|
static void
|
|
_table_set_add_indexes(netsnmp_table_data_set *table_set, struct tree *tp)
|
|
{
|
|
oid name[MAX_OID_LEN];
|
|
size_t name_length = MAX_OID_LEN;
|
|
struct index_list *index;
|
|
struct tree *indexnode;
|
|
u_char type;
|
|
int fixed_len = 0;
|
|
|
|
/*
|
|
* loop through indexes and add types
|
|
*/
|
|
for (index = tp->indexes; index; index = index->next) {
|
|
if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
|
|
(NULL ==
|
|
(indexnode = get_tree(name, name_length, get_tree_head())))) {
|
|
config_pwarn("can't instatiate table since "
|
|
"I don't know anything about one index");
|
|
snmp_log(LOG_WARNING, " index %s not found in tree\n",
|
|
index->ilabel);
|
|
return; /* xxx mem leak */
|
|
}
|
|
|
|
type = mib_to_asn_type(indexnode->type);
|
|
if (type == (u_char) - 1) {
|
|
config_pwarn("unknown index type");
|
|
return; /* xxx mem leak */
|
|
}
|
|
/*
|
|
* if implied, mark it as such. also mark fixed length
|
|
* octet strings as implied (ie no length prefix) as well.
|
|
* */
|
|
if ((TYPE_OCTETSTR == indexnode->type) && /* octet str */
|
|
(NULL != indexnode->ranges) && /* & has range */
|
|
(NULL == indexnode->ranges->next) && /* but only one */
|
|
(indexnode->ranges->high == /* & high==low */
|
|
indexnode->ranges->low)) {
|
|
type |= ASN_PRIVATE;
|
|
fixed_len = indexnode->ranges->high;
|
|
}
|
|
else if (index->isimplied)
|
|
type |= ASN_PRIVATE;
|
|
|
|
DEBUGMSGTL(("table_set_add_table",
|
|
"adding default index of type %d\n", type));
|
|
netsnmp_table_dataset_add_index(table_set, type);
|
|
|
|
/*
|
|
* hack alert: for fixed lenght strings, save the
|
|
* lenght for use during oid parsing.
|
|
*/
|
|
if (fixed_len) {
|
|
/*
|
|
* find last (just added) index
|
|
*/
|
|
netsnmp_variable_list *var = table_set->table->indexes_template;
|
|
while (NULL != var->next_variable)
|
|
var = var->next_variable;
|
|
var->val_len = fixed_len;
|
|
}
|
|
}
|
|
}
|
|
/** @internal */
|
|
void
|
|
netsnmp_config_parse_table_set(const char *token, char *line)
|
|
{
|
|
oid table_name[MAX_OID_LEN];
|
|
size_t table_name_length = MAX_OID_LEN;
|
|
struct tree *tp;
|
|
netsnmp_table_data_set *table_set;
|
|
data_set_tables *tables;
|
|
unsigned int mincol = 0xffffff, maxcol = 0;
|
|
char *pos;
|
|
|
|
/*
|
|
* instatiate a fake table based on MIB information
|
|
*/
|
|
DEBUGMSGTL(("9:table_set_add_table", "processing '%s'\n", line));
|
|
if (NULL != (pos = strchr(line,' '))) {
|
|
config_pwarn("ignoring extra tokens on line");
|
|
snmp_log(LOG_WARNING," ignoring '%s'\n", pos);
|
|
*pos = '\0';
|
|
}
|
|
|
|
/*
|
|
* check for duplicate table
|
|
*/
|
|
tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line);
|
|
if (NULL != tables) {
|
|
config_pwarn("duplicate table definition");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* parse oid and find tree structure
|
|
*/
|
|
if (!snmp_parse_oid(line, table_name, &table_name_length)) {
|
|
config_pwarn
|
|
("can't instatiate table since I can't parse the table name");
|
|
return;
|
|
}
|
|
if(NULL == (tp = get_tree(table_name, table_name_length,
|
|
get_tree_head()))) {
|
|
config_pwarn("can't instatiate table since "
|
|
"I can't find mib information about it");
|
|
return;
|
|
}
|
|
|
|
if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
|
|
config_pwarn("can't instatiate table since it doesn't appear to be "
|
|
"a proper table (no children)");
|
|
return;
|
|
}
|
|
|
|
table_set = netsnmp_create_table_data_set(line);
|
|
|
|
/*
|
|
* check for augments indexes
|
|
*/
|
|
if (NULL != tp->augments) {
|
|
oid name[MAX_OID_LEN];
|
|
size_t name_length = MAX_OID_LEN;
|
|
struct tree *tp2;
|
|
|
|
if (!snmp_parse_oid(tp->augments, name, &name_length)) {
|
|
config_pwarn("I can't parse the augment table name");
|
|
snmp_log(LOG_WARNING, " can't parse %s\n", tp->augments);
|
|
SNMP_FREE (table_set);
|
|
return;
|
|
}
|
|
if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) {
|
|
config_pwarn("can't instatiate table since "
|
|
"I can't find mib information about augment table");
|
|
snmp_log(LOG_WARNING, " table %s not found in tree\n",
|
|
tp->augments);
|
|
SNMP_FREE (table_set);
|
|
return;
|
|
}
|
|
_table_set_add_indexes(table_set, tp2);
|
|
}
|
|
|
|
_table_set_add_indexes(table_set, tp);
|
|
|
|
/*
|
|
* loop through children and add each column info
|
|
*/
|
|
for (tp = tp->child_list; tp; tp = tp->next_peer) {
|
|
int canwrite = 0;
|
|
u_char type;
|
|
type = mib_to_asn_type(tp->type);
|
|
if (type == (u_char) - 1) {
|
|
config_pwarn("unknown column type");
|
|
SNMP_FREE (table_set);
|
|
return; /* xxx mem leak */
|
|
}
|
|
|
|
DEBUGMSGTL(("table_set_add_table",
|
|
"adding column %s(%ld) of type %d (access %d)\n",
|
|
tp->label, tp->subid, type, tp->access));
|
|
|
|
switch (tp->access) {
|
|
case MIB_ACCESS_CREATE:
|
|
table_set->allow_creation = 1;
|
|
/* fallthrough */
|
|
case MIB_ACCESS_READWRITE:
|
|
case MIB_ACCESS_WRITEONLY:
|
|
canwrite = 1;
|
|
/* fallthrough */
|
|
case MIB_ACCESS_READONLY:
|
|
DEBUGMSGTL(("table_set_add_table",
|
|
"adding column %ld of type %d\n", tp->subid, type));
|
|
netsnmp_table_set_add_default_row(table_set, tp->subid, type,
|
|
canwrite, NULL, 0);
|
|
mincol = SNMP_MIN(mincol, tp->subid);
|
|
maxcol = SNMP_MAX(maxcol, tp->subid);
|
|
break;
|
|
|
|
case MIB_ACCESS_NOACCESS:
|
|
case MIB_ACCESS_NOTIFY:
|
|
break;
|
|
|
|
default:
|
|
config_pwarn("unknown column access type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* register the table
|
|
*/
|
|
netsnmp_register_table_data_set(netsnmp_create_handler_registration
|
|
(line, NULL, table_name,
|
|
table_name_length,
|
|
HANDLER_CAN_RWRITE), table_set, NULL);
|
|
|
|
netsnmp_register_auto_data_table(table_set, NULL);
|
|
}
|
|
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
|
|
|
/** @internal */
|
|
void
|
|
netsnmp_config_parse_add_row(const char *token, char *line)
|
|
{
|
|
char buf[SNMP_MAXBUF_MEDIUM];
|
|
char tname[SNMP_MAXBUF_MEDIUM];
|
|
size_t buf_size;
|
|
int rc;
|
|
|
|
data_set_tables *tables;
|
|
netsnmp_variable_list *vb; /* containing only types */
|
|
netsnmp_table_row *row;
|
|
netsnmp_table_data_set_storage *dr;
|
|
|
|
line = copy_nword(line, tname, sizeof(tname));
|
|
|
|
tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
|
|
if (!tables) {
|
|
config_pwarn("Unknown table trying to add a row");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* do the indexes first
|
|
*/
|
|
row = netsnmp_create_table_data_row();
|
|
|
|
for (vb = tables->table_set->table->indexes_template; vb;
|
|
vb = vb->next_variable) {
|
|
if (!line) {
|
|
config_pwarn("missing an index value");
|
|
SNMP_FREE (row);
|
|
return;
|
|
}
|
|
|
|
DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n",
|
|
vb->type));
|
|
buf_size = sizeof(buf);
|
|
line = read_config_read_memory(vb->type, line, buf, &buf_size);
|
|
netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
|
|
}
|
|
|
|
/*
|
|
* then do the data
|
|
*/
|
|
for (dr = tables->table_set->default_row; dr; dr = dr->next) {
|
|
if (!line) {
|
|
config_pwarn("missing a data value. "
|
|
"All columns must be specified.");
|
|
snmp_log(LOG_WARNING," can't find value for column %d\n",
|
|
dr->column - 1);
|
|
SNMP_FREE (row);
|
|
return;
|
|
}
|
|
|
|
buf_size = sizeof(buf);
|
|
line = read_config_read_memory(dr->type, line, buf, &buf_size);
|
|
DEBUGMSGTL(("table_set_add_row",
|
|
"adding data at column %d of type %d\n", dr->column,
|
|
dr->type));
|
|
netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
if (dr->writable)
|
|
netsnmp_mark_row_column_writable(row, dr->column, 1); /* make writable */
|
|
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
|
}
|
|
rc = netsnmp_table_data_add_row(tables->table_set->table, row);
|
|
if (SNMPERR_SUCCESS != rc) {
|
|
config_pwarn("error adding table row");
|
|
}
|
|
if (NULL != line) {
|
|
config_pwarn("extra data value. Too many columns specified.");
|
|
snmp_log(LOG_WARNING," extra data '%s'\n", line);
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
netsnmp_oid_stash_node **
|
|
netsnmp_table_dataset_get_or_create_stash(netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_table_data_set *datatable,
|
|
netsnmp_table_request_info *table_info)
|
|
{
|
|
netsnmp_oid_stash_node **stashp = NULL;
|
|
char buf[256]; /* is this reasonable size?? */
|
|
size_t len;
|
|
int rc;
|
|
|
|
rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:",
|
|
datatable->table->name);
|
|
if ((-1 == rc) || ((size_t)rc >= sizeof(buf))) {
|
|
snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name);
|
|
return NULL;
|
|
}
|
|
|
|
len = sizeof(buf) - rc;
|
|
rc = snprint_objid(&buf[rc], len, table_info->index_oid,
|
|
table_info->index_oid_len);
|
|
if (-1 == rc) {
|
|
snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name);
|
|
return NULL;
|
|
}
|
|
|
|
stashp = (netsnmp_oid_stash_node **)
|
|
netsnmp_table_get_or_create_row_stash(reqinfo, (u_char *) buf);
|
|
return stashp;
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW
|
|
netsnmp_table_row *
|
|
netsnmp_table_dataset_get_newrow(netsnmp_request_info *request,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
int rootoid_len,
|
|
netsnmp_table_data_set *datatable,
|
|
netsnmp_table_request_info *table_info)
|
|
{
|
|
oid * const suffix = request->requestvb->name + rootoid_len + 2;
|
|
size_t suffix_len = request->requestvb->name_length - (rootoid_len + 2);
|
|
netsnmp_oid_stash_node **stashp;
|
|
newrow_stash *newrowstash;
|
|
|
|
stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo, datatable,
|
|
table_info);
|
|
if (NULL == stashp)
|
|
return NULL;
|
|
|
|
newrowstash = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
|
|
if (NULL == newrowstash)
|
|
return NULL;
|
|
|
|
return newrowstash->newrow;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW */
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Row operations
|
|
*
|
|
* ================================== */
|
|
|
|
/** returns the first row in the table */
|
|
netsnmp_table_row *
|
|
netsnmp_table_data_set_get_first_row(netsnmp_table_data_set *table)
|
|
{
|
|
return netsnmp_table_data_get_first_row(table->table);
|
|
}
|
|
|
|
/** returns the next row in the table */
|
|
netsnmp_table_row *
|
|
netsnmp_table_data_set_get_next_row(netsnmp_table_data_set *table,
|
|
netsnmp_table_row *row)
|
|
{
|
|
return netsnmp_table_data_get_next_row(table->table, row);
|
|
}
|
|
|
|
int
|
|
netsnmp_table_set_num_rows(netsnmp_table_data_set *table)
|
|
{
|
|
if (!table)
|
|
return 0;
|
|
return netsnmp_table_data_num_rows(table->table);
|
|
}
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Column operations
|
|
*
|
|
* ================================== */
|
|
|
|
/** Finds a column within a given storage set, given the pointer to
|
|
the start of the storage set list.
|
|
*/
|
|
netsnmp_table_data_set_storage *
|
|
netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
|
|
unsigned int column)
|
|
{
|
|
while (start && start->column != column)
|
|
start = start->next;
|
|
return start;
|
|
}
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
/**
|
|
* marks a given column in a row as writable or not.
|
|
*/
|
|
int
|
|
netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
|
|
int writable)
|
|
{
|
|
netsnmp_table_data_set_storage *data;
|
|
|
|
if (!row)
|
|
return SNMPERR_GENERR;
|
|
|
|
data = (netsnmp_table_data_set_storage *) row->data;
|
|
data = netsnmp_table_data_set_find_column(data, column);
|
|
|
|
if (!data) {
|
|
/*
|
|
* create it
|
|
*/
|
|
data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
|
|
if (!data) {
|
|
snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
|
|
return SNMPERR_MALLOC;
|
|
}
|
|
data->column = column;
|
|
data->writable = writable;
|
|
data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
|
|
row->data = data;
|
|
} else {
|
|
data->writable = writable;
|
|
}
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
/**
|
|
* Sets a given column in a row with data given a type, value,
|
|
* and length. Data is memdup'ed by the function, at least if
|
|
* type != SNMP_NOSUCHINSTANCE and if value_len > 0.
|
|
*
|
|
* @param[in] row Pointer to the row to be modified.
|
|
* @param[in] column Index of the column to be modified.
|
|
* @param[in] type Either the ASN type of the value to be set or
|
|
* SNMP_NOSUCHINSTANCE.
|
|
* @param[in] value If type != SNMP_NOSUCHINSTANCE, pointer to the
|
|
* new value. May be NULL if value_len == 0, e.g. when storing a
|
|
* zero-length octet string. Ignored when type == SNMP_NOSUCHINSTANCE.
|
|
* @param[in] value_len If type != SNMP_NOSUCHINSTANCE, number of bytes
|
|
* occupied by *value. Ignored when type == SNMP_NOSUCHINSTANCE.
|
|
*
|
|
* @return SNMPERR_SUCCESS upon success; SNMPERR_MALLOC when out of memory;
|
|
* or SNMPERR_GENERR when row == 0 or when type does not match the datatype
|
|
* of the data stored in *row.
|
|
*
|
|
*/
|
|
int
|
|
netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
|
|
int type, const void *value, size_t value_len)
|
|
{
|
|
netsnmp_table_data_set_storage *data;
|
|
|
|
if (!row)
|
|
return SNMPERR_GENERR;
|
|
|
|
data = (netsnmp_table_data_set_storage *) row->data;
|
|
data = netsnmp_table_data_set_find_column(data, column);
|
|
|
|
if (!data) {
|
|
/*
|
|
* create it
|
|
*/
|
|
data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
|
|
if (!data) {
|
|
snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
|
|
return SNMPERR_MALLOC;
|
|
}
|
|
|
|
data->column = column;
|
|
data->type = type;
|
|
data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
|
|
row->data = data;
|
|
}
|
|
|
|
/* Transitions from / to SNMP_NOSUCHINSTANCE are allowed, but no other transitions. */
|
|
if (data->type != type && data->type != SNMP_NOSUCHINSTANCE
|
|
&& type != SNMP_NOSUCHINSTANCE)
|
|
return SNMPERR_GENERR;
|
|
|
|
/* Return now if neither the type nor the data itself has been modified. */
|
|
if (data->type == type && data->data_len == value_len
|
|
&& (value == NULL || memcmp(&data->data.string, value, value_len) == 0))
|
|
return SNMPERR_SUCCESS;
|
|
|
|
/* Reallocate memory and store the new value. */
|
|
data->data.voidp = realloc(data->data.voidp, value ? value_len : 0);
|
|
if (value && value_len && !data->data.voidp) {
|
|
data->data_len = 0;
|
|
data->type = SNMP_NOSUCHINSTANCE;
|
|
snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
|
|
return SNMPERR_MALLOC;
|
|
}
|
|
if (value && value_len)
|
|
memcpy(data->data.string, value, value_len);
|
|
data->type = type;
|
|
data->data_len = value_len;
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ==================================
|
|
*
|
|
* Data Set API: Index operations
|
|
*
|
|
* ================================== */
|
|
|
|
/** adds an index to the table. Call this repeatly for each index. */
|
|
void
|
|
netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
|
|
{
|
|
if (!table)
|
|
return;
|
|
netsnmp_table_data_add_index(table->table, type);
|
|
}
|
|
|
|
/** adds multiple indexes to a table_dataset helper object.
|
|
* To end the list, use a 0 after the list of ASN index types. */
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES
|
|
void
|
|
netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
|
|
...)
|
|
{
|
|
va_list debugargs;
|
|
int type;
|
|
|
|
va_start(debugargs, tset);
|
|
|
|
if (tset)
|
|
while ((type = va_arg(debugargs, int)) != 0)
|
|
netsnmp_table_data_add_index(tset->table, (u_char)type);
|
|
|
|
va_end(debugargs);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES */
|
|
|
|
#else /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
|
|
netsnmp_feature_unused(table_dataset);
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
|
|
/** @}
|
|
*/
|