| /* |
| * agent_registry.c |
| */ |
| /* Portions of this file are subject to the following copyright(s). See |
| * the Net-SNMP's COPYING file for more details and other copyrights |
| * that may apply: |
| */ |
| /* |
| * Portions of this file are copyrighted by: |
| * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms specified in the COPYING file |
| * distributed with the Net-SNMP package. |
| */ |
| /** @defgroup agent_registry Maintain a registry of MIB subtrees, together with related information regarding mibmodule, sessions, etc |
| * @ingroup agent |
| * |
| * @{ |
| */ |
| |
| #define IN_SNMP_VARS_C |
| |
| #include <net-snmp/net-snmp-config.h> |
| #include <signal.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #endif |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <fcntl.h> |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include <net-snmp/agent/agent_callbacks.h> |
| |
| #include "snmpd.h" |
| #include "mibgroup/struct.h" |
| #include <net-snmp/agent/old_api.h> |
| #include <net-snmp/agent/null.h> |
| #include <net-snmp/agent/table.h> |
| #include <net-snmp/agent/table_iterator.h> |
| #include <net-snmp/agent/agent_registry.h> |
| #include "mib_module_includes.h" |
| |
| #ifdef USING_AGENTX_SUBAGENT_MODULE |
| #include "agentx/subagent.h" |
| #include "agentx/client.h" |
| #endif |
| |
| static void register_mib_detach_node(netsnmp_subtree *s); |
| NETSNMP_STATIC_INLINE void invalidate_lookup_cache(const char *context); |
| void netsnmp_set_lookup_cache_size(int newsize); |
| int netsnmp_get_lookup_cache_size(void); |
| |
| subtree_context_cache *context_subtrees = NULL; |
| |
| void |
| netsnmp_subtree_free(netsnmp_subtree *a) |
| { |
| if (a != NULL) { |
| if (a->variables != NULL && netsnmp_oid_equals(a->name_a, a->namelen, |
| a->start_a, a->start_len) == 0) { |
| SNMP_FREE(a->variables); |
| } |
| SNMP_FREE(a->name_a); |
| a->namelen = 0; |
| SNMP_FREE(a->start_a); |
| a->start_len = 0; |
| SNMP_FREE(a->end_a); |
| a->end_len = 0; |
| SNMP_FREE(a->label_a); |
| netsnmp_handler_registration_free(a->reginfo); |
| a->reginfo = NULL; |
| SNMP_FREE(a); |
| } |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_deepcopy(netsnmp_subtree *a) |
| { |
| netsnmp_subtree *b = (netsnmp_subtree *)calloc(1, sizeof(netsnmp_subtree)); |
| |
| if (b != NULL) { |
| memcpy(b, a, sizeof(netsnmp_subtree)); |
| b->name_a = snmp_duplicate_objid(a->name_a, a->namelen); |
| b->start_a = snmp_duplicate_objid(a->start_a, a->start_len); |
| b->end_a = snmp_duplicate_objid(a->end_a, a->end_len); |
| b->label_a = strdup(a->label_a); |
| |
| if (b->name_a == NULL || b->start_a == NULL || |
| b->end_a == NULL || b->label_a == NULL) { |
| netsnmp_subtree_free(b); |
| return NULL; |
| } |
| |
| if (a->variables != NULL) { |
| b->variables = (struct variable *)malloc(a->variables_len * |
| a->variables_width); |
| if (b->variables != NULL) { |
| memcpy(b->variables, a->variables,a->variables_len*a->variables_width); |
| } else { |
| netsnmp_subtree_free(b); |
| return NULL; |
| } |
| } |
| |
| if (a->reginfo != NULL) { |
| b->reginfo = netsnmp_handler_registration_dup(a->reginfo); |
| if (b->reginfo == NULL) { |
| netsnmp_subtree_free(b); |
| return NULL; |
| } |
| } |
| } |
| return b; |
| } |
| |
| subtree_context_cache * |
| get_top_context_cache(void) |
| { |
| return context_subtrees; |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_find_first(const char *context_name) |
| { |
| subtree_context_cache *ptr; |
| |
| if (!context_name) { |
| context_name = ""; |
| } |
| |
| DEBUGMSGTL(("subtree", "looking for subtree for context: \"%s\"\n", |
| context_name)); |
| for (ptr = context_subtrees; ptr != NULL; ptr = ptr->next) { |
| if (ptr->context_name != NULL && |
| strcmp(ptr->context_name, context_name) == 0) { |
| DEBUGMSGTL(("subtree", "found one for: \"%s\"\n", context_name)); |
| return ptr->first_subtree; |
| } |
| } |
| DEBUGMSGTL(("subtree", "didn't find a subtree for context: \"%s\"\n", |
| context_name)); |
| return NULL; |
| } |
| |
| netsnmp_subtree * |
| add_subtree(netsnmp_subtree *new_tree, const char *context_name) |
| { |
| subtree_context_cache *ptr = SNMP_MALLOC_TYPEDEF(subtree_context_cache); |
| |
| if (!context_name) { |
| context_name = ""; |
| } |
| |
| if (!ptr) { |
| return NULL; |
| } |
| |
| DEBUGMSGTL(("subtree", "adding subtree for context: \"%s\"\n", |
| context_name)); |
| |
| ptr->next = context_subtrees; |
| ptr->first_subtree = new_tree; |
| ptr->context_name = strdup(context_name); |
| context_subtrees = ptr; |
| |
| return ptr->first_subtree; |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_replace_first(netsnmp_subtree *new_tree, |
| const char *context_name) |
| { |
| subtree_context_cache *ptr; |
| if (!context_name) { |
| context_name = ""; |
| } |
| for (ptr = context_subtrees; ptr != NULL; ptr = ptr->next) { |
| if (ptr->context_name != NULL && |
| strcmp(ptr->context_name, context_name) == 0) { |
| ptr->first_subtree = new_tree; |
| return ptr->first_subtree; |
| } |
| } |
| return add_subtree(new_tree, context_name); |
| } |
| |
| NETSNMP_INLINE void |
| netsnmp_subtree_change_next(netsnmp_subtree *ptr, netsnmp_subtree *thenext) |
| { |
| ptr->next = thenext; |
| if (thenext) |
| netsnmp_oid_compare_ll(ptr->start_a, |
| ptr->start_len, |
| thenext->start_a, |
| thenext->start_len, |
| &thenext->oid_off); |
| } |
| |
| NETSNMP_INLINE void |
| netsnmp_subtree_change_prev(netsnmp_subtree *ptr, netsnmp_subtree *theprev) |
| { |
| ptr->prev = theprev; |
| if (theprev) |
| netsnmp_oid_compare_ll(theprev->start_a, |
| theprev->start_len, |
| ptr->start_a, |
| ptr->start_len, |
| &ptr->oid_off); |
| } |
| |
| int |
| netsnmp_subtree_compare(const netsnmp_subtree *ap, const netsnmp_subtree *bp) |
| { |
| return snmp_oid_compare(ap->name_a, ap->namelen, bp->name_a, bp->namelen); |
| } |
| |
| void |
| netsnmp_subtree_join(netsnmp_subtree *root) |
| { |
| netsnmp_subtree *s, *tmp, *c, *d; |
| |
| while (root != NULL) { |
| s = root->next; |
| while (s != NULL && root->reginfo == s->reginfo) { |
| tmp = s->next; |
| DEBUGMSGTL(("subtree", "root start ")); |
| DEBUGMSGOID(("subtree", root->start_a, root->start_len)); |
| DEBUGMSG(("subtree", " (original end ")); |
| DEBUGMSGOID(("subtree", root->end_a, root->end_len)); |
| DEBUGMSG(("subtree", ")\n")); |
| DEBUGMSGTL(("subtree", " JOINING to ")); |
| DEBUGMSGOID(("subtree", s->start_a, s->start_len)); |
| |
| SNMP_FREE(root->end_a); |
| root->end_a = s->end_a; |
| root->end_len = s->end_len; |
| s->end_a = NULL; |
| |
| for (c = root; c != NULL; c = c->children) { |
| netsnmp_subtree_change_next(c, s->next); |
| } |
| for (c = s; c != NULL; c = c->children) { |
| netsnmp_subtree_change_prev(c, root); |
| } |
| DEBUGMSG(("subtree", " so new end ")); |
| DEBUGMSGOID(("subtree", root->end_a, root->end_len)); |
| DEBUGMSG(("subtree", "\n")); |
| /* |
| * Probably need to free children too? |
| */ |
| for (c = s->children; c != NULL; c = d) { |
| d = c->children; |
| netsnmp_subtree_free(c); |
| } |
| netsnmp_subtree_free(s); |
| s = tmp; |
| } |
| root = root->next; |
| } |
| } |
| |
| |
| /* |
| * Split the subtree into two at the specified point, |
| * returning the new (second) subtree |
| */ |
| netsnmp_subtree * |
| netsnmp_subtree_split(netsnmp_subtree *current, oid name[], int name_len) |
| { |
| struct variable *vp = NULL; |
| netsnmp_subtree *new_sub, *ptr; |
| int i = 0, rc = 0, rc2 = 0; |
| size_t common_len = 0; |
| char *cp; |
| oid *tmp_a, *tmp_b; |
| |
| if (snmp_oid_compare(name, name_len, current->end_a, current->end_len)>0) { |
| /* Split comes after the end of this subtree */ |
| return NULL; |
| } |
| |
| new_sub = netsnmp_subtree_deepcopy(current); |
| if (new_sub == NULL) { |
| return NULL; |
| } |
| |
| /* Set up the point of division. */ |
| tmp_a = snmp_duplicate_objid(name, name_len); |
| if (tmp_a == NULL) { |
| netsnmp_subtree_free(new_sub); |
| return NULL; |
| } |
| tmp_b = snmp_duplicate_objid(name, name_len); |
| if (tmp_b == NULL) { |
| netsnmp_subtree_free(new_sub); |
| SNMP_FREE(tmp_a); |
| return NULL; |
| } |
| |
| if (current->end_a != NULL) { |
| SNMP_FREE(current->end_a); |
| } |
| current->end_a = tmp_a; |
| current->end_len = name_len; |
| if (new_sub->start_a != NULL) { |
| SNMP_FREE(new_sub->start_a); |
| } |
| new_sub->start_a = tmp_b; |
| new_sub->start_len = name_len; |
| |
| /* Split the variables between the two new subtrees. */ |
| i = current->variables_len; |
| current->variables_len = 0; |
| |
| for (vp = current->variables; i > 0; i--) { |
| /* Note that the variable "name" field omits the prefix common to the |
| whole registration, hence the strange comparison here. */ |
| |
| rc = snmp_oid_compare(vp->name, vp->namelen, |
| name + current->namelen, |
| name_len - current->namelen); |
| |
| if (name_len - current->namelen > vp->namelen) { |
| common_len = vp->namelen; |
| } else { |
| common_len = name_len - current->namelen; |
| } |
| |
| rc2 = snmp_oid_compare(vp->name, common_len, |
| name + current->namelen, common_len); |
| |
| if (rc >= 0) { |
| break; /* All following variables belong to the second subtree */ |
| } |
| |
| current->variables_len++; |
| if (rc2 < 0) { |
| new_sub->variables_len--; |
| cp = (char *) new_sub->variables; |
| new_sub->variables = (struct variable *)(cp + |
| new_sub->variables_width); |
| } |
| vp = (struct variable *) ((char *) vp + current->variables_width); |
| } |
| |
| /* Delegated trees should retain their variables regardless */ |
| if (current->variables_len > 0 && |
| IS_DELEGATED((u_char) current->variables[0].type)) { |
| new_sub->variables_len = 1; |
| new_sub->variables = current->variables; |
| } |
| |
| /* Propogate this split down through any children */ |
| if (current->children) { |
| new_sub->children = netsnmp_subtree_split(current->children, |
| name, name_len); |
| } |
| |
| /* Retain the correct linking of the list */ |
| for (ptr = current; ptr != NULL; ptr = ptr->children) { |
| netsnmp_subtree_change_next(ptr, new_sub); |
| } |
| for (ptr = new_sub; ptr != NULL; ptr = ptr->children) { |
| netsnmp_subtree_change_prev(ptr, current); |
| } |
| for (ptr = new_sub->next; ptr != NULL; ptr=ptr->children) { |
| netsnmp_subtree_change_prev(ptr, new_sub); |
| } |
| |
| return new_sub; |
| } |
| |
| int |
| netsnmp_subtree_load(netsnmp_subtree *new_sub, const char *context_name) |
| { |
| netsnmp_subtree *tree1, *tree2, *new2; |
| netsnmp_subtree *prev, *next; |
| int res, rc = 0; |
| |
| if (new_sub == NULL) { |
| return MIB_REGISTERED_OK; /* Degenerate case */ |
| } |
| |
| if (!netsnmp_subtree_find_first(context_name)) { |
| static int inloop = 0; |
| if (!inloop) { |
| oid ccitt[1] = { 0 }; |
| oid iso[1] = { 1 }; |
| oid joint_ccitt_iso[1] = { 2 }; |
| inloop = 1; |
| netsnmp_register_null_context(snmp_duplicate_objid(ccitt, 1), 1, |
| context_name); |
| netsnmp_register_null_context(snmp_duplicate_objid(iso, 1), 1, |
| context_name); |
| netsnmp_register_null_context(snmp_duplicate_objid(joint_ccitt_iso, 1), |
| 1, context_name); |
| inloop = 0; |
| } |
| } |
| |
| /* Find the subtree that contains the start of the new subtree (if |
| any)...*/ |
| |
| tree1 = netsnmp_subtree_find(new_sub->start_a, new_sub->start_len, |
| NULL, context_name); |
| |
| /* ... and the subtree that follows the new one (NULL implies this is the |
| final region covered). */ |
| |
| if (tree1 == NULL) { |
| tree2 = netsnmp_subtree_find_next(new_sub->start_a, new_sub->start_len, |
| NULL, context_name); |
| } else { |
| tree2 = tree1->next; |
| } |
| |
| /* Handle new subtrees that start in virgin territory. */ |
| |
| if (tree1 == NULL) { |
| new2 = NULL; |
| /* Is there any overlap with later subtrees? */ |
| if (tree2 && snmp_oid_compare(new_sub->end_a, new_sub->end_len, |
| tree2->start_a, tree2->start_len) > 0) { |
| new2 = netsnmp_subtree_split(new_sub, |
| tree2->start_a, tree2->start_len); |
| } |
| |
| /* Link the new subtree (less any overlapping region) with the list of |
| existing registrations. */ |
| |
| if (tree2) { |
| netsnmp_subtree_change_prev(new_sub, tree2->prev); |
| netsnmp_subtree_change_prev(tree2, new_sub); |
| } else { |
| netsnmp_subtree_change_prev(new_sub, |
| netsnmp_subtree_find_prev(new_sub->start_a, |
| new_sub->start_len, NULL, context_name)); |
| |
| if (new_sub->prev) { |
| netsnmp_subtree_change_next(new_sub->prev, new_sub); |
| } else { |
| netsnmp_subtree_replace_first(new_sub, context_name); |
| } |
| |
| netsnmp_subtree_change_next(new_sub, tree2); |
| |
| /* If there was any overlap, recurse to merge in the overlapping |
| region (including anything that may follow the overlap). */ |
| if (new2) { |
| return netsnmp_subtree_load(new2, context_name); |
| } |
| } |
| } else { |
| /* If the new subtree starts *within* an existing registration |
| (rather than at the same point as it), then split the existing |
| subtree at this point. */ |
| |
| if (netsnmp_oid_equals(new_sub->start_a, new_sub->start_len, |
| tree1->start_a, tree1->start_len) != 0) { |
| tree1 = netsnmp_subtree_split(tree1, new_sub->start_a, |
| new_sub->start_len); |
| } |
| |
| if (tree1 == NULL) { |
| return MIB_REGISTRATION_FAILED; |
| } |
| |
| /* Now consider the end of this existing subtree: |
| |
| If it matches the new subtree precisely, |
| simply merge the new one into the list of children |
| |
| If it includes the whole of the new subtree, |
| split it at the appropriate point, and merge again |
| |
| If the new subtree extends beyond this existing region, |
| split it, and recurse to merge the two parts. */ |
| |
| rc = snmp_oid_compare(new_sub->end_a, new_sub->end_len, |
| tree1->end_a, tree1->end_len); |
| |
| switch (rc) { |
| |
| case -1: |
| /* Existing subtree contains new one. */ |
| netsnmp_subtree_split(tree1, new_sub->end_a, new_sub->end_len); |
| /* Fall Through */ |
| |
| case 0: |
| /* The two trees match precisely. */ |
| |
| /* Note: This is the only point where the original registration |
| OID ("name") is used. */ |
| |
| prev = NULL; |
| next = tree1; |
| |
| while (next && next->namelen > new_sub->namelen) { |
| prev = next; |
| next = next->children; |
| } |
| |
| while (next && next->namelen == new_sub->namelen && |
| next->priority < new_sub->priority ) { |
| prev = next; |
| next = next->children; |
| } |
| |
| if (next && (next->namelen == new_sub->namelen) && |
| (next->priority == new_sub->priority)) { |
| if (new_sub->namelen != 1) /* ignore root OID dups */ |
| snmp_log(LOG_ERR, "duplicate registration (%s, %s)", next->label_a, new_sub->label_a); |
| return MIB_DUPLICATE_REGISTRATION; |
| } |
| |
| if (prev) { |
| prev->children = new_sub; |
| new_sub->children = next; |
| netsnmp_subtree_change_prev(new_sub, prev->prev); |
| netsnmp_subtree_change_next(new_sub, prev->next); |
| } else { |
| new_sub->children = next; |
| netsnmp_subtree_change_prev(new_sub, next->prev); |
| netsnmp_subtree_change_next(new_sub, next->next); |
| |
| for (next = new_sub->next; next != NULL;next = next->children){ |
| netsnmp_subtree_change_prev(next, new_sub); |
| } |
| |
| for (prev = new_sub->prev; prev != NULL;prev = prev->children){ |
| netsnmp_subtree_change_next(prev, new_sub); |
| } |
| } |
| break; |
| |
| case 1: |
| /* New subtree contains the existing one. */ |
| new2 = netsnmp_subtree_split(new_sub, tree1->end_a,tree1->end_len); |
| res = netsnmp_subtree_load(new_sub, context_name); |
| if (res != MIB_REGISTERED_OK) { |
| netsnmp_subtree_free(new2); |
| return res; |
| } |
| return netsnmp_subtree_load(new2, context_name); |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Note: reginfo will be freed on failures |
| */ |
| int |
| netsnmp_register_mib(const char *moduleName, |
| struct variable *var, |
| size_t varsize, |
| size_t numvars, |
| oid * mibloc, |
| size_t mibloclen, |
| int priority, |
| int range_subid, |
| oid range_ubound, |
| netsnmp_session * ss, |
| const char *context, |
| int timeout, |
| int flags, |
| netsnmp_handler_registration *reginfo, |
| int perform_callback) |
| { |
| netsnmp_subtree *subtree, *sub2; |
| int res, i; |
| struct register_parameters reg_parms; |
| int old_lookup_cache_val = netsnmp_get_lookup_cache_size(); |
| |
| if (moduleName == NULL || |
| mibloc == NULL) { |
| /* Shouldn't happen ??? */ |
| netsnmp_handler_registration_free(reginfo); |
| return MIB_REGISTRATION_FAILED; |
| } |
| subtree = (netsnmp_subtree *)calloc(1, sizeof(netsnmp_subtree)); |
| if (subtree == NULL) { |
| netsnmp_handler_registration_free(reginfo); |
| return MIB_REGISTRATION_FAILED; |
| } |
| |
| DEBUGMSGTL(("register_mib", "registering \"%s\" at ", moduleName)); |
| DEBUGMSGOIDRANGE(("register_mib", mibloc, mibloclen, range_subid, |
| range_ubound)); |
| DEBUGMSG(("register_mib", " with context \"%s\"\n", |
| SNMP_STRORNULL(context))); |
| |
| /* |
| * verify that the passed context is equal to the context |
| * in the reginfo. |
| * (which begs the question, why do we have both? It appears that the |
| * reginfo item didn't appear til 5.2) |
| */ |
| if( ((NULL == context) && (NULL != reginfo->contextName)) || |
| ((NULL != context) && (NULL == reginfo->contextName)) || |
| ( ((NULL != context) && (NULL != reginfo->contextName)) && |
| (0 != strcmp(context, reginfo->contextName))) ) { |
| snmp_log(LOG_WARNING,"context passed during registration does not " |
| "equal the reginfo contextName! ('%s' != '%s')\n", |
| context, reginfo->contextName); |
| netsnmp_assert(!"register context == reginfo->contextName"); /* always false */ |
| } |
| |
| /* Create the new subtree node being registered. */ |
| |
| subtree->reginfo = reginfo; |
| subtree->name_a = snmp_duplicate_objid(mibloc, mibloclen); |
| subtree->start_a = snmp_duplicate_objid(mibloc, mibloclen); |
| subtree->end_a = snmp_duplicate_objid(mibloc, mibloclen); |
| subtree->label_a = strdup(moduleName); |
| if (subtree->name_a == NULL || subtree->start_a == NULL || |
| subtree->end_a == NULL || subtree->label_a == NULL) { |
| netsnmp_subtree_free(subtree); /* also frees reginfo */ |
| return MIB_REGISTRATION_FAILED; |
| } |
| subtree->namelen = (u_char)mibloclen; |
| subtree->start_len = (u_char)mibloclen; |
| subtree->end_len = (u_char)mibloclen; |
| subtree->end_a[mibloclen - 1]++; |
| |
| if (var != NULL) { |
| subtree->variables = (struct variable *)malloc(varsize*numvars); |
| if (subtree->variables == NULL) { |
| netsnmp_subtree_free(subtree); /* also frees reginfo */ |
| return MIB_REGISTRATION_FAILED; |
| } |
| memcpy(subtree->variables, var, numvars*varsize); |
| subtree->variables_len = numvars; |
| subtree->variables_width = varsize; |
| } |
| subtree->priority = priority; |
| subtree->timeout = timeout; |
| subtree->range_subid = range_subid; |
| subtree->range_ubound = range_ubound; |
| subtree->session = ss; |
| subtree->flags = (u_char)flags; /* used to identify instance oids */ |
| subtree->flags |= SUBTREE_ATTACHED; |
| subtree->global_cacheid = reginfo->global_cacheid; |
| |
| netsnmp_set_lookup_cache_size(0); |
| res = netsnmp_subtree_load(subtree, context); |
| |
| /* If registering a range, use the first subtree as a template for the |
| rest of the range. */ |
| |
| if (res == MIB_REGISTERED_OK && range_subid != 0) { |
| for (i = mibloc[range_subid - 1] + 1; i <= (int)range_ubound; i++) { |
| sub2 = netsnmp_subtree_deepcopy(subtree); |
| |
| if (sub2 == NULL) { |
| unregister_mib_context(mibloc, mibloclen, priority, |
| range_subid, range_ubound, context); |
| netsnmp_set_lookup_cache_size(old_lookup_cache_val); |
| invalidate_lookup_cache(context); |
| return MIB_REGISTRATION_FAILED; |
| } |
| |
| sub2->name_a[range_subid - 1] = i; |
| sub2->start_a[range_subid - 1] = i; |
| sub2->end_a[range_subid - 1] = i; /* XXX - ???? */ |
| if (range_subid == (int)mibloclen) { |
| ++sub2->end_a[range_subid - 1]; |
| } |
| sub2->flags |= SUBTREE_ATTACHED; |
| sub2->global_cacheid = reginfo->global_cacheid; |
| /* FRQ This is essential for requests to succeed! */ |
| sub2->reginfo->rootoid[range_subid - 1] = i; |
| |
| res = netsnmp_subtree_load(sub2, context); |
| if (res != MIB_REGISTERED_OK) { |
| unregister_mib_context(mibloc, mibloclen, priority, |
| range_subid, range_ubound, context); |
| netsnmp_subtree_free(sub2); |
| netsnmp_set_lookup_cache_size(old_lookup_cache_val); |
| invalidate_lookup_cache(context); |
| return res; |
| } |
| } |
| } else if (res == MIB_DUPLICATE_REGISTRATION || |
| res == MIB_REGISTRATION_FAILED) { |
| netsnmp_set_lookup_cache_size(old_lookup_cache_val); |
| invalidate_lookup_cache(context); |
| netsnmp_subtree_free(subtree); |
| return res; |
| } |
| |
| /* |
| * mark the MIB as detached, if there's no master agent present as of now |
| */ |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) { |
| extern struct snmp_session *main_session; |
| if (main_session == NULL) { |
| register_mib_detach_node(subtree); |
| } |
| } |
| |
| if (res == MIB_REGISTERED_OK && perform_callback) { |
| memset(®_parms, 0x0, sizeof(reg_parms)); |
| reg_parms.name = mibloc; |
| reg_parms.namelen = mibloclen; |
| reg_parms.priority = priority; |
| reg_parms.range_subid = range_subid; |
| reg_parms.range_ubound = range_ubound; |
| reg_parms.timeout = timeout; |
| reg_parms.flags = (u_char) flags; |
| reg_parms.contextName = context; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REGISTER_OID, ®_parms); |
| } |
| |
| netsnmp_set_lookup_cache_size(old_lookup_cache_val); |
| invalidate_lookup_cache(context); |
| return res; |
| } |
| |
| /* |
| * Reattach a particular node. |
| */ |
| |
| static void |
| register_mib_reattach_node(netsnmp_subtree *s) |
| { |
| if ((s != NULL) && (s->namelen > 1) && !(s->flags & SUBTREE_ATTACHED)) { |
| struct register_parameters reg_parms; |
| /* |
| * only do registrations that are not the top level nodes |
| */ |
| memset(®_parms, 0x0, sizeof(reg_parms)); |
| |
| /* |
| * XXX: do this better |
| */ |
| reg_parms.name = s->name_a; |
| reg_parms.namelen = s->namelen; |
| reg_parms.priority = s->priority; |
| reg_parms.range_subid = s->range_subid; |
| reg_parms.range_ubound = s->range_ubound; |
| reg_parms.timeout = s->timeout; |
| reg_parms.flags = s->flags; |
| if ((NULL != s->reginfo) && (NULL != s->reginfo->contextName)) |
| reg_parms.contextName = s->reginfo->contextName; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_REGISTER_OID, ®_parms); |
| s->flags |= SUBTREE_ATTACHED; |
| } |
| } |
| |
| /* |
| * Call callbacks to reattach all our nodes. |
| */ |
| |
| void |
| register_mib_reattach(void) |
| { |
| netsnmp_subtree *s, *t; |
| subtree_context_cache *ptr; |
| |
| for (ptr = context_subtrees; ptr; ptr = ptr->next) { |
| for (s = ptr->first_subtree; s != NULL; s = s->next) { |
| register_mib_reattach_node(s); |
| for (t = s->children; t != NULL; t = t->children) { |
| register_mib_reattach_node(t); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Mark a node as detached. |
| */ |
| |
| static void |
| register_mib_detach_node(netsnmp_subtree *s) |
| { |
| if (s != NULL) { |
| s->flags = s->flags & ~SUBTREE_ATTACHED; |
| } |
| } |
| |
| /* |
| * Mark all our registered OIDs as detached. This is only really |
| * useful for subagent protocols, when a connection is lost or |
| * something. |
| */ |
| |
| void |
| register_mib_detach(void) |
| { |
| netsnmp_subtree *s, *t; |
| subtree_context_cache *ptr; |
| for (ptr = context_subtrees; ptr; ptr = ptr->next) { |
| for (s = ptr->first_subtree; s != NULL; s = s->next) { |
| register_mib_detach_node(s); |
| for (t = s->children; t != NULL; t = t->children) { |
| register_mib_detach_node(t); |
| } |
| } |
| } |
| } |
| |
| int |
| register_mib_context(const char *moduleName, |
| struct variable *var, |
| size_t varsize, |
| size_t numvars, |
| oid * mibloc, |
| size_t mibloclen, |
| int priority, |
| int range_subid, |
| oid range_ubound, |
| netsnmp_session * ss, |
| const char *context, int timeout, int flags) |
| { |
| return netsnmp_register_old_api(moduleName, var, varsize, numvars, |
| mibloc, mibloclen, priority, |
| range_subid, range_ubound, ss, context, |
| timeout, flags); |
| } |
| |
| int |
| register_mib_range(const char *moduleName, |
| struct variable *var, |
| size_t varsize, |
| size_t numvars, |
| oid * mibloc, |
| size_t mibloclen, |
| int priority, |
| int range_subid, oid range_ubound, netsnmp_session * ss) |
| { |
| return register_mib_context(moduleName, var, varsize, numvars, |
| mibloc, mibloclen, priority, |
| range_subid, range_ubound, ss, "", -1, 0); |
| } |
| |
| int |
| register_mib_priority(const char *moduleName, |
| struct variable *var, |
| size_t varsize, |
| size_t numvars, |
| oid * mibloc, size_t mibloclen, int priority) |
| { |
| return register_mib_range(moduleName, var, varsize, numvars, |
| mibloc, mibloclen, priority, 0, 0, NULL); |
| } |
| |
| int |
| register_mib(const char *moduleName, |
| struct variable *var, |
| size_t varsize, |
| size_t numvars, oid * mibloc, size_t mibloclen) |
| { |
| return register_mib_priority(moduleName, var, varsize, numvars, |
| mibloc, mibloclen, DEFAULT_MIB_PRIORITY); |
| } |
| |
| void |
| netsnmp_subtree_unload(netsnmp_subtree *sub, netsnmp_subtree *prev, const char *context) |
| { |
| netsnmp_subtree *ptr; |
| |
| DEBUGMSGTL(("register_mib", "unload(")); |
| if (sub != NULL) { |
| DEBUGMSGOID(("register_mib", sub->start_a, sub->start_len)); |
| } else { |
| DEBUGMSG(("register_mib", "[NIL]")); |
| return; |
| } |
| DEBUGMSG(("register_mib", ", ")); |
| if (prev != NULL) { |
| DEBUGMSGOID(("register_mib", prev->start_a, prev->start_len)); |
| } else { |
| DEBUGMSG(("register_mib", "[NIL]")); |
| } |
| DEBUGMSG(("register_mib", ")\n")); |
| |
| if (prev != NULL) { /* non-leading entries are easy */ |
| prev->children = sub->children; |
| invalidate_lookup_cache(context); |
| return; |
| } |
| /* |
| * otherwise, we need to amend our neighbours as well |
| */ |
| |
| if (sub->children == NULL) { /* just remove this node completely */ |
| for (ptr = sub->prev; ptr; ptr = ptr->children) { |
| netsnmp_subtree_change_next(ptr, sub->next); |
| } |
| for (ptr = sub->next; ptr; ptr = ptr->children) { |
| netsnmp_subtree_change_prev(ptr, sub->prev); |
| } |
| |
| if (sub->prev == NULL) { |
| netsnmp_subtree_replace_first(sub->next, context); |
| } |
| |
| } else { |
| for (ptr = sub->prev; ptr; ptr = ptr->children) |
| netsnmp_subtree_change_next(ptr, sub->children); |
| for (ptr = sub->next; ptr; ptr = ptr->children) |
| netsnmp_subtree_change_prev(ptr, sub->children); |
| |
| if (sub->prev == NULL) { |
| netsnmp_subtree_replace_first(sub->children, context); |
| } |
| } |
| invalidate_lookup_cache(context); |
| } |
| |
| /** |
| * Unregisters an OID that has an associated context name value. |
| * Typically used when a module has multiple contexts defined. The parameters |
| * priority, range_subid, and range_ubound should be used in conjunction with |
| * agentx, see RFC 2741, otherwise these values should always be 0. |
| * |
| * @param name the specific OID to unregister if it conatins the associated |
| * context. |
| * |
| * @param len the length of the OID, use OID_LENGTH macro. |
| * |
| * @param priority a value between 1 and 255, used to achieve a desired |
| * configuration when different sessions register identical or |
| * overlapping regions. Subagents with no particular |
| * knowledge of priority should register with the default |
| * value of 127. |
| * |
| * @param range_subid permits specifying a range in place of one of a subtree |
| * sub-identifiers. When this value is zero, no range is |
| * being specified. |
| * |
| * @param range_ubound the upper bound of a sub-identifier's range. |
| * This field is present only if range_subid is not 0. |
| * |
| * @param context a context name that has been created |
| * |
| * @return |
| * |
| */ |
| int |
| unregister_mib_context(oid * name, size_t len, int priority, |
| int range_subid, oid range_ubound, |
| const char *context) |
| { |
| netsnmp_subtree *list, *myptr = NULL; |
| netsnmp_subtree *prev, *child, *next; /* loop through children */ |
| struct register_parameters reg_parms; |
| int old_lookup_cache_val = netsnmp_get_lookup_cache_size(); |
| int unregistering = 1; |
| int orig_subid_val = -1; |
| |
| netsnmp_set_lookup_cache_size(0); |
| |
| if ((range_subid != 0) && (range_subid <= len)) |
| orig_subid_val = name[range_subid-1]; |
| |
| while(unregistering){ |
| DEBUGMSGTL(("register_mib", "unregistering ")); |
| DEBUGMSGOIDRANGE(("register_mib", name, len, range_subid, range_ubound)); |
| DEBUGMSG(("register_mib", "\n")); |
| |
| list = netsnmp_subtree_find(name, len, netsnmp_subtree_find_first(context), |
| context); |
| if (list == NULL) { |
| return MIB_NO_SUCH_REGISTRATION; |
| } |
| |
| for (child = list, prev = NULL; child != NULL; |
| prev = child, child = child->children) { |
| if (netsnmp_oid_equals(child->name_a, child->namelen, name, len) == 0 && |
| child->priority == priority) { |
| break; /* found it */ |
| } |
| } |
| |
| if (child == NULL) { |
| return MIB_NO_SUCH_REGISTRATION; |
| } |
| |
| netsnmp_subtree_unload(child, prev, context); |
| myptr = child; /* remember this for later */ |
| |
| /* |
| * Now handle any occurances in the following subtrees, |
| * as a result of splitting this range. Due to the |
| * nature of the way such splits work, the first |
| * subtree 'slice' that doesn't refer to the given |
| * name marks the end of the original region. |
| * |
| * This should also serve to register ranges. |
| */ |
| |
| for (list = myptr->next; list != NULL; list = next) { |
| next = list->next; /* list gets freed sometimes; cache next */ |
| for (child = list, prev = NULL; child != NULL; |
| prev = child, child = child->children) { |
| if ((netsnmp_oid_equals(child->name_a, child->namelen, |
| name, len) == 0) && |
| (child->priority == priority)) { |
| netsnmp_subtree_unload(child, prev, context); |
| netsnmp_subtree_free(child); |
| break; |
| } |
| } |
| if (child == NULL) /* Didn't find the given name */ |
| break; |
| } |
| |
| /* Maybe we are in a range... */ |
| if (orig_subid_val != -1){ |
| if (++name[range_subid-1] >= orig_subid_val+range_ubound) |
| { |
| unregistering=0; |
| name[range_subid-1] = orig_subid_val; |
| } |
| } |
| else { |
| unregistering=0; |
| } |
| } |
| |
| memset(®_parms, 0x0, sizeof(reg_parms)); |
| reg_parms.name = name; |
| reg_parms.namelen = len; |
| reg_parms.priority = priority; |
| reg_parms.range_subid = range_subid; |
| reg_parms.range_ubound = range_ubound; |
| reg_parms.flags = 0x00; /* this is okay I think */ |
| reg_parms.contextName = context; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_UNREGISTER_OID, ®_parms); |
| |
| netsnmp_subtree_free(myptr); |
| netsnmp_set_lookup_cache_size(old_lookup_cache_val); |
| invalidate_lookup_cache(context); |
| return MIB_UNREGISTERED_OK; |
| } |
| |
| int |
| netsnmp_unregister_mib_table_row(oid * name, size_t len, int priority, |
| int var_subid, oid range_ubound, |
| const char *context) |
| { |
| netsnmp_subtree *list, *myptr, *futureptr; |
| netsnmp_subtree *prev, *child; /* loop through children */ |
| struct register_parameters reg_parms; |
| oid range_lbound = name[var_subid - 1]; |
| |
| DEBUGMSGTL(("register_mib", "unregistering ")); |
| DEBUGMSGOIDRANGE(("register_mib", name, len, var_subid, range_ubound)); |
| DEBUGMSG(("register_mib", "\n")); |
| |
| for (; name[var_subid - 1] <= range_ubound; name[var_subid - 1]++) { |
| list = netsnmp_subtree_find(name, len, |
| netsnmp_subtree_find_first(context), context); |
| |
| if (list == NULL) { |
| continue; |
| } |
| |
| for (child = list, prev = NULL; child != NULL; |
| prev = child, child = child->children) { |
| |
| if (netsnmp_oid_equals(child->name_a, child->namelen, |
| name, len) == 0 && |
| (child->priority == priority)) { |
| break; /* found it */ |
| } |
| } |
| |
| if (child == NULL) { |
| continue; |
| } |
| |
| netsnmp_subtree_unload(child, prev, context); |
| myptr = child; /* remember this for later */ |
| |
| for (list = myptr->next; list != NULL; list = futureptr) { |
| /* remember the next spot in the list in case we free this node */ |
| futureptr = list->next; |
| |
| /* check each child */ |
| for (child = list, prev = NULL; child != NULL; |
| prev = child, child = child->children) { |
| |
| if (netsnmp_oid_equals(child->name_a, child->namelen, |
| name, len) == 0 && |
| (child->priority == priority)) { |
| netsnmp_subtree_unload(child, prev, context); |
| netsnmp_subtree_free(child); |
| break; |
| } |
| } |
| |
| /* XXX: wjh: not sure why we're bailing here */ |
| if (child == NULL) { /* Didn't find the given name */ |
| break; |
| } |
| } |
| netsnmp_subtree_free(myptr); |
| } |
| |
| name[var_subid - 1] = range_lbound; |
| memset(®_parms, 0x0, sizeof(reg_parms)); |
| reg_parms.name = name; |
| reg_parms.namelen = len; |
| reg_parms.priority = priority; |
| reg_parms.range_subid = var_subid; |
| reg_parms.range_ubound = range_ubound; |
| reg_parms.flags = 0x00; /* this is okay I think */ |
| reg_parms.contextName = context; |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_UNREGISTER_OID, ®_parms); |
| |
| return 0; |
| } |
| |
| int |
| unregister_mib_range(oid * name, size_t len, int priority, |
| int range_subid, oid range_ubound) |
| { |
| return unregister_mib_context(name, len, priority, range_subid, |
| range_ubound, ""); |
| } |
| |
| int |
| unregister_mib_priority(oid * name, size_t len, int priority) |
| { |
| return unregister_mib_range(name, len, priority, 0, 0); |
| } |
| |
| int |
| unregister_mib(oid * name, size_t len) |
| { |
| return unregister_mib_priority(name, len, DEFAULT_MIB_PRIORITY); |
| } |
| |
| void |
| unregister_mibs_by_session(netsnmp_session * ss) |
| { |
| netsnmp_subtree *list, *list2; |
| netsnmp_subtree *child, *prev, *next_child; |
| struct register_parameters rp; |
| subtree_context_cache *contextptr; |
| |
| DEBUGMSGTL(("register_mib", "unregister_mibs_by_session(%p) ctxt \"%s\"\n", |
| ss, (ss && ss->contextName) ? ss->contextName : "[NIL]")); |
| |
| for (contextptr = get_top_context_cache(); contextptr != NULL; |
| contextptr = contextptr->next) { |
| for (list = contextptr->first_subtree; list != NULL; list = list2) { |
| list2 = list->next; |
| |
| for (child = list, prev = NULL; child != NULL; child = next_child){ |
| next_child = child->children; |
| |
| if (((!ss || ss->flags & SNMP_FLAGS_SUBSESSION) && |
| child->session == ss) || |
| (!(!ss || ss->flags & SNMP_FLAGS_SUBSESSION) && child->session && |
| child->session->subsession == ss)) { |
| |
| memset(&rp,0x0,sizeof(rp)); |
| rp.name = child->name_a; |
| child->name_a = NULL; |
| rp.namelen = child->namelen; |
| rp.priority = child->priority; |
| rp.range_subid = child->range_subid; |
| rp.range_ubound = child->range_ubound; |
| rp.timeout = child->timeout; |
| rp.flags = child->flags; |
| if ((NULL != child->reginfo) && |
| (NULL != child->reginfo->contextName)) |
| rp.contextName = child->reginfo->contextName; |
| |
| if (child->reginfo != NULL) { |
| /* |
| * Don't let's free the session pointer just yet! |
| */ |
| child->reginfo->handler->myvoid = NULL; |
| netsnmp_handler_registration_free(child->reginfo); |
| child->reginfo = NULL; |
| } |
| |
| netsnmp_subtree_unload(child, prev, contextptr->context_name); |
| netsnmp_subtree_free(child); |
| |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_UNREGISTER_OID, &rp); |
| SNMP_FREE(rp.name); |
| } else { |
| prev = child; |
| } |
| } |
| } |
| netsnmp_subtree_join(contextptr->first_subtree); |
| } |
| } |
| |
| /* |
| * in_a_view: determines if a given snmp_pdu is allowed to see a |
| * given name/namelen OID pointer |
| * name IN - name of var, OUT - name matched |
| * nameLen IN -number of sub-ids in name, OUT - subid-is in matched name |
| * pi IN - relevant auth info re PDU |
| * cvp IN - relevant auth info re mib module |
| */ |
| |
| int |
| in_a_view(oid *name, size_t *namelen, netsnmp_pdu *pdu, int type) |
| { |
| struct view_parameters view_parms; |
| |
| if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) { |
| /* Enable bypassing of view-based access control */ |
| return VACM_SUCCESS; |
| } |
| |
| /* |
| * check for v1 and counter64s, since snmpv1 doesn't support it |
| */ |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| if (pdu->version == SNMP_VERSION_1 && type == ASN_COUNTER64) { |
| return VACM_NOTINVIEW; |
| } |
| #endif |
| |
| view_parms.pdu = pdu; |
| view_parms.name = name; |
| if (namelen != NULL) { |
| view_parms.namelen = *namelen; |
| } else { |
| view_parms.namelen = 0; |
| } |
| view_parms.errorcode = 0; |
| view_parms.check_subtree = 0; |
| |
| switch (pdu->version) { |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| case SNMP_VERSION_1: |
| #endif |
| #ifndef NETSNMP_DISABLE_SNMPV2C |
| case SNMP_VERSION_2c: |
| #endif |
| case SNMP_VERSION_3: |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_ACM_CHECK, &view_parms); |
| return view_parms.errorcode; |
| } |
| return VACM_NOSECNAME; |
| } |
| |
| /* |
| * check_acces: determines if a given snmp_pdu is ever going to be |
| * allowed to do anynthing or if it's not going to ever be |
| * authenticated. |
| */ |
| int |
| check_access(netsnmp_pdu *pdu) |
| { /* IN - pdu being checked */ |
| struct view_parameters view_parms; |
| view_parms.pdu = pdu; |
| view_parms.name = 0; |
| view_parms.namelen = 0; |
| view_parms.errorcode = 0; |
| view_parms.check_subtree = 0; |
| |
| if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) { |
| /* Enable bypassing of view-based access control */ |
| return 0; |
| } |
| |
| switch (pdu->version) { |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| case SNMP_VERSION_1: |
| #endif |
| #ifndef NETSNMP_DISABLE_SNMPV2C |
| case SNMP_VERSION_2c: |
| #endif |
| case SNMP_VERSION_3: |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_ACM_CHECK_INITIAL, &view_parms); |
| return view_parms.errorcode; |
| } |
| return 1; |
| } |
| |
| /** checks to see if everything within a |
| * given subtree is either: in view, not in view, or possibly both. |
| * If the entire subtree is not-in-view we can use this information to |
| * skip calling the sub-handlers entirely. |
| * @returns 0 if entire subtree is accessible, 5 if not and 7 if |
| * portions are both. 1 on error (illegal pdu version). |
| */ |
| int |
| netsnmp_acm_check_subtree(netsnmp_pdu *pdu, oid *name, size_t namelen) |
| { /* IN - pdu being checked */ |
| struct view_parameters view_parms; |
| view_parms.pdu = pdu; |
| view_parms.name = name; |
| view_parms.namelen = namelen; |
| view_parms.errorcode = 0; |
| view_parms.check_subtree = 1; |
| |
| if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) { |
| /* Enable bypassing of view-based access control */ |
| return 0; |
| } |
| |
| switch (pdu->version) { |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| case SNMP_VERSION_1: |
| #endif |
| #ifndef NETSNMP_DISABLE_SNMPV2C |
| case SNMP_VERSION_2c: |
| #endif |
| case SNMP_VERSION_3: |
| snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
| SNMPD_CALLBACK_ACM_CHECK_SUBTREE, &view_parms); |
| return view_parms.errorcode; |
| } |
| return 1; |
| } |
| |
| #define SUBTREE_DEFAULT_CACHE_SIZE 8 |
| #define SUBTREE_MAX_CACHE_SIZE 32 |
| int lookup_cache_size = 0; /*enabled later after registrations are loaded */ |
| |
| typedef struct lookup_cache_s { |
| netsnmp_subtree *next; |
| netsnmp_subtree *previous; |
| } lookup_cache; |
| |
| typedef struct lookup_cache_context_s { |
| char *context; |
| struct lookup_cache_context_s *next; |
| int thecachecount; |
| int currentpos; |
| lookup_cache cache[SUBTREE_MAX_CACHE_SIZE]; |
| } lookup_cache_context; |
| |
| static lookup_cache_context *thecontextcache = NULL; |
| |
| /** set the lookup cache size for optimized agent registration performance. |
| * @param newsize set to the maximum size of a cache for a given |
| * context. Set to 0 to completely disable caching, or to -1 to set |
| * to the default cache size (8), or to a number of your chosing. The |
| * rough guide is that it should be equal to the maximum number of |
| * simultanious managers you expect to talk to the agent (M) times 80% |
| * (or so, he says randomly) the average number (N) of varbinds you |
| * expect to receive in a given request for a manager. ie, M times N. |
| * Bigger does NOT necessarily mean better. Certainly 16 should be an |
| * upper limit. 32 is the hard coded limit. |
| */ |
| void |
| netsnmp_set_lookup_cache_size(int newsize) { |
| if (newsize < 0) |
| lookup_cache_size = SUBTREE_DEFAULT_CACHE_SIZE; |
| else if (newsize < SUBTREE_MAX_CACHE_SIZE) |
| lookup_cache_size = newsize; |
| else |
| lookup_cache_size = SUBTREE_MAX_CACHE_SIZE; |
| } |
| |
| /** retrieves the current value of the lookup cache size |
| * @return the current lookup cache size |
| */ |
| int |
| netsnmp_get_lookup_cache_size(void) { |
| return lookup_cache_size; |
| } |
| |
| NETSNMP_STATIC_INLINE lookup_cache_context * |
| get_context_lookup_cache(const char *context) { |
| lookup_cache_context *ptr; |
| if (!context) |
| context = ""; |
| |
| for(ptr = thecontextcache; ptr; ptr = ptr->next) { |
| if (strcmp(ptr->context, context) == 0) |
| break; |
| } |
| if (!ptr) { |
| if (netsnmp_subtree_find_first(context)) { |
| ptr = SNMP_MALLOC_TYPEDEF(lookup_cache_context); |
| ptr->next = thecontextcache; |
| ptr->context = strdup(context); |
| thecontextcache = ptr; |
| } else { |
| return NULL; |
| } |
| } |
| return ptr; |
| } |
| |
| NETSNMP_STATIC_INLINE void |
| lookup_cache_add(const char *context, |
| netsnmp_subtree *next, netsnmp_subtree *previous) { |
| lookup_cache_context *cptr; |
| |
| if ((cptr = get_context_lookup_cache(context)) == NULL) |
| return; |
| |
| if (cptr->thecachecount < lookup_cache_size) |
| cptr->thecachecount++; |
| |
| cptr->cache[cptr->currentpos].next = next; |
| cptr->cache[cptr->currentpos].previous = previous; |
| |
| if (++cptr->currentpos >= lookup_cache_size) |
| cptr->currentpos = 0; |
| } |
| |
| NETSNMP_STATIC_INLINE void |
| lookup_cache_replace(lookup_cache *ptr, |
| netsnmp_subtree *next, netsnmp_subtree *previous) { |
| |
| ptr->next = next; |
| ptr->previous = previous; |
| } |
| |
| NETSNMP_STATIC_INLINE lookup_cache * |
| lookup_cache_find(const char *context, oid *name, size_t name_len, |
| int *retcmp) { |
| lookup_cache_context *cptr; |
| lookup_cache *ret = NULL; |
| int cmp; |
| int i; |
| |
| if ((cptr = get_context_lookup_cache(context)) == NULL) |
| return NULL; |
| |
| for(i = 0; i < cptr->thecachecount && i < lookup_cache_size; i++) { |
| if (cptr->cache[i].previous->start_a) |
| cmp = snmp_oid_compare(name, name_len, |
| cptr->cache[i].previous->start_a, |
| cptr->cache[i].previous->start_len); |
| else |
| cmp = 1; |
| if (cmp >= 0) { |
| *retcmp = cmp; |
| ret = &(cptr->cache[i]); |
| } |
| } |
| return ret; |
| } |
| |
| NETSNMP_STATIC_INLINE void |
| invalidate_lookup_cache(const char *context) { |
| lookup_cache_context *cptr; |
| if ((cptr = get_context_lookup_cache(context)) != NULL) { |
| cptr->thecachecount = 0; |
| cptr->currentpos = 0; |
| } |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_find_prev(oid *name, size_t len, netsnmp_subtree *subtree, |
| const char *context_name) |
| { |
| lookup_cache *lookup_cache = NULL; |
| netsnmp_subtree *myptr = NULL, *previous = NULL; |
| int cmp = 1; |
| size_t ll_off = 0; |
| |
| if (subtree) { |
| myptr = subtree; |
| } else { |
| /* look through everything */ |
| if (lookup_cache_size) { |
| lookup_cache = lookup_cache_find(context_name, name, len, &cmp); |
| if (lookup_cache) { |
| myptr = lookup_cache->next; |
| previous = lookup_cache->previous; |
| } |
| if (!myptr) |
| myptr = netsnmp_subtree_find_first(context_name); |
| } else { |
| myptr = netsnmp_subtree_find_first(context_name); |
| } |
| } |
| |
| /* |
| * this optimization causes a segfault on sf cf alpha-linux1. |
| * ifdef out until someone figures out why and fixes it. xxx-rks 20051117 |
| */ |
| #ifndef __alpha |
| #define WTEST_OPTIMIZATION 1 |
| #endif |
| #ifdef WTEST_OPTIMIZATION |
| DEBUGMSGTL(("wtest","oid in: ")); |
| DEBUGMSGOID(("wtest", name, len)); |
| DEBUGMSG(("wtest","\n")); |
| #endif |
| for (; myptr != NULL; previous = myptr, myptr = myptr->next) { |
| #ifdef WTEST_OPTIMIZATION |
| /* Compare the incoming oid with the linked list. If we have |
| results of previous compares, its faster to make sure the |
| length we differed in the last check is greater than the |
| length between this pointer and the last then we don't need |
| to actually perform a comparison */ |
| DEBUGMSGTL(("wtest","oid cmp: ")); |
| DEBUGMSGOID(("wtest", myptr->start_a, myptr->start_len)); |
| DEBUGMSG(("wtest"," --- off = %d, in off = %d test = %d\n", |
| myptr->oid_off, ll_off, |
| !(ll_off && myptr->oid_off && |
| myptr->oid_off > ll_off))); |
| if (!(ll_off && myptr->oid_off && myptr->oid_off > ll_off) && |
| netsnmp_oid_compare_ll(name, len, |
| myptr->start_a, myptr->start_len, |
| &ll_off) < 0) { |
| #else |
| if (snmp_oid_compare(name, len, myptr->start_a, myptr->start_len) < 0) { |
| #endif |
| if (lookup_cache_size && previous && cmp) { |
| if (lookup_cache) { |
| lookup_cache_replace(lookup_cache, myptr, previous); |
| } else { |
| lookup_cache_add(context_name, myptr, previous); |
| } |
| } |
| return previous; |
| } |
| } |
| return previous; |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_find_next(oid *name, size_t len, |
| netsnmp_subtree *subtree, const char *context_name) |
| { |
| netsnmp_subtree *myptr = NULL; |
| |
| myptr = netsnmp_subtree_find_prev(name, len, subtree, context_name); |
| |
| if (myptr != NULL) { |
| myptr = myptr->next; |
| while (myptr != NULL && (myptr->variables == NULL || |
| myptr->variables_len == 0)) { |
| myptr = myptr->next; |
| } |
| return myptr; |
| } else if (subtree != NULL && snmp_oid_compare(name, len, |
| subtree->start_a, subtree->start_len) < 0) { |
| return subtree; |
| } else { |
| return NULL; |
| } |
| } |
| |
| netsnmp_subtree * |
| netsnmp_subtree_find(oid *name, size_t len, netsnmp_subtree *subtree, |
| const char *context_name) |
| { |
| netsnmp_subtree *myptr; |
| |
| myptr = netsnmp_subtree_find_prev(name, len, subtree, context_name); |
| if (myptr && myptr->end_a && |
| snmp_oid_compare(name, len, myptr->end_a, myptr->end_len)<0) { |
| return myptr; |
| } |
| |
| return NULL; |
| } |
| |
| netsnmp_session * |
| get_session_for_oid(oid *name, size_t len, const char *context_name) |
| { |
| netsnmp_subtree *myptr; |
| |
| myptr = netsnmp_subtree_find_prev(name, len, |
| netsnmp_subtree_find_first(context_name), |
| context_name); |
| |
| while (myptr && myptr->variables == NULL) { |
| myptr = myptr->next; |
| } |
| |
| if (myptr == NULL) { |
| return NULL; |
| } else { |
| return myptr->session; |
| } |
| } |
| |
| void |
| setup_tree(void) |
| { |
| oid ccitt[1] = { 0 }; |
| oid iso[1] = { 1 }; |
| oid joint_ccitt_iso[1] = { 2 }; |
| |
| #ifdef USING_AGENTX_SUBAGENT_MODULE |
| int role = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
| NETSNMP_DS_AGENT_ROLE); |
| |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, |
| MASTER_AGENT); |
| #endif |
| |
| /* |
| * we need to have the oid's in the heap, that we can *free* it for every case, |
| * thats the purpose of the duplicate_objid's |
| */ |
| netsnmp_register_null(snmp_duplicate_objid(ccitt, 1), 1); |
| netsnmp_register_null(snmp_duplicate_objid(iso, 1), 1); |
| netsnmp_register_null(snmp_duplicate_objid(joint_ccitt_iso, 1), 1); |
| |
| #ifdef USING_AGENTX_SUBAGENT_MODULE |
| netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, |
| role); |
| #endif |
| } |
| |
| int |
| remove_tree_entry (oid *name, size_t len) { |
| |
| netsnmp_subtree *sub = NULL; |
| |
| if ((sub = netsnmp_subtree_find(name, len, NULL, "")) == NULL) { |
| return MIB_NO_SUCH_REGISTRATION; |
| } |
| |
| return unregister_mib_context(name, len, sub->priority, |
| sub->range_subid, sub->range_ubound, ""); |
| |
| } |
| |
| |
| void |
| shutdown_tree(void) { |
| oid ccitt[1] = { 0 }; |
| oid iso[1] = { 1 }; |
| oid joint_ccitt_iso[1] = { 2 }; |
| |
| DEBUGMSGTL(("agent_registry", "shut down tree\n")); |
| |
| remove_tree_entry(joint_ccitt_iso, 1); |
| remove_tree_entry(iso, 1); |
| remove_tree_entry(ccitt, 1); |
| |
| } |
| |
| void |
| clear_subtree (netsnmp_subtree *sub) { |
| |
| netsnmp_subtree *c; |
| |
| if (sub == NULL) |
| return; |
| |
| for(c = sub; c;) { |
| sub = c; |
| c = c->children; |
| netsnmp_subtree_free(sub); |
| } |
| |
| } |
| |
| void |
| clear_lookup_cache(void) { |
| |
| lookup_cache_context *ptr = NULL, *next = NULL; |
| |
| ptr = thecontextcache; |
| while (ptr) { |
| next = ptr->next; |
| SNMP_FREE(ptr->context); |
| SNMP_FREE(ptr); |
| ptr = next; |
| } |
| thecontextcache = NULL; /* !!! */ |
| } |
| |
| void |
| clear_context(void) { |
| |
| subtree_context_cache *ptr = NULL, *next = NULL; |
| netsnmp_subtree *t, *u; |
| |
| DEBUGMSGTL(("agent_registry", "clear context\n")); |
| |
| ptr = get_top_context_cache(); |
| while (ptr) { |
| next = ptr->next; |
| |
| for (t = ptr->first_subtree; t; t = u) { |
| u = t->next; |
| clear_subtree(t); |
| } |
| |
| SNMP_FREE(ptr->context_name); |
| SNMP_FREE(ptr); |
| |
| ptr = next; |
| } |
| context_subtrees = NULL; /* !!! */ |
| clear_lookup_cache(); |
| } |
| |
| extern void dump_idx_registry(void); |
| void |
| dump_registry(void) |
| { |
| struct variable *vp = NULL; |
| netsnmp_subtree *myptr, *myptr2; |
| u_char *s = NULL, *e = NULL, *v = NULL; |
| size_t sl = 256, el = 256, vl = 256, sl_o = 0, el_o = 0, vl_o = 0; |
| int i = 0; |
| |
| if ((s = (u_char *) calloc(sl, 1)) != NULL && |
| (e = (u_char *) calloc(sl, 1)) != NULL && |
| (v = (u_char *) calloc(sl, 1)) != NULL) { |
| |
| subtree_context_cache *ptr; |
| for (ptr = context_subtrees; ptr; ptr = ptr->next) { |
| printf("Subtrees for Context: %s\n", ptr->context_name); |
| for (myptr = ptr->first_subtree; myptr != NULL; |
| myptr = myptr->next) { |
| sl_o = el_o = vl_o = 0; |
| |
| if (!sprint_realloc_objid(&s, &sl, &sl_o, 1, |
| myptr->start_a, |
| myptr->start_len)) { |
| break; |
| } |
| if (!sprint_realloc_objid(&e, &el, &el_o, 1, |
| myptr->end_a, |
| myptr->end_len)) { |
| break; |
| } |
| |
| if (myptr->variables) { |
| printf("%02x ( %s - %s ) [", myptr->flags, s, e); |
| for (i = 0, vp = myptr->variables; |
| i < myptr->variables_len; i++) { |
| vl_o = 0; |
| if (!sprint_realloc_objid |
| (&v, &vl, &vl_o, 1, vp->name, vp->namelen)) { |
| break; |
| } |
| printf("%s, ", v); |
| vp = (struct variable *) ((char *) vp + |
| myptr->variables_width); |
| } |
| printf("]\n"); |
| } else { |
| printf("%02x %s - %s \n", myptr->flags, s, e); |
| } |
| for (myptr2 = myptr; myptr2 != NULL; |
| myptr2 = myptr2->children) { |
| if (myptr2->label_a && myptr2->label_a[0]) { |
| if (strcmp(myptr2->label_a, "old_api") == 0) { |
| struct variable *vp = |
| myptr2->reginfo->handler->myvoid; |
| |
| if (!sprint_realloc_objid(&s, &sl, &sl_o, 1, |
| vp->name, vp->namelen)) { |
| continue; |
| } |
| printf("\t%s[%s] %p var %s\n", myptr2->label_a, |
| myptr2->reginfo->handlerName ? |
| myptr2->reginfo->handlerName : "no-name", |
| myptr2->reginfo, s); |
| } else { |
| printf("\t%s %s %p\n", myptr2->label_a, |
| myptr2->reginfo->handlerName ? |
| myptr2->reginfo->handlerName : "no-handler-name", |
| myptr2->reginfo); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (s != NULL) { |
| SNMP_FREE(s); |
| } |
| if (e != NULL) { |
| SNMP_FREE(e); |
| } |
| if (v != NULL) { |
| SNMP_FREE(v); |
| } |
| |
| dump_idx_registry(); |
| } |
| |
| int external_signal_scheduled[NUM_EXTERNAL_SIGS]; |
| void (*external_signal_handler[NUM_EXTERNAL_SIGS]) (int); |
| |
| #ifndef WIN32 |
| |
| /* |
| * TODO: add agent_SIGXXX_handler functions and `case SIGXXX: ...' lines |
| * below for every single that might be handled by register_signal(). |
| */ |
| |
| RETSIGTYPE |
| agent_SIGCHLD_handler(int sig) |
| { |
| external_signal_scheduled[SIGCHLD]++; |
| #ifndef HAVE_SIGACTION |
| /* |
| * signal() sucks. It *might* have SysV semantics, which means that |
| * * a signal handler is reset once it gets called. Ensure that it |
| * * remains active. |
| */ |
| signal(SIGCHLD, agent_SIGCHLD_handler); |
| #endif |
| } |
| |
| int |
| register_signal(int sig, void (*func) (int)) |
| { |
| |
| switch (sig) { |
| #if defined(SIGCHLD) |
| case SIGCHLD: |
| #ifdef HAVE_SIGACTION |
| { |
| static struct sigaction act; |
| act.sa_handler = agent_SIGCHLD_handler; |
| sigemptyset(&act.sa_mask); |
| act.sa_flags = 0; |
| sigaction(SIGCHLD, &act, NULL); |
| } |
| #else |
| signal(SIGCHLD, agent_SIGCHLD_handler); |
| #endif |
| break; |
| #endif |
| default: |
| snmp_log(LOG_CRIT, |
| "register_signal: signal %d cannot be handled\n", sig); |
| return SIG_REGISTRATION_FAILED; |
| } |
| |
| external_signal_handler[sig] = func; |
| external_signal_scheduled[sig] = 0; |
| |
| DEBUGMSGTL(("register_signal", "registered signal %d\n", sig)); |
| return SIG_REGISTERED_OK; |
| } |
| |
| int |
| unregister_signal(int sig) |
| { |
| signal(sig, SIG_DFL); |
| DEBUGMSGTL(("unregister_signal", "unregistered signal %d\n", sig)); |
| return SIG_UNREGISTERED_OK; |
| } |
| |
| #endif /* !WIN32 */ |
| |
| /** @} */ |