diff options
author | Hideki Yamane <henrich@debian.org> | 2014-03-30 19:38:48 +0900 |
---|---|---|
committer | Hideki Yamane <henrich@debian.org> | 2014-03-30 19:38:48 +0900 |
commit | 7769a9595c3da9a35f31b42451b1f6c3ed4004fa (patch) | |
tree | 009bf8fd68af6bb1129e07dd8c1ed205010d81f8 /agent/helpers/row_merge.c | |
parent | 2e7891b0311204e0ecd5dc4a4334df01f3a6a1b4 (diff) | |
download | pkg-net-snmp-7769a9595c3da9a35f31b42451b1f6c3ed4004fa.tar.gz |
Imported Upstream version 5.7.2~dfsg
Diffstat (limited to 'agent/helpers/row_merge.c')
-rw-r--r-- | agent/helpers/row_merge.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/agent/helpers/row_merge.c b/agent/helpers/row_merge.c new file mode 100644 index 0000000..4a8e476 --- /dev/null +++ b/agent/helpers/row_merge.c @@ -0,0 +1,381 @@ +#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/row_merge.h> + +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +netsnmp_feature_provide(row_merge) +netsnmp_feature_child_of(row_merge, row_merge_all) +netsnmp_feature_child_of(row_merge_all, mib_helpers) + + +#ifndef NETSNMP_FEATURE_REMOVE_ROW_MERGE +/** @defgroup row_merge row_merge + * Calls sub handlers with request for one row at a time. + * @ingroup utilities + * This helper splits a whole bunch of requests into chunks based on the row + * index that they refer to, and passes all requests for a given row to the lower handlers. + * This is useful for handlers that don't want to process multiple rows at the + * same time, but are happy to iterate through the request list for a single row. + * @{ + */ + +/** returns a row_merge handler that can be injected into a given + * handler chain. + */ +netsnmp_mib_handler * +netsnmp_get_row_merge_handler(int prefix_len) +{ + netsnmp_mib_handler *ret = NULL; + ret = netsnmp_create_handler("row_merge", + netsnmp_row_merge_helper_handler); + if (ret) { + ret->myvoid = (void *)(intptr_t)prefix_len; + } + return ret; +} + +/** functionally the same as calling netsnmp_register_handler() but also + * injects a row_merge handler at the same time for you. */ +netsnmp_feature_child_of(register_row_merge, row_merge_all) +#ifndef NETSNMP_FEATURE_REMOVE_REGISTER_ROW_MERGE +int +netsnmp_register_row_merge(netsnmp_handler_registration *reginfo) +{ + netsnmp_inject_handler(reginfo, + netsnmp_get_row_merge_handler(reginfo->rootoid_len+1)); + return netsnmp_register_handler(reginfo); +} +#endif /* NETSNMP_FEATURE_REMOVE_REGISTER_ROW_MERGE */ + +static void +_rm_status_free(void *mem) +{ + netsnmp_row_merge_status *rm_status = (netsnmp_row_merge_status*)mem; + + if (NULL != rm_status->saved_requests) + free(rm_status->saved_requests); + + if (NULL != rm_status->saved_status) + free(rm_status->saved_status); + + free(mem); +} + + +/** retrieve row_merge_status + */ +netsnmp_row_merge_status * +netsnmp_row_merge_status_get(netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + int create_missing) +{ + netsnmp_row_merge_status *rm_status; + char buf[64]; + int rc; + + /* + * see if we've already been here + */ + rc = snprintf(buf, sizeof(buf), "row_merge:%p", reginfo); + if ((-1 == rc) || ((size_t)rc >= sizeof(buf))) { + snmp_log(LOG_ERR,"error creating key\n"); + return NULL; + } + + rm_status = (netsnmp_row_merge_status*)netsnmp_agent_get_list_data(reqinfo, buf); + if ((NULL == rm_status) && create_missing) { + netsnmp_data_list *data_list; + + rm_status = SNMP_MALLOC_TYPEDEF(netsnmp_row_merge_status); + if (NULL == rm_status) { + snmp_log(LOG_ERR,"error allocating memory\n"); + return NULL; + } + data_list = netsnmp_create_data_list(buf, rm_status, + _rm_status_free); + if (NULL == data_list) { + free(rm_status); + return NULL; + } + netsnmp_agent_add_list_data(reqinfo, data_list); + } + + return rm_status; +} + +/** Determine if this is the first row + * + * returns 1 if this is the first row for this pass of the handler. + */ +int +netsnmp_row_merge_status_first(netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo) +{ + netsnmp_row_merge_status *rm_status; + + /* + * find status + */ + rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0); + if (NULL == rm_status) + return 0; + + return (rm_status->count == 1) ? 1 : (rm_status->current == 1); +} + +/** Determine if this is the last row + * + * returns 1 if this is the last row for this pass of the handler. + */ +int +netsnmp_row_merge_status_last(netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo) +{ + netsnmp_row_merge_status *rm_status; + + /* + * find status + */ + rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0); + if (NULL == rm_status) + return 0; + + return (rm_status->count == 1) ? 1 : + (rm_status->current == rm_status->rows); +} + + +#define ROW_MERGE_WAITING 0 +#define ROW_MERGE_ACTIVE 1 +#define ROW_MERGE_DONE 2 +#define ROW_MERGE_HEAD 3 + +/** Implements the row_merge handler */ +int +netsnmp_row_merge_helper_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request, **saved_requests; + char *saved_status; + netsnmp_row_merge_status *rm_status; + int i, j, ret, tail, count, final_rc = SNMP_ERR_NOERROR; + + /* + * Use the prefix length as supplied during registration, rather + * than trying to second-guess what the MIB implementer wanted. + */ + int SKIP_OID = (int)(intptr_t)handler->myvoid; + + DEBUGMSGTL(("helper:row_merge", "Got request (%d): ", SKIP_OID)); + DEBUGMSGOID(("helper:row_merge", reginfo->rootoid, reginfo->rootoid_len)); + DEBUGMSG(("helper:row_merge", "\n")); + + /* + * find or create status + */ + rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 1); + + /* + * Count the requests, and set up an array to keep + * track of the original order. + */ + for (count = 0, request = requests; request; request = request->next) { + DEBUGIF("helper:row_merge") { + DEBUGMSGTL(("helper:row_merge", " got varbind: ")); + DEBUGMSGOID(("helper:row_merge", request->requestvb->name, + request->requestvb->name_length)); + DEBUGMSG(("helper:row_merge", "\n")); + } + count++; + } + + /* + * Optimization: skip all this if there is just one request + */ + if(count == 1) { + rm_status->count = count; + if (requests->processed) + return SNMP_ERR_NOERROR; + return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); + } + + /* + * we really should only have to do this once, instead of every pass. + * as a precaution, we'll do it every time, but put in some asserts + * to see if we have to. + */ + /* + * if the count changed, re-do everything + */ + if ((0 != rm_status->count) && (rm_status->count != count)) { + /* + * ok, i know next/bulk can cause this condition. Probably + * GET, too. need to rethink this mode counting. maybe + * add the mode to the rm_status structure? xxx-rks + */ + if ((reqinfo->mode != MODE_GET) && + (reqinfo->mode != MODE_GETNEXT) && + (reqinfo->mode != MODE_GETBULK)) { + netsnmp_assert((NULL != rm_status->saved_requests) && + (NULL != rm_status->saved_status)); + } + DEBUGMSGTL(("helper:row_merge", "count changed! do over...\n")); + + SNMP_FREE(rm_status->saved_requests); + SNMP_FREE(rm_status->saved_status); + + rm_status->count = 0; + rm_status->rows = 0; + } + + if (0 == rm_status->count) { + /* + * allocate memory for saved structure + */ + rm_status->saved_requests = + (netsnmp_request_info**)calloc(count+1, + sizeof(netsnmp_request_info*)); + rm_status->saved_status = (char*)calloc(count,sizeof(char)); + } + + saved_status = rm_status->saved_status; + saved_requests = rm_status->saved_requests; + + /* + * set up saved requests, and set any processed requests to done + */ + i = 0; + for (request = requests; request; request = request->next, i++) { + if (request->processed) { + saved_status[i] = ROW_MERGE_DONE; + DEBUGMSGTL(("helper:row_merge", " skipping processed oid: ")); + DEBUGMSGOID(("helper:row_merge", request->requestvb->name, + request->requestvb->name_length)); + DEBUGMSG(("helper:row_merge", "\n")); + } + else + saved_status[i] = ROW_MERGE_WAITING; + if (0 != rm_status->count) + netsnmp_assert(saved_requests[i] == request); + saved_requests[i] = request; + saved_requests[i]->prev = NULL; + } + saved_requests[i] = NULL; + + /* + * Note that saved_requests[count] is valid + * (because of the 'count+1' in the calloc above), + * but NULL (since it's past the end of the list). + * This simplifies the re-linking later. + */ + + /* + * Work through the (unprocessed) requests in order. + * For each of these, search the rest of the list for any + * matching indexes, and link them into a new list. + */ + for (i=0; i<count; i++) { + if (saved_status[i] != ROW_MERGE_WAITING) + continue; + + if (0 == rm_status->count) + rm_status->rows++; + DEBUGMSGTL(("helper:row_merge", " row %d oid[%d]: ", rm_status->rows, i)); + DEBUGMSGOID(("helper:row_merge", saved_requests[i]->requestvb->name, + saved_requests[i]->requestvb->name_length)); + DEBUGMSG(("helper:row_merge", "\n")); + + saved_requests[i]->next = NULL; + saved_status[i] = ROW_MERGE_HEAD; + tail = i; + for (j=i+1; j<count; j++) { + if (saved_status[j] != ROW_MERGE_WAITING) + continue; + + DEBUGMSGTL(("helper:row_merge", "? oid[%d]: ", j)); + DEBUGMSGOID(("helper:row_merge", + saved_requests[j]->requestvb->name, + saved_requests[j]->requestvb->name_length)); + if (!snmp_oid_compare( + saved_requests[i]->requestvb->name+SKIP_OID, + saved_requests[i]->requestvb->name_length-SKIP_OID, + saved_requests[j]->requestvb->name+SKIP_OID, + saved_requests[j]->requestvb->name_length-SKIP_OID)) { + DEBUGMSG(("helper:row_merge", " match\n")); + saved_requests[tail]->next = saved_requests[j]; + saved_requests[j]->next = NULL; + saved_requests[j]->prev = saved_requests[tail]; + saved_status[j] = ROW_MERGE_ACTIVE; + tail = j; + } + else + DEBUGMSG(("helper:row_merge", " no match\n")); + } + } + + /* + * not that we have a list for each row, call next handler... + */ + if (0 == rm_status->count) + rm_status->count = count; + rm_status->current = 0; + for (i=0; i<count; i++) { + if (saved_status[i] != ROW_MERGE_HEAD) + continue; + + /* + * found the head of a new row, + * call the next handler with this list + */ + rm_status->current++; + ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, + saved_requests[i]); + if (ret != SNMP_ERR_NOERROR) { + snmp_log(LOG_WARNING, + "bad rc (%d) from next handler in row_merge\n", ret); + if (SNMP_ERR_NOERROR == final_rc) + final_rc = ret; + } + } + + /* + * restore original linked list + */ + for (i=0; i<count; i++) { + saved_requests[i]->next = saved_requests[i+1]; + if (i>0) + saved_requests[i]->prev = saved_requests[i-1]; + } + + return final_rc; +} + +/** + * initializes the row_merge helper which then registers a row_merge + * handler as a run-time injectable handler for configuration file + * use. + */ +void +netsnmp_init_row_merge(void) +{ + netsnmp_register_handler_by_name("row_merge", + netsnmp_get_row_merge_handler(-1)); +} +#else /* NETSNMP_FEATURE_REMOVE_ROW_MERGE */ +netsnmp_feature_unused(row_merge); +#endif /* NETSNMP_FEATURE_REMOVE_ROW_MERGE */ + + +/** @} */ + |