| #include <net-snmp/net-snmp-config.h> |
| |
| #include <string.h> |
| |
| #include <stdlib.h> |
| #include <sys/types.h> |
| |
| #include <net-snmp/net-snmp-features.h> |
| #include <net-snmp/net-snmp-includes.h> |
| |
| netsnmp_feature_child_of(oid_stash_all, libnetsnmp) |
| netsnmp_feature_child_of(oid_stash, oid_stash_all) |
| netsnmp_feature_child_of(oid_stash_no_free, oid_stash_all) |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH |
| |
| /** @defgroup oid_stash Store and retrieve data referenced by an OID. |
| This is essentially a way of storing data associated with a given |
| OID. It stores a bunch of data pointers within a memory tree that |
| allows fairly efficient lookups with a heavily populated tree. |
| @ingroup library |
| @{ |
| */ |
| |
| /* |
| * xxx-rks: when you have some spare time: |
| * |
| * b) basically, everything currently creates one node per sub-oid, |
| * which is less than optimal. add code to create nodes with the |
| * longest possible OID per node, and split nodes when necessary |
| * during adds. |
| * |
| * c) If you are feeling really ambitious, also merge split nodes if |
| * possible on a delete. |
| * |
| * xxx-wes: uh, right, like I *ever* have that much time. |
| * |
| */ |
| |
| /*************************************************************************** |
| * |
| * |
| ***************************************************************************/ |
| |
| /** |
| * Create an netsnmp_oid_stash node |
| * |
| * @param mysize the size of the child pointer array |
| * |
| * @return NULL on error, otherwise the newly allocated node |
| */ |
| netsnmp_oid_stash_node * |
| netsnmp_oid_stash_create_sized_node(size_t mysize) |
| { |
| netsnmp_oid_stash_node *ret; |
| ret = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node); |
| if (!ret) |
| return NULL; |
| ret->children = (netsnmp_oid_stash_node**) calloc(mysize, sizeof(netsnmp_oid_stash_node *)); |
| if (!ret->children) { |
| free(ret); |
| return NULL; |
| } |
| ret->children_size = mysize; |
| return ret; |
| } |
| |
| /** Creates a netsnmp_oid_stash_node. |
| * Assumes you want the default OID_STASH_CHILDREN_SIZE hash size for the node. |
| * @return NULL on error, otherwise the newly allocated node |
| */ |
| NETSNMP_INLINE netsnmp_oid_stash_node * |
| netsnmp_oid_stash_create_node(void) |
| { |
| return netsnmp_oid_stash_create_sized_node(OID_STASH_CHILDREN_SIZE); |
| } |
| |
| netsnmp_feature_child_of(oid_stash_add_data, oid_stash_all) |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA |
| /** adds data to the stash at a given oid. |
| |
| * @param root the top of the stash tree |
| * @param lookup the oid index to store the data at. |
| * @param lookup_len the length of the lookup oid. |
| * @param mydata the data to store |
| |
| * @return SNMPERR_SUCCESS on success, SNMPERR_GENERR if data is |
| already there, SNMPERR_MALLOC on malloc failures or if arguments |
| passed in with NULL values. |
| */ |
| int |
| netsnmp_oid_stash_add_data(netsnmp_oid_stash_node **root, |
| const oid * lookup, size_t lookup_len, void *mydata) |
| { |
| netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
| unsigned int i; |
| |
| if (!root || !lookup || lookup_len == 0) |
| return SNMPERR_GENERR; |
| |
| if (!*root) { |
| *root = netsnmp_oid_stash_create_node(); |
| if (!*root) |
| return SNMPERR_MALLOC; |
| } |
| DEBUGMSGTL(( "oid_stash", "stash_add_data ")); |
| DEBUGMSGOID(("oid_stash", lookup, lookup_len)); |
| DEBUGMSG(( "oid_stash", "\n")); |
| tmpp = NULL; |
| for (curnode = *root, i = 0; i < lookup_len; i++) { |
| tmpp = curnode->children[lookup[i] % curnode->children_size]; |
| if (!tmpp) { |
| /* |
| * no child in array at all |
| */ |
| tmpp = curnode->children[lookup[i] % curnode->children_size] = |
| netsnmp_oid_stash_create_node(); |
| tmpp->value = lookup[i]; |
| tmpp->parent = curnode; |
| } else { |
| for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
| if (loopp->value == lookup[i]) |
| break; |
| } |
| if (loopp) { |
| tmpp = loopp; |
| } else { |
| /* |
| * none exists. Create it |
| */ |
| loopp = netsnmp_oid_stash_create_node(); |
| loopp->value = lookup[i]; |
| loopp->next_sibling = tmpp; |
| loopp->parent = curnode; |
| tmpp->prev_sibling = loopp; |
| curnode->children[lookup[i] % curnode->children_size] = |
| loopp; |
| tmpp = loopp; |
| } |
| /* |
| * tmpp now points to the proper node |
| */ |
| } |
| curnode = tmpp; |
| } |
| /* |
| * tmpp now points to the exact match |
| */ |
| if (curnode->thedata) |
| return SNMPERR_GENERR; |
| if (NULL == tmpp) |
| return SNMPERR_GENERR; |
| tmpp->thedata = mydata; |
| return SNMPERR_SUCCESS; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA */ |
| |
| /** returns a node associated with a given OID. |
| * @param root the top of the stash tree |
| * @param lookup the oid to look up a node for. |
| * @param lookup_len the length of the lookup oid |
| */ |
| netsnmp_oid_stash_node * |
| netsnmp_oid_stash_get_node(netsnmp_oid_stash_node *root, |
| const oid * lookup, size_t lookup_len) |
| { |
| netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
| unsigned int i; |
| |
| if (!root) |
| return NULL; |
| tmpp = NULL; |
| for (curnode = root, i = 0; i < lookup_len; i++) { |
| tmpp = curnode->children[lookup[i] % curnode->children_size]; |
| if (!tmpp) { |
| return NULL; |
| } else { |
| for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
| if (loopp->value == lookup[i]) |
| break; |
| } |
| if (loopp) { |
| tmpp = loopp; |
| } else { |
| return NULL; |
| } |
| } |
| curnode = tmpp; |
| } |
| return tmpp; |
| } |
| |
| /** returns the next node associated with a given OID. INCOMPLETE. |
| This is equivelent to a GETNEXT operation. |
| * @internal |
| * @param root the top of the stash tree |
| * @param lookup the oid to look up a node for. |
| * @param lookup_len the length of the lookup oid |
| */ |
| netsnmp_feature_child_of(oid_stash_iterate, oid_stash_all) |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE |
| netsnmp_oid_stash_node * |
| netsnmp_oid_stash_getnext_node(netsnmp_oid_stash_node *root, |
| oid * lookup, size_t lookup_len) |
| { |
| netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
| unsigned int i, j, bigger_than = 0, do_bigger = 0; |
| |
| if (!root) |
| return NULL; |
| tmpp = NULL; |
| |
| /* get closest matching node */ |
| for (curnode = root, i = 0; i < lookup_len; i++) { |
| tmpp = curnode->children[lookup[i] % curnode->children_size]; |
| if (!tmpp) { |
| break; |
| } else { |
| for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
| if (loopp->value == lookup[i]) |
| break; |
| } |
| if (loopp) { |
| tmpp = loopp; |
| } else { |
| break; |
| } |
| } |
| curnode = tmpp; |
| } |
| |
| /* find the *next* node lexographically greater */ |
| if (!curnode) |
| return NULL; /* ack! */ |
| |
| if (i+1 < lookup_len) { |
| bigger_than = lookup[i+1]; |
| do_bigger = 1; |
| } |
| |
| do { |
| /* check the children first */ |
| tmpp = NULL; |
| /* next child must be (next) greater than our next search node */ |
| /* XXX: should start this loop at best_nums[i]%... and wrap */ |
| for(j = 0; j < curnode->children_size; j++) { |
| for (loopp = curnode->children[j]; |
| loopp; loopp = loopp->next_sibling) { |
| if ((!do_bigger || loopp->value > bigger_than) && |
| (!tmpp || tmpp->value > loopp->value)) { |
| tmpp = loopp; |
| /* XXX: can do better and include min_nums[i] */ |
| if (tmpp->value <= curnode->children_size-1) { |
| /* best we can do. */ |
| goto done_this_loop; |
| } |
| } |
| } |
| } |
| |
| done_this_loop: |
| if (tmpp && tmpp->thedata) |
| /* found a node with data. Go with it. */ |
| return tmpp; |
| |
| if (tmpp) { |
| /* found a child node without data, maybe find a grandchild? */ |
| do_bigger = 0; |
| curnode = tmpp; |
| } else { |
| /* no respectable children (the bums), we'll have to go up. |
| But to do so, they must be better than our current best_num + 1. |
| */ |
| do_bigger = 1; |
| bigger_than = curnode->value; |
| curnode = curnode->parent; |
| } |
| } while (curnode); |
| |
| /* fell off the top */ |
| return NULL; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE */ |
| |
| netsnmp_feature_child_of(oid_stash_get_data, oid_stash_all) |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA |
| /** returns a data pointer associated with a given OID. |
| |
| This is equivelent to netsnmp_oid_stash_get_node, but returns only |
| the data not the entire node. |
| |
| * @param root the top of the stash |
| * @param lookup the oid to search for |
| * @param lookup_len the length of the search oid. |
| */ |
| void * |
| netsnmp_oid_stash_get_data(netsnmp_oid_stash_node *root, |
| const oid * lookup, size_t lookup_len) |
| { |
| netsnmp_oid_stash_node *ret; |
| ret = netsnmp_oid_stash_get_node(root, lookup, lookup_len); |
| if (ret) |
| return ret->thedata; |
| return NULL; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA */ |
| |
| netsnmp_feature_child_of(oid_stash_store_all, oid_stash_all) |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL |
| /** a wrapper around netsnmp_oid_stash_store for use with a snmp_alarm. |
| * when calling snmp_alarm, you can list this as a callback. The |
| * clientarg should be a pointer to a netsnmp_oid_stash_save_info |
| * pointer. It can also be called directly, of course. The last |
| * argument (clientarg) is the only one that is used. The rest are |
| * ignored by the function. |
| * @param majorID |
| * @param minorID |
| * @param serverarg |
| * @param clientarg A pointer to a netsnmp_oid_stash_save_info structure. |
| */ |
| int |
| netsnmp_oid_stash_store_all(int majorID, int minorID, |
| void *serverarg, void *clientarg) { |
| oid oidbase[MAX_OID_LEN]; |
| netsnmp_oid_stash_save_info *sinfo; |
| |
| if (!clientarg) |
| return SNMP_ERR_NOERROR; |
| |
| sinfo = (netsnmp_oid_stash_save_info *) clientarg; |
| netsnmp_oid_stash_store(*(sinfo->root), sinfo->token, sinfo->dumpfn, |
| oidbase,0); |
| return SNMP_ERR_NOERROR; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL */ |
| |
| /** stores data in a starsh tree to peristent storage. |
| |
| This function can be called to save all data in a stash tree to |
| Net-SNMP's percent storage. Make sure you register a parsing |
| function with the read_config system to re-incorperate your saved |
| data into future trees. |
| |
| @param root the top of the stash to store. |
| @param tokenname the file token name to save in (passing "snmpd" will |
| save things into snmpd.conf). |
| @param dumpfn A function which can dump the data stored at a particular |
| node into a char buffer. |
| @param curoid must be a pointer to a OID array of length MAX_OID_LEN. |
| @param curoid_len must be 0 for the top level call. |
| */ |
| void |
| netsnmp_oid_stash_store(netsnmp_oid_stash_node *root, |
| const char *tokenname, NetSNMPStashDump *dumpfn, |
| oid *curoid, size_t curoid_len) { |
| |
| char buf[SNMP_MAXBUF]; |
| netsnmp_oid_stash_node *tmpp; |
| char *cp; |
| char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE); |
| int i; |
| |
| if (!tokenname || !root || !curoid || !dumpfn) |
| return; |
| |
| for (i = 0; i < (int)root->children_size; i++) { |
| if (root->children[i]) { |
| for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) { |
| curoid[curoid_len] = tmpp->value; |
| if (tmpp->thedata) { |
| snprintf(buf, sizeof(buf), "%s ", tokenname); |
| cp = read_config_save_objid(buf+strlen(buf), curoid, |
| curoid_len+1); |
| *cp++ = ' '; |
| *cp = '\0'; |
| if ((*dumpfn)(cp, sizeof(buf) - strlen(buf), |
| tmpp->thedata, tmpp)) |
| read_config_store(appname, buf); |
| } |
| netsnmp_oid_stash_store(tmpp, tokenname, dumpfn, |
| curoid, curoid_len+1); |
| } |
| } |
| } |
| } |
| |
| /** For debugging: dump the netsnmp_oid_stash tree to stdout |
| @param root The top of the tree |
| @param prefix a character string prefix printed to the beginning of each line. |
| */ |
| void |
| oid_stash_dump(netsnmp_oid_stash_node *root, char *prefix) |
| { |
| char myprefix[MAX_OID_LEN * 4]; |
| netsnmp_oid_stash_node *tmpp; |
| int prefix_len = strlen(prefix) + 1; /* actually it's +2 */ |
| unsigned int i; |
| |
| memset(myprefix, ' ', MAX_OID_LEN * 4); |
| myprefix[prefix_len] = '\0'; |
| |
| for (i = 0; i < root->children_size; i++) { |
| if (root->children[i]) { |
| for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) { |
| printf("%s%" NETSNMP_PRIo "d@%d: %s\n", prefix, tmpp->value, i, |
| (tmpp->thedata) ? "DATA" : ""); |
| oid_stash_dump(tmpp, myprefix); |
| } |
| } |
| } |
| } |
| |
| /** Frees the contents of a netsnmp_oid_stash tree. |
| @param root the top of the tree (or branch to be freed) |
| @param freefn The function to be called on each data (void *) |
| pointer. If left NULL the system free() function will be called |
| */ |
| void |
| netsnmp_oid_stash_free(netsnmp_oid_stash_node **root, |
| NetSNMPStashFreeNode *freefn) { |
| |
| netsnmp_oid_stash_node *curnode, *tmpp; |
| unsigned int i; |
| |
| if (!root || !*root) |
| return; |
| |
| /* loop through all our children and free each node */ |
| for (i = 0; i < (*root)->children_size; i++) { |
| if ((*root)->children[i]) { |
| for(tmpp = (*root)->children[i]; tmpp; tmpp = curnode) { |
| if (tmpp->thedata) { |
| if (freefn) |
| (*freefn)(tmpp->thedata); |
| else |
| free(tmpp->thedata); |
| } |
| curnode = tmpp->next_sibling; |
| netsnmp_oid_stash_free(&tmpp, freefn); |
| } |
| } |
| } |
| free((*root)->children); |
| free (*root); |
| *root = NULL; |
| } |
| |
| #else /* NETSNMP_FEATURE_REMOVE_OID_STASH */ |
| netsnmp_feature_unused(oid_stash); |
| #endif/* NETSNMP_FEATURE_REMOVE_OID_STASH */ |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE |
| void |
| netsnmp_oid_stash_no_free(void *bogus) |
| { |
| /* noop */ |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE */ |
| |
| /** @} */ |