Blame snmplib/oid_stash.c

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