| /* |
| * 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 */ |