summaryrefslogtreecommitdiff
path: root/snmplib/oid_stash.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/oid_stash.c')
-rw-r--r--snmplib/oid_stash.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/snmplib/oid_stash.c b/snmplib/oid_stash.c
new file mode 100644
index 0000000..d7526de
--- /dev/null
+++ b/snmplib/oid_stash.c
@@ -0,0 +1,436 @@
+#include <net-snmp/net-snmp-config.h>
+
+#include <string.h>
+
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <net-snmp/net-snmp-includes.h>
+
+/** @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);
+}
+
+/** 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,
+ 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;
+}
+
+/** 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,
+ 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_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;
+}
+
+/** 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,
+ 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;
+}
+
+/** 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;
+}
+
+/** 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%ld@%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;
+}
+
+void
+netsnmp_oid_stash_no_free(void *bogus)
+{
+ /* noop */
+}
+
+/** @} */