net-snmp/agent/helpers/table_array.c

937 lines
33 KiB
C

/*
* table_array.c
* $Id$
*
* 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_array.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <net-snmp/agent/table.h>
#include <net-snmp/library/container.h>
#include <net-snmp/library/snmp_assert.h>
netsnmp_feature_child_of(table_array_all, mib_helpers)
netsnmp_feature_child_of(table_array_register,table_array_all)
netsnmp_feature_child_of(table_array_find_table_array_handler,table_array_all)
netsnmp_feature_child_of(table_array_extract_array_context,table_array_all)
netsnmp_feature_child_of(table_array_check_row_status,table_array_all)
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_CONTAINER
/*
* snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN -1
* snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1 0
* snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2 1
* snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION 2
* snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT 3
* snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE 4
* snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO 5
*/
static const char *mode_name[] = {
"Reserve 1",
"Reserve 2",
"Action",
"Commit",
"Free",
"Undo"
};
/*
* PRIVATE structure for holding important info for each table.
*/
typedef struct table_container_data_s {
/** registration info for the table */
netsnmp_table_registration_info *tblreg_info;
/** container for the table rows */
netsnmp_container *table;
/*
* mutex_type lock;
*/
/** do we want to group rows with the same index
* together when calling callbacks? */
int group_rows;
/** callbacks for this table */
netsnmp_table_array_callbacks *cb;
} table_container_data;
/** @defgroup table_array table_array
* Helps you implement a table when data can be stored locally. The data is stored in a sorted array, using a binary search for lookups.
* @ingroup table
*
* The table_array handler is used (automatically) in conjuntion
* with the @link table table@endlink handler. It is primarily
* intended to be used with the mib2c configuration file
* mib2c.array-user.conf.
*
* The code generated by mib2c is useful when you have control of
* the data for each row. If you cannot control when rows are added
* and deleted (or at least be notified of changes to row data),
* then this handler is probably not for you.
*
* This handler makes use of callbacks (function pointers) to
* handle various tasks. Code is generated for each callback,
* but will need to be reviewed and flushed out by the user.
*
* NOTE NOTE NOTE: Once place where mib2c is somewhat lacking
* is with regards to tables with external indices. If your
* table makes use of one or more external indices, please
* review the generated code very carefully for comments
* regarding external indices.
*
* NOTE NOTE NOTE: This helper, the API and callbacks are still
* being tested and may change.
*
* The generated code will define a structure for storage of table
* related data. This structure must be used, as it contains the index
* OID for the row, which is used for keeping the array sorted. You can
* add addition fields or data to the structure for your own use.
*
* The generated code will also have code to handle SNMP-SET processing.
* If your table does not support any SET operations, simply comment
* out the \#define \<PREFIX\>_SET_HANDLING (where \<PREFIX\> is your
* table name) in the header file.
*
* SET processing modifies the row in-place. The duplicate_row
* callback will be called to save a copy of the original row.
* In the event of a failure before the commite phase, the
* row_copy callback will be called to restore the original row
* from the copy.
*
* Code will be generated to handle row creation. This code may be
* disabled by commenting out the \#define \<PREFIX\>_ROW_CREATION
* in the header file.
*
* If your table contains a RowStatus object, by default the
* code will not allow object in an active row to be modified.
* To allow active rows to be modified, remove the comment block
* around the \#define \<PREFIX\>_CAN_MODIFY_ACTIVE_ROW in the header
* file.
*
* Code will be generated to maintain a secondary index for all
* rows, stored in a binary tree. This is very useful for finding
* rows by a key other than the OID index. By default, the functions
* for maintaining this tree will be based on a character string.
* NOTE: this will likely be made into a more generic mechanism,
* using new callback methods, in the near future.
*
* The generated code contains many TODO comments. Make sure you
* check each one to see if it applies to your code. Examples include
* checking indices for syntax (ranges, etc), initializing default
* values in newly created rows, checking for row activation and
* deactivation requirements, etc.
*
* @{
*/
/**********************************************************************
**********************************************************************
* *
* *
* PUBLIC Registration functions *
* *
* *
**********************************************************************
**********************************************************************/
/** register specified callbacks for the specified table/oid. If the
group_rows parameter is set, the row related callbacks will be
called once for each unique row index. Otherwise, each callback
will be called only once, for all objects.
*/
int
netsnmp_table_container_register(netsnmp_handler_registration *reginfo,
netsnmp_table_registration_info *tabreg,
netsnmp_table_array_callbacks *cb,
netsnmp_container *container,
int group_rows)
{
table_container_data *tad = SNMP_MALLOC_TYPEDEF(table_container_data);
if (!tad)
return SNMPERR_GENERR;
tad->tblreg_info = tabreg; /* we need it too, but it really is not ours */
if (!cb) {
snmp_log(LOG_ERR, "table_array registration with no callbacks\n" );
free(tad); /* SNMP_FREE is overkill for local var */
return SNMPERR_GENERR;
}
/*
* check for required callbacks
*/
if ((cb->can_set &&
((NULL==cb->duplicate_row) || (NULL==cb->delete_row) ||
(NULL==cb->row_copy)) )) {
snmp_log(LOG_ERR, "table_array registration with incomplete "
"callback structure.\n");
free(tad); /* SNMP_FREE is overkill for local var */
return SNMPERR_GENERR;
}
if (NULL==container) {
tad->table = netsnmp_container_find("table_array");
snmp_log(LOG_ERR, "table_array couldn't allocate container\n" );
free(tad); /* SNMP_FREE is overkill for local var */
return SNMPERR_GENERR;
} else
tad->table = container;
if (NULL==tad->table->compare)
tad->table->compare = netsnmp_compare_netsnmp_index;
if (NULL==tad->table->ncompare)
tad->table->ncompare = netsnmp_ncompare_netsnmp_index;
tad->cb = cb;
reginfo->handler->myvoid = tad;
return netsnmp_register_table(reginfo, tabreg);
}
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_REGISTER
int
netsnmp_table_array_register(netsnmp_handler_registration *reginfo,
netsnmp_table_registration_info *tabreg,
netsnmp_table_array_callbacks *cb,
netsnmp_container *container,
int group_rows)
{
netsnmp_mib_handler *handler =
netsnmp_create_handler(reginfo->handlerName,
netsnmp_table_array_helper_handler);
if (!handler ||
(netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
snmp_log(LOG_ERR, "could not create table array handler\n");
netsnmp_handler_free(handler);
netsnmp_handler_registration_free(reginfo);
return SNMP_ERR_GENERR;
}
return netsnmp_table_container_register(reginfo, tabreg, cb,
container, group_rows);
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_REGISTER */
/** find the handler for the table_array helper. */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_FIND_TABLE_ARRAY_HANDLER
netsnmp_mib_handler *
netsnmp_find_table_array_handler(netsnmp_handler_registration *reginfo)
{
netsnmp_mib_handler *mh;
if (!reginfo)
return NULL;
mh = reginfo->handler;
while (mh) {
if (mh->access_method == netsnmp_table_array_helper_handler)
break;
mh = mh->next;
}
return mh;
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_FIND_TABLE_ARRAY_HANDLER */
/** find the context data used by the table_array helper */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_EXTRACT_ARRAY_CONTEXT
netsnmp_container *
netsnmp_extract_array_context(netsnmp_request_info *request)
{
return (netsnmp_container*)netsnmp_request_get_list_data(request, TABLE_ARRAY_NAME);
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_EXTRACT_ARRAY_CONTEXT */
/** this function is called to validate RowStatus transitions. */
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_CHECK_ROW_STATUS
int
netsnmp_table_array_check_row_status(netsnmp_table_array_callbacks *cb,
netsnmp_request_group *ag,
long *rs_new, long *rs_old)
{
netsnmp_index *row_ctx;
netsnmp_index *undo_ctx;
if (!ag || !cb)
return SNMPERR_GENERR;
row_ctx = ag->existing_row;
undo_ctx = ag->undo_info;
/*
* xxx-rks: revisit row delete scenario
*/
if (row_ctx) {
/*
* either a new row, or change to old row
*/
/*
* is it set to active?
*/
if (RS_IS_GOING_ACTIVE(*rs_new)) {
/*
* is it ready to be active?
*/
if ((NULL==cb->can_activate) ||
cb->can_activate(undo_ctx, row_ctx, ag))
*rs_new = RS_ACTIVE;
else
return SNMP_ERR_INCONSISTENTVALUE;
} else {
/*
* not going active
*/
if (undo_ctx) {
/*
* change
*/
if (RS_IS_ACTIVE(*rs_old)) {
/*
* check pre-reqs for deactivation
*/
if (cb->can_deactivate &&
!cb->can_deactivate(undo_ctx, row_ctx, ag)) {
return SNMP_ERR_INCONSISTENTVALUE;
}
}
} else {
/*
* new row
*/
}
if (*rs_new != RS_DESTROY) {
if ((NULL==cb->can_activate) ||
cb->can_activate(undo_ctx, row_ctx, ag))
*rs_new = RS_NOTINSERVICE;
else
*rs_new = RS_NOTREADY;
} else {
if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
return SNMP_ERR_INCONSISTENTVALUE;
}
ag->row_deleted = 1;
}
}
} else {
/*
* check pre-reqs for delete row
*/
if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
return SNMP_ERR_INCONSISTENTVALUE;
}
}
return SNMP_ERR_NOERROR;
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_CHECK_ROW_STATUS */
/** @} */
/** @cond */
/**********************************************************************
**********************************************************************
**********************************************************************
**********************************************************************
* *
* *
* *
* *
* EVERYTHING BELOW THIS IS PRIVATE IMPLEMENTATION DETAILS. *
* *
* *
* *
* *
**********************************************************************
**********************************************************************
**********************************************************************
**********************************************************************/
/**********************************************************************
**********************************************************************
* *
* *
* Structures, Utility/convenience functions *
* *
* *
**********************************************************************
**********************************************************************/
/*
* context info for SET requests
*/
#ifndef NETSNMP_NO_WRITE_SUPPORT
typedef struct set_context_s {
netsnmp_agent_request_info *agtreq_info;
table_container_data *tad;
int status;
} set_context;
#endif /* NETSNMP_NO_WRITE_SUPPORT */
void
build_new_oid(netsnmp_handler_registration *reginfo,
netsnmp_table_request_info *tblreq_info,
netsnmp_index *row, netsnmp_request_info *current)
{
oid coloid[MAX_OID_LEN];
if (!tblreq_info || !reginfo || !row || !current)
return;
memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
/** table.entry */
coloid[reginfo->rootoid_len] = 1;
/** table.entry.column */
coloid[reginfo->rootoid_len + 1] = tblreq_info->colnum;
/** table.entry.column.index */
memcpy(&coloid[reginfo->rootoid_len + 2], row->oids,
row->len * sizeof(oid));
snmp_set_var_objid(current->requestvb, coloid,
reginfo->rootoid_len + 2 + row->len);
}
/**********************************************************************
**********************************************************************
* *
* *
* GET procession functions *
* *
* *
**********************************************************************
**********************************************************************/
int
process_get_requests(netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *agtreq_info,
netsnmp_request_info *requests,
table_container_data * tad)
{
int rc = SNMP_ERR_NOERROR;
netsnmp_request_info *current;
netsnmp_index *row = NULL;
netsnmp_table_request_info *tblreq_info;
netsnmp_variable_list *var;
/*
* Loop through each of the requests, and
* try to find the appropriate row from the container.
*/
for (current = requests; current; current = current->next) {
var = current->requestvb;
DEBUGMSGTL(("table_array:get",
" process_get_request oid:"));
DEBUGMSGOID(("table_array:get", var->name,
var->name_length));
DEBUGMSG(("table_array:get", "\n"));
/*
* skip anything that doesn't need processing.
*/
if (current->processed != 0) {
DEBUGMSGTL(("table_array:get", "already processed\n"));
continue;
}
/*
* Get pointer to the table information for this request. This
* information was saved by table_helper_handler. When
* debugging, we double check a few assumptions. For example,
* the table_helper_handler should enforce column boundaries.
*/
tblreq_info = netsnmp_extract_table_info(current);
netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
if ((agtreq_info->mode == MODE_GETNEXT) ||
(agtreq_info->mode == MODE_GETBULK)) {
/*
* find the row
*/
row = netsnmp_table_index_find_next_row(tad->table, tblreq_info);
if (!row) {
/*
* no results found.
*
* xxx-rks: how do we skip this entry for the next handler,
* but still allow it a chance to hit another handler?
*/
DEBUGMSGTL(("table_array:get", "no row found\n"));
netsnmp_set_request_error(agtreq_info, current,
SNMP_ENDOFMIBVIEW);
continue;
}
/*
* * if data was found, make sure it has the column we want
*/
/* xxx-rks: add suport for sparse tables */
/*
* build new oid
*/
build_new_oid(reginfo, tblreq_info, row, current);
} /** GETNEXT/GETBULK */
else {
netsnmp_index index;
index.oids = tblreq_info->index_oid;
index.len = tblreq_info->index_oid_len;
row = (netsnmp_index*)CONTAINER_FIND(tad->table, &index);
if (!row) {
DEBUGMSGTL(("table_array:get", "no row found\n"));
netsnmp_set_request_error(agtreq_info, current,
SNMP_NOSUCHINSTANCE);
continue;
}
} /** GET */
/*
* get the data
*/
rc = tad->cb->get_value(current, row, tblreq_info);
} /** for ( ... requests ... ) */
return rc;
}
/**********************************************************************
**********************************************************************
* *
* *
* SET procession functions *
* *
* *
**********************************************************************
**********************************************************************/
void
group_requests(netsnmp_agent_request_info *agtreq_info,
netsnmp_request_info *requests,
netsnmp_container *request_group, table_container_data * tad)
{
netsnmp_table_request_info *tblreq_info;
netsnmp_index *row, *tmp, index;
netsnmp_request_info *current;
netsnmp_request_group *g;
netsnmp_request_group_item *i;
for (current = requests; current; current = current->next) {
/*
* skip anything that doesn't need processing.
*/
if (current->processed != 0) {
DEBUGMSGTL(("table_array:group",
"already processed\n"));
continue;
}
/*
* 3.2.1 Setup and paranoia
* *
* * Get pointer to the table information for this request. This
* * information was saved by table_helper_handler. When
* * debugging, we double check a few assumptions. For example,
* * the table_helper_handler should enforce column boundaries.
*/
row = NULL;
tblreq_info = netsnmp_extract_table_info(current);
netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
/*
* search for index
*/
index.oids = tblreq_info->index_oid;
index.len = tblreq_info->index_oid_len;
tmp = (netsnmp_index*)CONTAINER_FIND(request_group, &index);
if (tmp) {
DEBUGMSGTL(("table_array:group",
" existing group:"));
DEBUGMSGOID(("table_array:group", index.oids,
index.len));
DEBUGMSG(("table_array:group", "\n"));
g = (netsnmp_request_group *) tmp;
i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
if (i == NULL)
return;
i->ri = current;
i->tri = tblreq_info;
i->next = g->list;
g->list = i;
/** xxx-rks: store map of colnum to request */
continue;
}
DEBUGMSGTL(("table_array:group", " new group"));
DEBUGMSGOID(("table_array:group", index.oids,
index.len));
DEBUGMSG(("table_array:group", "\n"));
g = SNMP_MALLOC_TYPEDEF(netsnmp_request_group);
i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
if (i == NULL || g == NULL) {
SNMP_FREE(i);
SNMP_FREE(g);
return;
}
g->list = i;
g->table = tad->table;
i->ri = current;
i->tri = tblreq_info;
/** xxx-rks: store map of colnum to request */
/*
* search for row. all changes are made to the original row,
* later, we'll make a copy in undo_info before we start processing.
*/
row = g->existing_row = (netsnmp_index*)CONTAINER_FIND(tad->table, &index);
if (!g->existing_row) {
if (!tad->cb->create_row) {
#ifndef NETSNMP_NO_WRITE_SUPPORT
if(MODE_IS_SET(agtreq_info->mode))
netsnmp_set_request_error(agtreq_info, current,
SNMP_ERR_NOTWRITABLE);
else
#endif /* NETSNMP_NO_WRITE_SUPPORT */
netsnmp_set_request_error(agtreq_info, current,
SNMP_NOSUCHINSTANCE);
free(g);
free(i);
continue;
}
/** use undo_info temporarily */
row = g->existing_row = tad->cb->create_row(&index);
if (!row) {
/* xxx-rks : parameter to create_row to allow
* for better error reporting. */
netsnmp_set_request_error(agtreq_info, current,
SNMP_ERR_GENERR);
free(g);
free(i);
continue;
}
g->row_created = 1;
}
g->index.oids = row->oids;
g->index.len = row->len;
CONTAINER_INSERT(request_group, g);
} /** for( current ... ) */
}
#ifndef NETSNMP_NO_WRITE_SUPPORT
static void
release_netsnmp_request_group(netsnmp_index *g, void *v)
{
netsnmp_request_group_item *tmp;
netsnmp_request_group *group = (netsnmp_request_group *) g;
if (!g)
return;
while (group->list) {
tmp = group->list;
group->list = tmp->next;
free(tmp);
}
free(group);
}
static void
release_netsnmp_request_groups(void *vp)
{
netsnmp_container *c = (netsnmp_container*)vp;
CONTAINER_FOR_EACH(c, (netsnmp_container_obj_func*)
release_netsnmp_request_group, NULL);
CONTAINER_FREE(c);
}
static void
process_set_group(netsnmp_index *o, void *c)
{
/* xxx-rks: should we continue processing after an error?? */
set_context *context = (set_context *) c;
netsnmp_request_group *ag = (netsnmp_request_group *) o;
int rc = SNMP_ERR_NOERROR;
switch (context->agtreq_info->mode) {
case MODE_SET_RESERVE1:/** -> SET_RESERVE2 || SET_FREE */
/*
* if not a new row, save undo info
*/
if (ag->row_created == 0) {
if (context->tad->cb->duplicate_row)
ag->undo_info = context->tad->cb->duplicate_row(ag->existing_row);
else
ag->undo_info = NULL;
if (NULL == ag->undo_info) {
rc = SNMP_ERR_RESOURCEUNAVAILABLE;
break;
}
}
if (context->tad->cb->set_reserve1)
context->tad->cb->set_reserve1(ag);
break;
case MODE_SET_RESERVE2:/** -> SET_ACTION || SET_FREE */
if (context->tad->cb->set_reserve2)
context->tad->cb->set_reserve2(ag);
break;
case MODE_SET_ACTION:/** -> SET_COMMIT || SET_UNDO */
if (context->tad->cb->set_action)
context->tad->cb->set_action(ag);
break;
case MODE_SET_COMMIT:/** FINAL CHANCE ON SUCCESS */
if (ag->row_created == 0) {
/*
* this is an existing row, has it been deleted?
*/
if (ag->row_deleted == 1) {
DEBUGMSGT((TABLE_ARRAY_NAME, "action: deleting row\n"));
if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
rc = SNMP_ERR_COMMITFAILED;
break;
}
}
} else if (ag->row_deleted == 0) {
/*
* new row (that hasn't been deleted) should be inserted
*/
DEBUGMSGT((TABLE_ARRAY_NAME, "action: inserting row\n"));
if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
rc = SNMP_ERR_COMMITFAILED;
break;
}
}
if (context->tad->cb->set_commit)
context->tad->cb->set_commit(ag);
/** no more use for undo_info, so free it */
if (ag->undo_info) {
context->tad->cb->delete_row(ag->undo_info);
ag->undo_info = NULL;
}
#if 0
/* XXX-rks: finish row cooperative notifications
* if the table has requested it, send cooperative notifications
* for row operations.
*/
if (context->tad->notifications) {
if (ag->undo_info) {
if (!ag->existing_row)
netsnmp_monitor_notify(EVENT_ROW_DEL);
else
netsnmp_monitor_notify(EVENT_ROW_MOD);
}
else
netsnmp_monitor_notify(EVENT_ROW_ADD);
}
#endif
if ((ag->row_created == 0) && (ag->row_deleted == 1)) {
context->tad->cb->delete_row(ag->existing_row);
ag->existing_row = NULL;
}
break;
case MODE_SET_FREE:/** FINAL CHANCE ON FAILURE */
if (context->tad->cb->set_free)
context->tad->cb->set_free(ag);
/** no more use for undo_info, so free it */
if (ag->row_created == 1) {
if (context->tad->cb->delete_row)
context->tad->cb->delete_row(ag->existing_row);
ag->existing_row = NULL;
}
else {
if (context->tad->cb->delete_row)
context->tad->cb->delete_row(ag->undo_info);
ag->undo_info = NULL;
}
break;
case MODE_SET_UNDO:/** FINAL CHANCE ON FAILURE */
/*
* status already set - don't change it now
*/
if (context->tad->cb->set_undo)
context->tad->cb->set_undo(ag);
/*
* no more use for undo_info, so free it
*/
if (ag->row_created == 0) {
/*
* restore old values
*/
context->tad->cb->row_copy(ag->existing_row, ag->undo_info);
context->tad->cb->delete_row(ag->undo_info);
ag->undo_info = NULL;
}
else {
context->tad->cb->delete_row(ag->existing_row);
ag->existing_row = NULL;
}
break;
default:
snmp_log(LOG_ERR, "unknown mode processing SET for "
"netsnmp_table_array_helper_handler\n");
rc = SNMP_ERR_GENERR;
break;
}
if (rc)
netsnmp_set_request_error(context->agtreq_info,
ag->list->ri, rc);
}
int
process_set_requests(netsnmp_agent_request_info *agtreq_info,
netsnmp_request_info *requests,
table_container_data * tad, char *handler_name)
{
set_context context;
netsnmp_container *request_group;
/*
* create and save structure for set info
*/
request_group = (netsnmp_container*) netsnmp_agent_get_list_data
(agtreq_info, handler_name);
if (request_group == NULL) {
netsnmp_data_list *tmp;
request_group = netsnmp_container_find("request_group:"
"table_container");
request_group->compare = netsnmp_compare_netsnmp_index;
request_group->ncompare = netsnmp_ncompare_netsnmp_index;
DEBUGMSGTL(("table_array", "Grouping requests by oid\n"));
tmp = netsnmp_create_data_list(handler_name,
request_group,
release_netsnmp_request_groups);
netsnmp_agent_add_list_data(agtreq_info, tmp);
/*
* group requests.
*/
group_requests(agtreq_info, requests, request_group, tad);
}
/*
* process each group one at a time
*/
context.agtreq_info = agtreq_info;
context.tad = tad;
context.status = SNMP_ERR_NOERROR;
CONTAINER_FOR_EACH(request_group,
(netsnmp_container_obj_func*)process_set_group,
&context);
return context.status;
}
#endif /* NETSNMP_NO_WRITE_SUPPORT */
/**********************************************************************
**********************************************************************
* *
* *
* netsnmp_table_array_helper_handler() *
* *
* *
**********************************************************************
**********************************************************************/
int
netsnmp_table_array_helper_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *agtreq_info,
netsnmp_request_info *requests)
{
/*
* First off, get our pointer from the handler. This
* lets us get to the table registration information we
* saved in get_table_array_handler(), as well as the
* container where the actual table data is stored.
*/
int rc = SNMP_ERR_NOERROR;
table_container_data *tad = (table_container_data *)handler->myvoid;
if (agtreq_info->mode < 0 || agtreq_info->mode > 5) {
DEBUGMSGTL(("table_array", "Mode %d, Got request:\n",
agtreq_info->mode));
} else {
DEBUGMSGTL(("table_array", "Mode %s, Got request:\n",
mode_name[agtreq_info->mode]));
}
#ifndef NETSNMP_NO_WRITE_SUPPORT
if (MODE_IS_SET(agtreq_info->mode)) {
/*
* netsnmp_mutex_lock(&tad->lock);
*/
rc = process_set_requests(agtreq_info, requests,
tad, handler->handler_name);
/*
* netsnmp_mutex_unlock(&tad->lock);
*/
} else
#endif /* NETSNMP_NO_WRITE_SUPPORT */
rc = process_get_requests(reginfo, agtreq_info, requests, tad);
if (rc != SNMP_ERR_NOERROR) {
DEBUGMSGTL(("table_array", "processing returned rc %d\n", rc));
}
/*
* Now we've done our processing. If there is another handler below us,
* call them.
*/
if (handler->next) {
rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, requests);
if (rc != SNMP_ERR_NOERROR) {
DEBUGMSGTL(("table_array", "next handler returned rc %d\n", rc));
}
}
return rc;
}
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_CONTAINER */
/** @endcond */