1
#include <net-snmp/net-snmp-config.h>
12
#include <net-snmp/net-snmp-includes.h>
14
/** @defgroup oid_stash Store and retrieve data referenced by an OID.
15
This is essentially a way of storing data associated with a given
16
OID. It stores a bunch of data pointers within a memory tree that
17
allows fairly efficient lookups with a heavily populated tree.
23
* xxx-rks: when you have some spare time:
25
* b) basically, everything currently creates one node per sub-oid,
26
* which is less than optimal. add code to create nodes with the
27
* longest possible OID per node, and split nodes when necessary
30
* c) If you are feeling really ambitious, also merge split nodes if
31
* possible on a delete.
33
* xxx-wes: uh, right, like I *ever* have that much time.
37
/***************************************************************************
40
***************************************************************************/
43
* Create an netsnmp_oid_stash node
45
* @param mysize the size of the child pointer array
47
* @return NULL on error, otherwise the newly allocated node
49
netsnmp_oid_stash_node *
50
netsnmp_oid_stash_create_sized_node(size_t mysize)
52
netsnmp_oid_stash_node *ret;
53
ret = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node);
56
ret->children = calloc(mysize, sizeof(netsnmp_oid_stash_node *));
61
ret->children_size = mysize;
65
/** Creates a netsnmp_oid_stash_node.
66
* Assumes you want the default OID_STASH_CHILDREN_SIZE hash size for the node.
67
* @return NULL on error, otherwise the newly allocated node
69
NETSNMP_INLINE netsnmp_oid_stash_node *
70
netsnmp_oid_stash_create_node(void)
72
return netsnmp_oid_stash_create_sized_node(OID_STASH_CHILDREN_SIZE);
75
/** adds data to the stash at a given oid.
77
* @param root the top of the stash tree
78
* @param lookup the oid index to store the data at.
79
* @param lookup_len the length of the lookup oid.
80
* @param mydata the data to store
82
* @return SNMPERR_SUCCESS on success, SNMPERR_GENERR if data is
83
already there, SNMPERR_MALLOC on malloc failures or if arguments
84
passed in with NULL values.
87
netsnmp_oid_stash_add_data(netsnmp_oid_stash_node **root,
88
oid * lookup, size_t lookup_len, void *mydata)
90
netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
93
if (!root || !lookup || lookup_len == 0)
94
return SNMPERR_GENERR;
97
*root = netsnmp_oid_stash_create_node();
99
return SNMPERR_MALLOC;
102
for (curnode = *root, i = 0; i < lookup_len; i++) {
103
tmpp = curnode->children[lookup[i] % curnode->children_size];
106
* no child in array at all
108
tmpp = curnode->children[lookup[i] % curnode->children_size] =
109
netsnmp_oid_stash_create_node();
110
tmpp->value = lookup[i];
111
tmpp->parent = curnode;
113
for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
114
if (loopp->value == lookup[i])
121
* none exists. Create it
123
loopp = netsnmp_oid_stash_create_node();
124
loopp->value = lookup[i];
125
loopp->next_sibling = tmpp;
126
loopp->parent = curnode;
127
tmpp->prev_sibling = loopp;
128
curnode->children[lookup[i] % curnode->children_size] =
133
* tmpp now points to the proper node
139
* tmpp now points to the exact match
141
if (curnode->thedata)
142
return SNMPERR_GENERR;
144
return SNMPERR_GENERR;
145
tmpp->thedata = mydata;
146
return SNMPERR_SUCCESS;
149
/** returns a node associated with a given OID.
150
* @param root the top of the stash tree
151
* @param lookup the oid to look up a node for.
152
* @param lookup_len the length of the lookup oid
154
netsnmp_oid_stash_node *
155
netsnmp_oid_stash_get_node(netsnmp_oid_stash_node *root,
156
oid * lookup, size_t lookup_len)
158
netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
164
for (curnode = root, i = 0; i < lookup_len; i++) {
165
tmpp = curnode->children[lookup[i] % curnode->children_size];
169
for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
170
if (loopp->value == lookup[i])
184
/** returns the next node associated with a given OID. INCOMPLETE.
185
This is equivelent to a GETNEXT operation.
187
* @param root the top of the stash tree
188
* @param lookup the oid to look up a node for.
189
* @param lookup_len the length of the lookup oid
191
netsnmp_oid_stash_node *
192
netsnmp_oid_stash_getnext_node(netsnmp_oid_stash_node *root,
193
oid * lookup, size_t lookup_len)
195
netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
196
unsigned int i, j, bigger_than = 0, do_bigger = 0;
202
/* get closest matching node */
203
for (curnode = root, i = 0; i < lookup_len; i++) {
204
tmpp = curnode->children[lookup[i] % curnode->children_size];
208
for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
209
if (loopp->value == lookup[i])
221
/* find the *next* node lexographically greater */
223
return NULL; /* ack! */
225
if (i+1 < lookup_len) {
226
bigger_than = lookup[i+1];
231
/* check the children first */
233
/* next child must be (next) greater than our next search node */
234
/* XXX: should start this loop at best_nums[i]%... and wrap */
235
for(j = 0; j < curnode->children_size; j++) {
236
for (loopp = curnode->children[j];
237
loopp; loopp = loopp->next_sibling) {
238
if ((!do_bigger || loopp->value > bigger_than) &&
239
(!tmpp || tmpp->value > loopp->value)) {
241
/* XXX: can do better and include min_nums[i] */
242
if (tmpp->value <= curnode->children_size-1) {
243
/* best we can do. */
251
if (tmpp && tmpp->thedata)
252
/* found a node with data. Go with it. */
256
/* found a child node without data, maybe find a grandchild? */
260
/* no respectable children (the bums), we'll have to go up.
261
But to do so, they must be better than our current best_num + 1.
264
bigger_than = curnode->value;
265
curnode = curnode->parent;
269
/* fell off the top */
273
/** returns a data pointer associated with a given OID.
275
This is equivelent to netsnmp_oid_stash_get_node, but returns only
276
the data not the entire node.
278
* @param root the top of the stash
279
* @param oid the oid to search for
280
* @param the length of the search oid.
283
netsnmp_oid_stash_get_data(netsnmp_oid_stash_node *root,
284
oid * lookup, size_t lookup_len)
286
netsnmp_oid_stash_node *ret;
287
ret = netsnmp_oid_stash_get_node(root, lookup, lookup_len);
293
/** a wrapper around netsnmp_oid_stash_store for use with a snmp_alarm.
294
* when calling snmp_alarm, you can list this as a callback. The
295
* clientarg should be a pointer to a netsnmp_oid_stash_save_info
296
* pointer. It can also be called directly, of course. The last
297
* argument (clientarg) is the only one that is used. The rest are
298
* ignored by the function.
299
* @param clientarg A pointer to a netsnmp_oid_stash_save_info structure.
302
netsnmp_oid_stash_store_all(int majorID, int minorID,
303
void *serverarg, void *clientarg) {
304
oid oidbase[MAX_OID_LEN];
305
netsnmp_oid_stash_save_info *sinfo;
308
return SNMP_ERR_NOERROR;
311
netsnmp_oid_stash_store(*(sinfo->root), sinfo->token, sinfo->dumpfn,
313
return SNMP_ERR_NOERROR;
316
/** stores data in a starsh tree to peristent storage.
318
This function can be called to save all data in a stash tree to
319
Net-SNMP's percent storage. Make sure you register a parsing
320
function with the read_config system to re-incorperate your saved
321
data into future trees.
323
@param root the top of the stash to store.
324
@param tokenname the file token name to save in (passing "snmpd" will
325
save things into snmpd.conf).
326
@param dumpfn A function which can dump the data stored at a particular
327
node into a char buffer.
328
@param curoid must be a pointer to a OID array of length MAX_OID_LEN.
329
@param curoid_len must be 0 for the top level call.
332
netsnmp_oid_stash_store(netsnmp_oid_stash_node *root,
333
const char *tokenname, NetSNMPStashDump *dumpfn,
334
oid *curoid, size_t curoid_len) {
336
char buf[SNMP_MAXBUF];
337
netsnmp_oid_stash_node *tmpp;
339
char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
340
NETSNMP_DS_LIB_APPTYPE);
343
if (!tokenname || !root || !curoid || !dumpfn)
346
for (i = 0; i < (int)root->children_size; i++) {
347
if (root->children[i]) {
348
for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) {
349
curoid[curoid_len] = tmpp->value;
351
snprintf(buf, sizeof(buf), "%s ", tokenname);
352
cp = read_config_save_objid(buf+strlen(buf), curoid,
356
if ((*dumpfn)(cp, sizeof(buf) - strlen(buf),
357
tmpp->thedata, tmpp))
358
read_config_store(appname, buf);
360
netsnmp_oid_stash_store(tmpp, tokenname, dumpfn,
361
curoid, curoid_len+1);
367
/** For debugging: dump the netsnmp_oid_stash tree to stdout
368
@param root The top of the tree
369
@param prefix a character string prefix printed to the beginning of each line.
372
oid_stash_dump(netsnmp_oid_stash_node *root, char *prefix)
374
char myprefix[MAX_OID_LEN * 4];
375
netsnmp_oid_stash_node *tmpp;
376
int prefix_len = strlen(prefix) + 1; /* actually it's +2 */
379
memset(myprefix, ' ', MAX_OID_LEN * 4);
380
myprefix[prefix_len] = '\0';
382
for (i = 0; i < root->children_size; i++) {
383
if (root->children[i]) {
384
for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) {
385
printf("%s%ld@%d: %s\n", prefix, tmpp->value, i,
386
(tmpp->thedata) ? "DATA" : "");
387
oid_stash_dump(tmpp, myprefix);
393
/** Frees the contents of a netsnmp_oid_stash tree.
394
@param root the top of the tree (or branch to be freed)
395
@param freefn The function to be called on each data (void *)
396
pointer. If left NULL the system free() function will be called
399
netsnmp_oid_stash_free(netsnmp_oid_stash_node **root,
400
NetSNMPStashFreeNode *freefn) {
402
netsnmp_oid_stash_node *curnode, *tmpp;
408
/* loop through all our children and free each node */
409
for (i = 0; i < (*root)->children_size; i++) {
410
if ((*root)->children[i]) {
411
for(tmpp = (*root)->children[i]; tmpp; tmpp = curnode) {
414
(*freefn)(tmpp->thedata);
418
curnode = tmpp->next_sibling;
419
netsnmp_oid_stash_free(&tmpp, freefn);
423
free((*root)->children);
429
netsnmp_oid_stash_no_free(void *bogus)