blob: 8a798975716a48453effccd4a3098378d6caa51e [file] [log] [blame]
#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 */
/** @} */