diff options
Diffstat (limited to 'agent/helpers/table_array.c')
-rw-r--r-- | agent/helpers/table_array.c | 919 |
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 */ |