| /* |
| * $Id$ |
| * |
| */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #include <stdio.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_MALLOC_H |
| #include <malloc.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/types.h> |
| #include <net-snmp/library/snmp_api.h> |
| #include <net-snmp/library/container.h> |
| #include <net-snmp/library/tools.h> |
| #include <net-snmp/library/snmp_assert.h> |
| |
| #include <net-snmp/library/container_iterator.h> |
| |
| netsnmp_feature_child_of(container_iterator, container_types) |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR |
| /** |
| * Holds iterator information containing functions which should be called |
| * by the iterator_handler to loop over your data set and sort it in a |
| * SNMP specific manner. |
| * |
| * The iterator_info typedef can be used instead of directly calling this |
| * struct if you would prefer. |
| */ |
| typedef struct iterator_info_s { |
| /* |
| * netsnmp_conatiner must be first |
| */ |
| netsnmp_container c; |
| |
| /* |
| * iterator data |
| */ |
| Netsnmp_Iterator_Loop_Key *get_first; |
| Netsnmp_Iterator_Loop_Key *get_next; |
| |
| Netsnmp_Iterator_Loop_Data *get_data; |
| |
| Netsnmp_Iterator_Data *free_user_ctx; |
| |
| Netsnmp_Iterator_Ctx *init_loop_ctx; |
| Netsnmp_Iterator_Ctx *cleanup_loop_ctx; |
| Netsnmp_Iterator_Ctx_Dup *save_pos; |
| |
| Netsnmp_Iterator_Data * release_data; |
| Netsnmp_Iterator_Data * insert_data; |
| Netsnmp_Iterator_Data * remove_data; |
| |
| Netsnmp_Iterator_Op * get_size; |
| |
| int sorted; |
| |
| /** This can be used by client handlers to store any |
| information they need */ |
| void *user_ctx; |
| } iterator_info; |
| |
| /********************************************************************** |
| * |
| * iterator |
| * |
| **********************************************************************/ |
| static void * |
| _iterator_get(iterator_info *ii, const void *key) |
| { |
| int cmp, rc = SNMP_ERR_NOERROR; |
| netsnmp_ref_void best = { NULL }; |
| netsnmp_ref_void tmp = { NULL }; |
| netsnmp_ref_void loop_ctx = { NULL }; |
| |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_get")); |
| |
| if(ii->init_loop_ctx) |
| ii->init_loop_ctx(ii->user_ctx, &loop_ctx); |
| |
| rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp); |
| if(SNMP_ERR_NOERROR != rc) { |
| if(SNMP_ENDOFMIBVIEW != rc) |
| snmp_log(LOG_ERR, "bad rc %d from get_next\n", rc); |
| } |
| else { |
| for( ; |
| (NULL != tmp.val) && (SNMP_ERR_NOERROR == rc); |
| rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) { |
| |
| /* |
| * if keys are equal, we are done. |
| */ |
| cmp = ii->c.compare(tmp.val, key); |
| if(0 == cmp) { |
| best.val = tmp.val; |
| if(ii->get_data) |
| ii->get_data(ii->user_ctx, &loop_ctx, &best); |
| } |
| |
| /* |
| * if data is sorted and if key is greater, |
| * we are done (not found) |
| */ |
| if((cmp > 0) && ii->sorted) |
| break; |
| } /* end for */ |
| } |
| |
| if(ii->cleanup_loop_ctx) |
| ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx); |
| |
| return best.val; |
| } |
| |
| /** |
| * |
| * NOTE: the returned data context can be reused, to save from |
| * having to allocate memory repeatedly. However, in this case, |
| * the get_data and get_pos functions must be implemented to |
| * return unique memory that will be saved for later comparisons. |
| */ |
| static void * |
| _iterator_get_next(iterator_info *ii, const void *key) |
| { |
| int cmp, rc = SNMP_ERR_NOERROR; |
| netsnmp_ref_void best_val = { NULL }; |
| netsnmp_ref_void best_ctx = { NULL }; |
| netsnmp_ref_void tmp = { NULL }; |
| netsnmp_ref_void loop_ctx = { NULL }; |
| |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_get_next")); |
| |
| /* |
| * initialize loop context |
| */ |
| if(ii->init_loop_ctx) |
| ii->init_loop_ctx(ii->user_ctx, &loop_ctx); |
| |
| /* |
| * get first item |
| */ |
| rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp); |
| if(SNMP_ERR_NOERROR == rc) { |
| /* |
| * special case: if key is null, find the first item. |
| * this is each if the container is sorted, since we're |
| * already done! Otherwise, get the next item for the |
| * first comparison in the loop below. |
| */ |
| if (NULL == key) { |
| if(ii->get_data) |
| ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1); |
| best_val.val = tmp.val; |
| if(ii->sorted) |
| tmp.val = NULL; /* so we skip for loop */ |
| else |
| rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp); |
| } |
| /* |
| * loop over remaining items |
| */ |
| for( ; |
| (NULL != tmp.val) && (rc == SNMP_ERR_NOERROR); |
| rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) { |
| |
| /* |
| * if we have a key, this is a get-next, and we need to compare |
| * the key to the tmp value to see if the tmp value is greater |
| * than the key, but less than any previous match. |
| * |
| * if there is no key, this is a get-first, and we need to |
| * compare the best value agains the tmp value to see if the |
| * tmp value is lesser than the best match. |
| */ |
| if(key) /* get next */ |
| cmp = ii->c.compare(tmp.val, key); |
| else { /* get first */ |
| /* |
| * best value and tmp value should never be equal, |
| * otherwise we'd be comparing a pointer to itself. |
| * (see note on context reuse in comments above function. |
| */ |
| if(best_val.val == tmp.val) { |
| snmp_log(LOG_ERR,"illegal reuse of data context in " |
| "container_iterator\n"); |
| rc = SNMP_ERR_GENERR; |
| break; |
| } |
| cmp = ii->c.compare(best_val.val, tmp.val); |
| } |
| if(cmp > 0) { |
| /* |
| * if we don't have a key (get-first) or a current best match, |
| * then the comparison above is all we need to know that |
| * tmp is the best match. otherwise, compare against the |
| * current best match. |
| */ |
| if((NULL == key) || (NULL == best_val.val) || |
| ((cmp=ii->c.compare(tmp.val, best_val.val)) < 0) ) { |
| DEBUGMSGT(("container_iterator:results"," best match\n")); |
| best_val.val = tmp.val; |
| if(ii->get_data) |
| ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1); |
| } |
| } |
| else if((cmp == 0) && ii->sorted && key) { |
| /* |
| * if keys are equal and container is sorted, then we know |
| * the next key will be the one we want. |
| * NOTE: if no vars, treat as generr, since we |
| * went past the end of the container when we know |
| * the next item is the one we want. (IGN-A) |
| */ |
| rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp); |
| if(SNMP_ERR_NOERROR == rc) { |
| best_val.val = tmp.val; |
| if(ii->get_data) |
| ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1); |
| } |
| else if(SNMP_ENDOFMIBVIEW == rc) |
| rc = SNMPERR_GENERR; /* not found */ |
| break; |
| } |
| |
| } /* end for */ |
| } |
| |
| /* |
| * no vars is ok, except as noted above (IGN-A) |
| */ |
| if(SNMP_ENDOFMIBVIEW == rc) |
| rc = SNMP_ERR_NOERROR; |
| |
| /* |
| * get data, iff necessary |
| * clear return value iff errors |
| */ |
| if(SNMP_ERR_NOERROR == rc) { |
| if(ii->get_data && best_val.val) { |
| rc = ii->get_data(ii->user_ctx, &best_ctx, &best_val); |
| if(SNMP_ERR_NOERROR != rc) { |
| snmp_log(LOG_ERR, "bad rc %d from get_data\n", rc); |
| best_val.val = NULL; |
| } |
| } |
| } |
| else if(SNMP_ENDOFMIBVIEW != rc) { |
| snmp_log(LOG_ERR, "bad rc %d from get_next\n", rc); |
| best_val.val = NULL; |
| } |
| |
| /* |
| * if we have a saved loop ctx, clean it up |
| */ |
| if((best_ctx.val != NULL) && (best_ctx.val != loop_ctx.val) && |
| (ii->cleanup_loop_ctx)) |
| ii->cleanup_loop_ctx(ii->user_ctx,&best_ctx); |
| |
| /* |
| * clean up loop ctx |
| */ |
| if(ii->cleanup_loop_ctx) |
| ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx); |
| |
| DEBUGMSGT(("container_iterator:results"," returning %p\n", best_val.val)); |
| return best_val.val; |
| } |
| |
| /********************************************************************** |
| * |
| * container |
| * |
| **********************************************************************/ |
| static void |
| _iterator_free(iterator_info *ii) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_free")); |
| |
| if(NULL == ii) |
| return; |
| |
| if(ii->user_ctx) |
| ii->free_user_ctx(ii->user_ctx,ii->user_ctx); |
| |
| free(ii); |
| } |
| |
| static void * |
| _iterator_find(iterator_info *ii, const void *data) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_find")); |
| |
| if((NULL == ii) || (NULL == data)) |
| return NULL; |
| |
| return _iterator_get(ii, data); |
| } |
| |
| static void * |
| _iterator_find_next(iterator_info *ii, const void *data) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_find_next")); |
| |
| if(NULL == ii) |
| return NULL; |
| |
| return _iterator_get_next(ii, data); |
| } |
| |
| static int |
| _iterator_insert(iterator_info *ii, const void *data) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_insert")); |
| |
| if(NULL == ii) |
| return -1; |
| |
| if(NULL == ii->insert_data) |
| return -1; |
| |
| return ii->insert_data(ii->user_ctx, data); |
| } |
| |
| static int |
| _iterator_remove(iterator_info *ii, const void *data) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_remove")); |
| |
| if(NULL == ii) |
| return -1; |
| |
| if(NULL == ii->remove_data) |
| return -1; |
| |
| return ii->remove_data(ii->user_ctx, data); |
| } |
| |
| static int |
| _iterator_release(iterator_info *ii, const void *data) |
| { |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_release")); |
| |
| if(NULL == ii) |
| return -1; |
| |
| if(NULL == ii->release_data) |
| return -1; |
| |
| return ii->release_data(ii->user_ctx, data); |
| } |
| |
| static size_t |
| _iterator_size(iterator_info *ii) |
| { |
| size_t count = 0; |
| netsnmp_ref_void loop_ctx = { NULL }; |
| netsnmp_ref_void tmp = { NULL }; |
| |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_size")); |
| |
| if(NULL == ii) |
| return -1; |
| |
| if(NULL != ii->get_size) |
| return ii->get_size(ii->user_ctx); |
| |
| /* |
| * no get_size. loop and count ourselves |
| */ |
| if(ii->init_loop_ctx) |
| ii->init_loop_ctx(ii->user_ctx, &loop_ctx); |
| |
| for( ii->get_first(ii->user_ctx, &loop_ctx, &tmp); |
| NULL != tmp.val; |
| ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) |
| ++count; |
| |
| if(ii->cleanup_loop_ctx) |
| ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx); |
| |
| return count; |
| } |
| |
| static void |
| _iterator_for_each(iterator_info *ii, netsnmp_container_obj_func *f, |
| void *ctx) |
| { |
| netsnmp_ref_void loop_ctx = { NULL }; |
| netsnmp_ref_void tmp = { NULL }; |
| |
| DEBUGMSGT(("container_iterator",">%s\n", "_iterator_foreach")); |
| |
| if(NULL == ii) |
| return; |
| |
| if(ii->init_loop_ctx) |
| ii->init_loop_ctx(ii->user_ctx, &loop_ctx); |
| |
| for( ii->get_first(ii->user_ctx, &loop_ctx, &tmp); |
| NULL != tmp.val; |
| ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) |
| (*f) (tmp.val, ctx); |
| |
| if(ii->cleanup_loop_ctx) |
| ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx); |
| } |
| |
| static void |
| _iterator_clear(netsnmp_container *container, netsnmp_container_obj_func *f, |
| void *context) |
| { |
| snmp_log(LOG_WARNING,"clear is meaningless for iterator container.\n"); |
| } |
| |
| /********************************************************************** |
| * |
| */ |
| netsnmp_container* |
| netsnmp_container_iterator_get(void *iterator_user_ctx, |
| netsnmp_container_compare * compare, |
| Netsnmp_Iterator_Loop_Key * get_first, |
| Netsnmp_Iterator_Loop_Key * get_next, |
| Netsnmp_Iterator_Loop_Data * get_data, |
| Netsnmp_Iterator_Ctx_Dup * save_pos, |
| Netsnmp_Iterator_Ctx * init_loop_ctx, |
| Netsnmp_Iterator_Ctx * cleanup_loop_ctx, |
| Netsnmp_Iterator_Data * free_user_ctx, |
| int sorted) |
| { |
| iterator_info *ii; |
| |
| /* |
| * sanity checks |
| */ |
| if(get_data && ! save_pos) { |
| snmp_log(LOG_ERR, "save_pos required with get_data\n"); |
| return NULL; |
| } |
| |
| /* |
| * allocate memory |
| */ |
| ii = SNMP_MALLOC_TYPEDEF(iterator_info); |
| if (NULL==ii) { |
| snmp_log(LOG_ERR, "couldn't allocate memory\n"); |
| return NULL; |
| } |
| |
| /* |
| * init container structure with iterator functions |
| */ |
| ii->c.cfree = (netsnmp_container_rc*)_iterator_free; |
| ii->c.compare = compare; |
| ii->c.get_size = (netsnmp_container_size*)_iterator_size; |
| ii->c.init = NULL; |
| ii->c.insert = (netsnmp_container_op*)_iterator_insert; |
| ii->c.remove = (netsnmp_container_op*)_iterator_remove; |
| ii->c.release = (netsnmp_container_op*)_iterator_release; |
| ii->c.find = (netsnmp_container_rtn*)_iterator_find; |
| ii->c.find_next = (netsnmp_container_rtn*)_iterator_find_next; |
| ii->c.get_subset = NULL; |
| ii->c.get_iterator = NULL; |
| ii->c.for_each = (netsnmp_container_func*)_iterator_for_each; |
| ii->c.clear = _iterator_clear; |
| |
| /* |
| * init iterator structure with user functions |
| */ |
| ii->get_first = get_first; |
| ii->get_next = get_next; |
| ii->get_data = get_data; |
| ii->save_pos = save_pos; |
| ii->init_loop_ctx = init_loop_ctx; |
| ii->cleanup_loop_ctx = cleanup_loop_ctx; |
| ii->free_user_ctx = free_user_ctx; |
| ii->sorted = sorted; |
| |
| ii->user_ctx = iterator_user_ctx; |
| |
| return (netsnmp_container*)ii; |
| } |
| |
| void |
| netsnmp_container_iterator_set_data_cb(netsnmp_container *c, |
| Netsnmp_Iterator_Data * insert_data, |
| Netsnmp_Iterator_Data * remove_data, |
| Netsnmp_Iterator_Op * get_size) |
| { |
| iterator_info *ii = (iterator_info *)c; |
| if(NULL == ii) |
| return; |
| |
| ii->insert_data = insert_data; |
| ii->remove_data = remove_data; |
| ii->get_size = get_size; |
| } |
| #else /* NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR */ |
| netsnmp_feature_unused(container_iterator); |
| #endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR */ |