summaryrefslogtreecommitdiff
path: root/agent/helpers/table_array.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/helpers/table_array.c')
-rw-r--r--agent/helpers/table_array.c919
1 files changed, 919 insertions, 0 deletions
diff --git a/agent/helpers/table_array.c b/agent/helpers/table_array.c
new file mode 100644
index 0000000..7c98aae
--- /dev/null
+++ b/agent/helpers/table_array.c
@@ -0,0 +1,919 @@
+/*
+ * table_array.c
+ * $Id$
+ */
+
+#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_inject_handler(reginfo,
+ netsnmp_create_handler(reginfo->handlerName,
+ netsnmp_table_array_helper_handler));
+ 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 */