/src/net-snmp/snmplib/oid_stash.c
Line | Count | Source |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | |
3 | | #include <string.h> |
4 | | |
5 | | #include <stdlib.h> |
6 | | #include <sys/types.h> |
7 | | |
8 | | #include <net-snmp/net-snmp-features.h> |
9 | | #include <net-snmp/net-snmp-includes.h> |
10 | | |
11 | | netsnmp_feature_child_of(oid_stash_all, libnetsnmp); |
12 | | netsnmp_feature_child_of(oid_stash, oid_stash_all); |
13 | | netsnmp_feature_child_of(oid_stash_no_free, oid_stash_all); |
14 | | |
15 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH |
16 | | |
17 | | /** @defgroup oid_stash Store and retrieve data referenced by an OID. |
18 | | This is essentially a way of storing data associated with a given |
19 | | OID. It stores a bunch of data pointers within a memory tree that |
20 | | allows fairly efficient lookups with a heavily populated tree. |
21 | | @ingroup library |
22 | | @{ |
23 | | */ |
24 | | |
25 | | /* |
26 | | * xxx-rks: when you have some spare time: |
27 | | * |
28 | | * b) basically, everything currently creates one node per sub-oid, |
29 | | * which is less than optimal. add code to create nodes with the |
30 | | * longest possible OID per node, and split nodes when necessary |
31 | | * during adds. |
32 | | * |
33 | | * c) If you are feeling really ambitious, also merge split nodes if |
34 | | * possible on a delete. |
35 | | * |
36 | | * xxx-wes: uh, right, like I *ever* have that much time. |
37 | | * |
38 | | */ |
39 | | |
40 | | /*************************************************************************** |
41 | | * |
42 | | * |
43 | | ***************************************************************************/ |
44 | | |
45 | | /** |
46 | | * Create an netsnmp_oid_stash node |
47 | | * |
48 | | * @param mysize the size of the child pointer array |
49 | | * |
50 | | * @return NULL on error, otherwise the newly allocated node |
51 | | */ |
52 | | netsnmp_oid_stash_node * |
53 | | netsnmp_oid_stash_create_sized_node(size_t mysize) |
54 | 0 | { |
55 | 0 | netsnmp_oid_stash_node *ret; |
56 | 0 | ret = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node); |
57 | 0 | if (!ret) |
58 | 0 | return NULL; |
59 | 0 | ret->children = calloc(mysize, sizeof(netsnmp_oid_stash_node *)); |
60 | 0 | if (!ret->children) { |
61 | 0 | free(ret); |
62 | 0 | return NULL; |
63 | 0 | } |
64 | 0 | ret->children_size = mysize; |
65 | 0 | return ret; |
66 | 0 | } |
67 | | |
68 | | /** Creates a netsnmp_oid_stash_node. |
69 | | * Assumes you want the default OID_STASH_CHILDREN_SIZE hash size for the node. |
70 | | * @return NULL on error, otherwise the newly allocated node |
71 | | */ |
72 | | NETSNMP_INLINE netsnmp_oid_stash_node * |
73 | | netsnmp_oid_stash_create_node(void) |
74 | 0 | { |
75 | 0 | return netsnmp_oid_stash_create_sized_node(OID_STASH_CHILDREN_SIZE); |
76 | 0 | } |
77 | | |
78 | | netsnmp_feature_child_of(oid_stash_add_data, oid_stash_all); |
79 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA |
80 | | /** adds data to the stash at a given oid. |
81 | | |
82 | | * @param root the top of the stash tree |
83 | | * @param lookup the oid index to store the data at. |
84 | | * @param lookup_len the length of the lookup oid. |
85 | | * @param mydata the data to store |
86 | | |
87 | | * @return SNMPERR_SUCCESS on success, SNMPERR_GENERR if data is |
88 | | already there, SNMPERR_MALLOC on malloc failures or if arguments |
89 | | passed in with NULL values. |
90 | | */ |
91 | | int |
92 | | netsnmp_oid_stash_add_data(netsnmp_oid_stash_node **root, |
93 | | const oid * lookup, size_t lookup_len, void *mydata) |
94 | 0 | { |
95 | 0 | netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
96 | 0 | unsigned int i; |
97 | |
|
98 | 0 | if (!root || !lookup || lookup_len == 0) |
99 | 0 | return SNMPERR_GENERR; |
100 | | |
101 | 0 | if (!*root) { |
102 | 0 | *root = netsnmp_oid_stash_create_node(); |
103 | 0 | if (!*root) |
104 | 0 | return SNMPERR_MALLOC; |
105 | 0 | } |
106 | 0 | DEBUGMSGTL(( "oid_stash", "stash_add_data ")); |
107 | 0 | DEBUGMSGOID(("oid_stash", lookup, lookup_len)); |
108 | 0 | DEBUGMSG(( "oid_stash", "\n")); |
109 | 0 | tmpp = NULL; |
110 | 0 | for (curnode = *root, i = 0; i < lookup_len; i++) { |
111 | 0 | tmpp = curnode->children[lookup[i] % curnode->children_size]; |
112 | 0 | if (!tmpp) { |
113 | | /* |
114 | | * no child in array at all |
115 | | */ |
116 | 0 | tmpp = curnode->children[lookup[i] % curnode->children_size] = |
117 | 0 | netsnmp_oid_stash_create_node(); |
118 | 0 | tmpp->value = lookup[i]; |
119 | 0 | tmpp->parent = curnode; |
120 | 0 | } else { |
121 | 0 | for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
122 | 0 | if (loopp->value == lookup[i]) |
123 | 0 | break; |
124 | 0 | } |
125 | 0 | if (loopp) { |
126 | 0 | tmpp = loopp; |
127 | 0 | } else { |
128 | | /* |
129 | | * none exists. Create it |
130 | | */ |
131 | 0 | loopp = netsnmp_oid_stash_create_node(); |
132 | 0 | loopp->value = lookup[i]; |
133 | 0 | loopp->next_sibling = tmpp; |
134 | 0 | loopp->parent = curnode; |
135 | 0 | tmpp->prev_sibling = loopp; |
136 | 0 | curnode->children[lookup[i] % curnode->children_size] = |
137 | 0 | loopp; |
138 | 0 | tmpp = loopp; |
139 | 0 | } |
140 | | /* |
141 | | * tmpp now points to the proper node |
142 | | */ |
143 | 0 | } |
144 | 0 | curnode = tmpp; |
145 | 0 | } |
146 | | /* |
147 | | * tmpp now points to the exact match |
148 | | */ |
149 | 0 | if (curnode->thedata) |
150 | 0 | return SNMPERR_GENERR; |
151 | 0 | if (NULL == tmpp) |
152 | 0 | return SNMPERR_GENERR; |
153 | 0 | tmpp->thedata = mydata; |
154 | 0 | return SNMPERR_SUCCESS; |
155 | 0 | } |
156 | | #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA */ |
157 | | |
158 | | /** returns a node associated with a given OID. |
159 | | * @param root the top of the stash tree |
160 | | * @param lookup the oid to look up a node for. |
161 | | * @param lookup_len the length of the lookup oid |
162 | | */ |
163 | | netsnmp_oid_stash_node * |
164 | | netsnmp_oid_stash_get_node(netsnmp_oid_stash_node *root, |
165 | | const oid * lookup, size_t lookup_len) |
166 | 0 | { |
167 | 0 | netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
168 | 0 | unsigned int i; |
169 | |
|
170 | 0 | if (!root) |
171 | 0 | return NULL; |
172 | 0 | tmpp = NULL; |
173 | 0 | for (curnode = root, i = 0; i < lookup_len; i++) { |
174 | 0 | tmpp = curnode->children[lookup[i] % curnode->children_size]; |
175 | 0 | if (!tmpp) { |
176 | 0 | return NULL; |
177 | 0 | } else { |
178 | 0 | for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
179 | 0 | if (loopp->value == lookup[i]) |
180 | 0 | break; |
181 | 0 | } |
182 | 0 | if (loopp) { |
183 | 0 | tmpp = loopp; |
184 | 0 | } else { |
185 | 0 | return NULL; |
186 | 0 | } |
187 | 0 | } |
188 | 0 | curnode = tmpp; |
189 | 0 | } |
190 | 0 | return tmpp; |
191 | 0 | } |
192 | | |
193 | | /** returns the next node associated with a given OID. INCOMPLETE. |
194 | | This is equivalent to a GETNEXT operation. |
195 | | * @internal |
196 | | * @param root the top of the stash tree |
197 | | * @param lookup the oid to look up a node for. |
198 | | * @param lookup_len the length of the lookup oid |
199 | | */ |
200 | | netsnmp_feature_child_of(oid_stash_iterate, oid_stash_all); |
201 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE |
202 | | netsnmp_oid_stash_node * |
203 | | netsnmp_oid_stash_getnext_node(netsnmp_oid_stash_node *root, |
204 | | oid * lookup, size_t lookup_len) |
205 | 0 | { |
206 | 0 | netsnmp_oid_stash_node *curnode, *tmpp, *loopp; |
207 | 0 | unsigned int i, j, bigger_than = 0, do_bigger = 0; |
208 | |
|
209 | 0 | if (!root) |
210 | 0 | return NULL; |
211 | 0 | tmpp = NULL; |
212 | | |
213 | | /* get closest matching node */ |
214 | 0 | for (curnode = root, i = 0; i < lookup_len; i++) { |
215 | 0 | tmpp = curnode->children[lookup[i] % curnode->children_size]; |
216 | 0 | if (!tmpp) { |
217 | 0 | break; |
218 | 0 | } else { |
219 | 0 | for (loopp = tmpp; loopp; loopp = loopp->next_sibling) { |
220 | 0 | if (loopp->value == lookup[i]) |
221 | 0 | break; |
222 | 0 | } |
223 | 0 | if (loopp) { |
224 | 0 | tmpp = loopp; |
225 | 0 | } else { |
226 | 0 | break; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | curnode = tmpp; |
230 | 0 | } |
231 | | |
232 | | /* find the *next* node lexographically greater */ |
233 | 0 | if (!curnode) |
234 | 0 | return NULL; /* ack! */ |
235 | | |
236 | 0 | if (i+1 < lookup_len) { |
237 | 0 | bigger_than = lookup[i+1]; |
238 | 0 | do_bigger = 1; |
239 | 0 | } |
240 | |
|
241 | 0 | do { |
242 | | /* check the children first */ |
243 | 0 | tmpp = NULL; |
244 | | /* next child must be (next) greater than our next search node */ |
245 | | /* XXX: should start this loop at best_nums[i]%... and wrap */ |
246 | 0 | for(j = 0; j < curnode->children_size; j++) { |
247 | 0 | for (loopp = curnode->children[j]; |
248 | 0 | loopp; loopp = loopp->next_sibling) { |
249 | 0 | if ((!do_bigger || loopp->value > bigger_than) && |
250 | 0 | (!tmpp || tmpp->value > loopp->value)) { |
251 | 0 | tmpp = loopp; |
252 | | /* XXX: can do better and include min_nums[i] */ |
253 | 0 | if (tmpp->value <= curnode->children_size-1) { |
254 | | /* best we can do. */ |
255 | 0 | goto done_this_loop; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | 0 | done_this_loop: |
262 | 0 | if (tmpp && tmpp->thedata) |
263 | | /* found a node with data. Go with it. */ |
264 | 0 | return tmpp; |
265 | | |
266 | 0 | if (tmpp) { |
267 | | /* found a child node without data, maybe find a grandchild? */ |
268 | 0 | do_bigger = 0; |
269 | 0 | curnode = tmpp; |
270 | 0 | } else { |
271 | | /* no respectable children (the bums), we'll have to go up. |
272 | | But to do so, they must be better than our current best_num + 1. |
273 | | */ |
274 | 0 | do_bigger = 1; |
275 | 0 | bigger_than = curnode->value; |
276 | 0 | curnode = curnode->parent; |
277 | 0 | } |
278 | 0 | } while (curnode); |
279 | | |
280 | | /* fell off the top */ |
281 | 0 | return NULL; |
282 | 0 | } |
283 | | #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE */ |
284 | | |
285 | | netsnmp_feature_child_of(oid_stash_get_data, oid_stash_all); |
286 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA |
287 | | /** returns a data pointer associated with a given OID. |
288 | | |
289 | | This is equivalent to netsnmp_oid_stash_get_node, but returns only |
290 | | the data not the entire node. |
291 | | |
292 | | * @param root the top of the stash |
293 | | * @param lookup the oid to search for |
294 | | * @param lookup_len the length of the search oid. |
295 | | */ |
296 | | void * |
297 | | netsnmp_oid_stash_get_data(netsnmp_oid_stash_node *root, |
298 | | const oid * lookup, size_t lookup_len) |
299 | 0 | { |
300 | 0 | netsnmp_oid_stash_node *ret; |
301 | 0 | ret = netsnmp_oid_stash_get_node(root, lookup, lookup_len); |
302 | 0 | if (ret) |
303 | 0 | return ret->thedata; |
304 | 0 | return NULL; |
305 | 0 | } |
306 | | #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA */ |
307 | | |
308 | | netsnmp_feature_child_of(oid_stash_store_all, oid_stash_all); |
309 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL |
310 | | /** a wrapper around netsnmp_oid_stash_store for use with a snmp_alarm. |
311 | | * when calling snmp_alarm, you can list this as a callback. The |
312 | | * clientarg should be a pointer to a netsnmp_oid_stash_save_info |
313 | | * pointer. It can also be called directly, of course. The last |
314 | | * argument (clientarg) is the only one that is used. The rest are |
315 | | * ignored by the function. |
316 | | * @param majorID |
317 | | * @param minorID |
318 | | * @param serverarg |
319 | | * @param clientarg A pointer to a netsnmp_oid_stash_save_info structure. |
320 | | */ |
321 | | int |
322 | | netsnmp_oid_stash_store_all(int majorID, int minorID, |
323 | 0 | void *serverarg, void *clientarg) { |
324 | 0 | oid oidbase[MAX_OID_LEN]; |
325 | 0 | netsnmp_oid_stash_save_info *sinfo; |
326 | | |
327 | 0 | if (!clientarg) |
328 | 0 | return SNMP_ERR_NOERROR; |
329 | | |
330 | 0 | sinfo = (netsnmp_oid_stash_save_info *) clientarg; |
331 | 0 | netsnmp_oid_stash_store(*(sinfo->root), sinfo->token, sinfo->dumpfn, |
332 | 0 | oidbase,0); |
333 | 0 | return SNMP_ERR_NOERROR; |
334 | 0 | } |
335 | | #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL */ |
336 | | |
337 | | /** stores data in a stash tree to persistent storage. |
338 | | |
339 | | This function can be called to save all data in a stash tree to |
340 | | Net-SNMP's percent storage. Make sure you register a parsing |
341 | | function with the read_config system to re-incorporate your saved |
342 | | data into future trees. |
343 | | |
344 | | @param root the top of the stash to store. |
345 | | @param tokenname the file token name to save in (passing "snmpd" will |
346 | | save things into snmpd.conf). |
347 | | @param dumpfn A function which can dump the data stored at a particular |
348 | | node into a char buffer. |
349 | | @param curoid must be a pointer to a OID array of length MAX_OID_LEN. |
350 | | @param curoid_len must be 0 for the top level call. |
351 | | */ |
352 | | void |
353 | | netsnmp_oid_stash_store(netsnmp_oid_stash_node *root, |
354 | | const char *tokenname, NetSNMPStashDump *dumpfn, |
355 | 0 | oid *curoid, size_t curoid_len) { |
356 | |
|
357 | 0 | char buf[SNMP_MAXBUF]; |
358 | 0 | netsnmp_oid_stash_node *tmpp; |
359 | 0 | char *cp; |
360 | 0 | char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
361 | 0 | NETSNMP_DS_LIB_APPTYPE); |
362 | 0 | int i; |
363 | | |
364 | 0 | if (!tokenname || !root || !curoid || !dumpfn) |
365 | 0 | return; |
366 | | |
367 | 0 | for (i = 0; i < (int)root->children_size; i++) { |
368 | 0 | if (root->children[i]) { |
369 | 0 | for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) { |
370 | 0 | curoid[curoid_len] = tmpp->value; |
371 | 0 | if (tmpp->thedata) { |
372 | 0 | snprintf(buf, sizeof(buf), "%s ", tokenname); |
373 | 0 | cp = read_config_save_objid(buf+strlen(buf), curoid, |
374 | 0 | curoid_len+1); |
375 | 0 | *cp++ = ' '; |
376 | 0 | *cp = '\0'; |
377 | 0 | if ((*dumpfn)(cp, sizeof(buf) - strlen(buf), |
378 | 0 | tmpp->thedata, tmpp)) |
379 | 0 | read_config_store(appname, buf); |
380 | 0 | } |
381 | 0 | netsnmp_oid_stash_store(tmpp, tokenname, dumpfn, |
382 | 0 | curoid, curoid_len+1); |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | /** For debugging: dump the netsnmp_oid_stash tree to stdout |
389 | | @param root The top of the tree |
390 | | @param prefix a character string prefix printed to the beginning of each line. |
391 | | */ |
392 | | void |
393 | | oid_stash_dump(netsnmp_oid_stash_node *root, char *prefix) |
394 | 0 | { |
395 | 0 | char myprefix[MAX_OID_LEN * 4]; |
396 | 0 | netsnmp_oid_stash_node *tmpp; |
397 | 0 | int prefix_len = strlen(prefix) + 1; /* actually it's +2 */ |
398 | 0 | unsigned int i; |
399 | |
|
400 | 0 | memset(myprefix, ' ', MAX_OID_LEN * 4); |
401 | 0 | myprefix[prefix_len] = '\0'; |
402 | |
|
403 | 0 | for (i = 0; i < root->children_size; i++) { |
404 | 0 | if (root->children[i]) { |
405 | 0 | for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) { |
406 | 0 | printf("%s%" NETSNMP_PRIo "d@%d: %s\n", prefix, tmpp->value, i, |
407 | 0 | (tmpp->thedata) ? "DATA" : ""); |
408 | 0 | oid_stash_dump(tmpp, myprefix); |
409 | 0 | } |
410 | 0 | } |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | /** Frees the contents of a netsnmp_oid_stash tree. |
415 | | @param root the top of the tree (or branch to be freed) |
416 | | @param freefn The function to be called on each data (void *) |
417 | | pointer. If left NULL the system free() function will be called |
418 | | */ |
419 | | void |
420 | | netsnmp_oid_stash_free(netsnmp_oid_stash_node **root, |
421 | 0 | NetSNMPStashFreeNode *freefn) { |
422 | |
|
423 | 0 | netsnmp_oid_stash_node *curnode, *tmpp; |
424 | 0 | unsigned int i; |
425 | |
|
426 | 0 | if (!root || !*root) |
427 | 0 | return; |
428 | | |
429 | | /* loop through all our children and free each node */ |
430 | 0 | for (i = 0; i < (*root)->children_size; i++) { |
431 | 0 | if ((*root)->children[i]) { |
432 | 0 | for(tmpp = (*root)->children[i]; tmpp; tmpp = curnode) { |
433 | 0 | if (tmpp->thedata) { |
434 | 0 | if (freefn) |
435 | 0 | (*freefn)(tmpp->thedata); |
436 | 0 | else |
437 | 0 | free(tmpp->thedata); |
438 | 0 | } |
439 | 0 | curnode = tmpp->next_sibling; |
440 | 0 | netsnmp_oid_stash_free(&tmpp, freefn); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | 0 | free((*root)->children); |
445 | 0 | free (*root); |
446 | 0 | *root = NULL; |
447 | 0 | } |
448 | | |
449 | | #else /* NETSNMP_FEATURE_REMOVE_OID_STASH */ |
450 | | netsnmp_feature_unused(oid_stash); |
451 | | #endif/* NETSNMP_FEATURE_REMOVE_OID_STASH */ |
452 | | |
453 | | #ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE |
454 | | void |
455 | | netsnmp_oid_stash_no_free(void *bogus) |
456 | 0 | { |
457 | | /* noop */ |
458 | 0 | } |
459 | | #endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE */ |
460 | | |
461 | | /** @} */ |