2441 lines
77 KiB
C
2441 lines
77 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Portions of this file are copyrighted by:
|
|
* Copyright (c) 2016 VMware, Inc. All rights reserved.
|
|
* Use is subject to license terms specified in the COPYING file
|
|
* distributed with the Net-SNMP package.
|
|
*/
|
|
/** @defgroup agent_registry Registry of MIB subtrees, modules, sessions, etc
|
|
* Maintain a registry of MIB subtrees, together with related information
|
|
* regarding MIB modules, sessions, etc
|
|
* @ingroup agent
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
#define IN_SNMP_VARS_C
|
|
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-features.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 TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# 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/library/snmp_assert.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
#include <net-snmp/agent/agent_callbacks.h>
|
|
|
|
#include "snmpd.h"
|
|
#include "agent_global_vars.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_index.h>
|
|
#include <net-snmp/agent/agent_registry.h>
|
|
|
|
#ifdef USING_AGENTX_SUBAGENT_MODULE
|
|
#include "agentx/subagent.h"
|
|
#include "agentx/client.h"
|
|
#endif
|
|
|
|
netsnmp_feature_child_of(agent_registry_all, libnetsnmpagent)
|
|
|
|
netsnmp_feature_child_of(unregister_mib_table_row, agent_registry_all)
|
|
|
|
/** @defgroup agent_lookup_cache Lookup cache, storing the registered OIDs.
|
|
* Maintain the cache used for locating sub-trees and OIDs.
|
|
* @ingroup agent_registry
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/** Lookup cache - default size.*/
|
|
#define SUBTREE_DEFAULT_CACHE_SIZE 8
|
|
/** Lookup cache - max acceptable size.*/
|
|
#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.
|
|
* Note that it is only used by master agent - sub-agent doesn't need the cache.
|
|
* The rough guide is that the cache size should be equal to the maximum
|
|
* number of simultaneous 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.
|
|
*
|
|
* @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
|
|
*/
|
|
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
|
|
* Should be called from master agent only - sub-agent doesn't need the cache.
|
|
*
|
|
* @return the current lookup cache size
|
|
*/
|
|
int
|
|
netsnmp_get_lookup_cache_size(void) {
|
|
return lookup_cache_size;
|
|
}
|
|
|
|
/** Returns lookup cache entry for the context of given name.
|
|
*
|
|
* @param context Name of the context. Name is case sensitive.
|
|
*
|
|
* @return the lookup cache context
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** Adds an entry to the Lookup Cache under specified context name.
|
|
*
|
|
* @param context Name of the context. Name is case sensitive.
|
|
*
|
|
* @param next Next subtree item.
|
|
*
|
|
* @param previous Previous subtree item.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** @private
|
|
* Replaces next and previous pointer in given Lookup Cache.
|
|
*
|
|
* @param ptr Lookup Cache pointer.
|
|
*
|
|
* @param next Next subtree item.
|
|
*
|
|
* @param previous Previous subtree item.
|
|
*/
|
|
NETSNMP_STATIC_INLINE void
|
|
lookup_cache_replace(lookup_cache *ptr,
|
|
netsnmp_subtree *next, netsnmp_subtree *previous) {
|
|
|
|
ptr->next = next;
|
|
ptr->previous = previous;
|
|
}
|
|
|
|
/** Finds an entry in the Lookup Cache.
|
|
*
|
|
* @param context Case sensitive name of the context.
|
|
*
|
|
* @param name The OID we're searching for.
|
|
*
|
|
* @param name_len Number of sub-ids (single integers) in the OID.
|
|
*
|
|
* @param retcmp Value set to snmp_oid_compare() call result.
|
|
* The value, if set, is always nonnegative.
|
|
*
|
|
* @return gives Lookup Cache entry, or NULL if not found.
|
|
*
|
|
* @see snmp_oid_compare()
|
|
*/
|
|
NETSNMP_STATIC_INLINE lookup_cache *
|
|
lookup_cache_find(const char *context, const 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;
|
|
}
|
|
|
|
/** @private
|
|
* Clears cache count and position in Lookup Cache.
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
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; /* !!! */
|
|
}
|
|
|
|
/** @} */
|
|
/* End of Lookup cache code */
|
|
|
|
/** @defgroup agent_context_cache Context cache, storing the OIDs under their contexts.
|
|
* Maintain the cache used for locating sub-trees registered under different contexts.
|
|
* @ingroup agent_registry
|
|
*
|
|
* @{
|
|
*/
|
|
subtree_context_cache *context_subtrees = NULL;
|
|
|
|
/** Returns the top element of context subtrees cache.
|
|
* Use it if you wish to sweep through the cache elements.
|
|
* Note that the return may be NULL (cache may be empty).
|
|
*
|
|
* @return pointer to topmost context subtree cache element.
|
|
*/
|
|
subtree_context_cache *
|
|
get_top_context_cache(void)
|
|
{
|
|
return context_subtrees;
|
|
}
|
|
|
|
/** Finds the first subtree registered under given context.
|
|
*
|
|
* @param context_name Text name of the context we're searching for.
|
|
*
|
|
* @return pointer to the first subtree element, or NULL if not found.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** Adds the subtree to Context Cache under given context name.
|
|
*
|
|
* @param context_name Text name of the context we're adding.
|
|
*
|
|
* @param new_tree The subtree to be added.
|
|
*
|
|
* @return copy of the new_tree pointer, or NULL if cannot add.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
void
|
|
netsnmp_remove_subtree(netsnmp_subtree *tree)
|
|
{
|
|
subtree_context_cache *ptr;
|
|
|
|
if (!tree->prev) {
|
|
for (ptr = context_subtrees; ptr; ptr = ptr->next)
|
|
if (ptr->first_subtree == tree)
|
|
break;
|
|
netsnmp_assert(ptr);
|
|
if (ptr)
|
|
ptr->first_subtree = tree->next;
|
|
} else
|
|
tree->prev->next = tree->next;
|
|
|
|
if (tree->next)
|
|
tree->next->prev = tree->prev;
|
|
}
|
|
|
|
/** Replaces first subtree registered under given context name.
|
|
* Overwrites a subtree pointer in Context Cache for the context name.
|
|
* The previous subtree pointer is lost. If there's no subtree
|
|
* under the supplied name, then a new cache item is created.
|
|
*
|
|
* @param new_tree The new subtree to be set.
|
|
*
|
|
* @param context_name Text name of the context we're replacing.
|
|
* It is case sensitive.
|
|
*
|
|
* @return copy of the new_tree pointer, or NULL on error.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
|
|
void clear_subtree (netsnmp_subtree *sub);
|
|
|
|
/** Completely clears both the Context cache and the Lookup cache.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
free(NETSNMP_REMOVE_CONST(char*, ptr->context_name));
|
|
SNMP_FREE(ptr);
|
|
|
|
ptr = next;
|
|
}
|
|
context_subtrees = NULL; /* !!! */
|
|
clear_lookup_cache();
|
|
}
|
|
|
|
/** @} */
|
|
/* End of Context cache code */
|
|
|
|
/** @defgroup agent_mib_subtree Maintaining MIB subtrees.
|
|
* Maintaining MIB nodes and subtrees.
|
|
* @ingroup agent_registry
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
static void register_mib_detach_node(netsnmp_subtree *s);
|
|
|
|
/** Frees single subtree item.
|
|
* Deallocated memory for given netsnmp_subtree item, including
|
|
* Handle Registration structure stored inside this item.
|
|
* After calling this function, the pointer is invalid
|
|
* and should be set to NULL.
|
|
*
|
|
* @param a The subtree item to dispose.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/** Creates deep copy of a subtree item.
|
|
* Duplicates all properties stored in the structure, including
|
|
* Handle Registration structure stored inside the item.
|
|
*
|
|
* @param a The subtree item to copy.
|
|
*
|
|
* @return deep copy of the subtree item, or NULL on error.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** @private
|
|
* Replaces next subtree pointer in given subtree.
|
|
*/
|
|
NETSNMP_STATIC_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);
|
|
}
|
|
|
|
/** @private
|
|
* Replaces previous subtree pointer in given subtree.
|
|
*/
|
|
NETSNMP_STATIC_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);
|
|
}
|
|
|
|
netsnmp_feature_child_of(netsnmp_subtree_compare,netsnmp_unused)
|
|
#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_SUBTREE_COMPARE
|
|
/** Compares OIDs of given subtrees.
|
|
*
|
|
* @param ap,bp Pointers to the subtrees to be compared.
|
|
*
|
|
* @return OIDs lexicographical comparison result.
|
|
*
|
|
* @see snmp_oid_compare()
|
|
*/
|
|
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);
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_SUBTREE_COMPARE */
|
|
|
|
/** Joins the given subtree with the current tree.
|
|
* Trees are joined and the one supplied as parameter is freed.
|
|
*
|
|
* @param root The subtree to be merged with current subtree.
|
|
* Do not use the pointer after joining - it may be invalid.
|
|
*/
|
|
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.
|
|
* Subtrees of the given OID and separated and formed into the
|
|
* returned subtree.
|
|
*
|
|
* @param current The element at which splitting is started.
|
|
*
|
|
* @param name The OID we'd like to split.
|
|
*
|
|
* @param name_len Length of the OID.
|
|
*
|
|
* @return head of 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/** Loads the subtree under given context name.
|
|
*
|
|
* @param new_sub The subtree to be loaded into current subtree.
|
|
*
|
|
* @param context_name Text name of the context we're searching for.
|
|
*
|
|
* @return gives MIB_REGISTERED_OK on success, error code otherwise.
|
|
*/
|
|
int
|
|
netsnmp_subtree_load(netsnmp_subtree *new_sub, const char *context_name)
|
|
{
|
|
netsnmp_subtree *tree1, *tree2;
|
|
netsnmp_subtree *prev, *next;
|
|
|
|
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) {
|
|
/*netsnmp_subtree *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 0
|
|
/* The code below cannot be reached which is why it has been
|
|
surrounded with #if 0 / #endif. */
|
|
/* 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);
|
|
}
|
|
#endif
|
|
}
|
|
} 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. */
|
|
|
|
switch (snmp_oid_compare(new_sub->end_a, new_sub->end_len,
|
|
tree1->end_a, tree1->end_len)) {
|
|
|
|
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 */
|
|
size_t out_len = 0;
|
|
size_t buf_len = 0;
|
|
char *buf = NULL;
|
|
int buf_overflow = 0;
|
|
|
|
netsnmp_sprint_realloc_objid((u_char **) &buf, &buf_len, &out_len,
|
|
1, &buf_overflow,
|
|
new_sub->start_a,
|
|
new_sub->start_len);
|
|
snmp_log(LOG_ERR,
|
|
"duplicate registration: MIB modules %s and %s (oid %s%s).\n",
|
|
next->label_a, new_sub->label_a,
|
|
buf ? buf : "",
|
|
buf_overflow ? " [TRUNCATED]" : "");
|
|
free(buf);
|
|
}
|
|
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. */
|
|
{
|
|
netsnmp_subtree *new2 =
|
|
netsnmp_subtree_split(new_sub, tree1->end_a,tree1->end_len);
|
|
int res = netsnmp_subtree_load(new_sub, context_name);
|
|
if (res != MIB_REGISTERED_OK) {
|
|
netsnmp_remove_subtree(new2);
|
|
netsnmp_subtree_free(new2);
|
|
return res;
|
|
}
|
|
return netsnmp_subtree_load(new2, context_name);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Free the given subtree and all its children.
|
|
*
|
|
* @param sub Subtree branch to be cleared and freed.
|
|
* After the call, this pointer is invalid
|
|
* and should be set to NULL.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
netsnmp_subtree *
|
|
netsnmp_subtree_find_prev(const 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 = %lu, in off = %lu test = %d\n",
|
|
(unsigned long)myptr->oid_off, (unsigned long)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(const 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(const 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;
|
|
}
|
|
|
|
/** @} */
|
|
/* End of Subtrees maintaining code */
|
|
|
|
/** @defgroup agent_mib_registering Registering and unregistering MIB subtrees.
|
|
* Adding and removing MIB nodes to the database under their contexts.
|
|
* @ingroup agent_registry
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
|
|
/** Registers a MIB handler.
|
|
*
|
|
* @param moduleName
|
|
* @param var
|
|
* @param varsize
|
|
* @param numvars
|
|
* @param mibloc
|
|
* @param mibloclen
|
|
* @param priority
|
|
* @param range_subid
|
|
* @param range_ubound
|
|
* @param ss
|
|
* @param context
|
|
* @param timeout
|
|
* @param flags
|
|
* @param reginfo Registration handler structure.
|
|
* In a case of failure, it will be freed.
|
|
* @param perform_callback
|
|
*
|
|
* @return gives MIB_REGISTERED_OK or MIB_* error code.
|
|
*
|
|
* @see netsnmp_register_handler()
|
|
* @see register_agentx_list()
|
|
* @see netsnmp_handler_registration_free()
|
|
*/
|
|
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;
|
|
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) {
|
|
int i;
|
|
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_remove_subtree(sub2);
|
|
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) {
|
|
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;
|
|
reg_parms.session = ss;
|
|
reg_parms.reginfo = reginfo;
|
|
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;
|
|
}
|
|
|
|
/** @private
|
|
* 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;
|
|
reg_parms.session = s->session;
|
|
reg_parms.reginfo = s->reginfo;
|
|
/* XXX: missing in subtree: reg_parms.contextName = s->context; */
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @private
|
|
* Mark a node as detached.
|
|
*
|
|
* @param s The note to be marked
|
|
*/
|
|
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 the subagent is being shut down.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Register a new module into the MIB database, with all possible custom options
|
|
*
|
|
* @param moduleName Text name of the module.
|
|
* The given name will be used to identify the module
|
|
* inside the agent.
|
|
*
|
|
* @param var Array of variables to be registered in the module.
|
|
*
|
|
* @param varsize Size of a single variable in var array.
|
|
* The size is normally equal to sizeof(struct variable),
|
|
* but if we wish to use shorter (or longer) OIDs, then we
|
|
* could use different variant of the variable structure.
|
|
*
|
|
* @param numvars Number of variables in the var array.
|
|
* This is how many variables the function will try to register.
|
|
*
|
|
* @param mibloc Base OID of the module.
|
|
* All OIDs in var array should be sub-oids of the base OID.
|
|
*
|
|
* @param mibloclen Length of the base OID.
|
|
* Number of integers making up the base OID.
|
|
*
|
|
* @param priority Registration priority.
|
|
* Used to achieve a desired configuration when different
|
|
* sessions register identical or overlapping regions.
|
|
* Primarily used with AgentX subagent registrations.
|
|
*
|
|
* @param range_subid If non-zero, the module is registered against a range
|
|
* of OIDs, with this parameter identifying the relevant
|
|
* subidentifier - see RFC 2741 for details.
|
|
* Typically used to register a single row of a table.
|
|
* If zero, then register the module against the full OID subtree.
|
|
*
|
|
* @param range_ubound The end of the range being registered (see RFC 2741)
|
|
* If range_subid is zero, then this parameter is ignored.
|
|
*
|
|
* @param ss
|
|
* @param context
|
|
* @param timeout
|
|
* @param flags
|
|
*
|
|
* @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
|
|
*
|
|
* @see register_mib()
|
|
* @see register_mib_priority()
|
|
* @see register_mib_range()
|
|
* @see unregister_mib()
|
|
*/
|
|
int
|
|
register_mib_context(const char *moduleName,
|
|
const struct variable *var,
|
|
size_t varsize,
|
|
size_t numvars,
|
|
const 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);
|
|
}
|
|
|
|
/** Register a new module into the MIB database, as being responsible
|
|
* for a range of OIDs (typically a single row of a table).
|
|
*
|
|
* @param moduleName Text name of the module.
|
|
* The given name will be used to identify the module
|
|
* inside the agent.
|
|
*
|
|
* @param var Array of variables to be registered in the module.
|
|
*
|
|
* @param varsize Size of a single variable in var array.
|
|
* The size is normally equal to sizeof(struct variable),
|
|
* but if we wish to use shorter (or longer) OIDs, then we
|
|
* could use different variant of the variable structure.
|
|
*
|
|
* @param numvars Number of variables in the var array.
|
|
* This is how many variables the function will try to register.
|
|
*
|
|
* @param mibloc Base OID of the module.
|
|
* All OIDs in var array should be sub-oids of the base OID.
|
|
*
|
|
* @param mibloclen Length of the base OID.
|
|
* Number of integers making up the base OID.
|
|
*
|
|
* @param priority Registration priority.
|
|
* Used to achieve a desired configuration when different
|
|
* sessions register identical or overlapping regions.
|
|
* Primarily used with AgentX subagent registrations.
|
|
*
|
|
* @param range_subid If non-zero, the module is registered against a range
|
|
* of OIDs, with this parameter identifying the relevant
|
|
* subidentifier - see RFC 2741 for details.
|
|
* Typically used to register a single row of a table.
|
|
* If zero, then register the module against the full OID subtree.
|
|
*
|
|
* @param range_ubound The end of the range being registered (see RFC 2741)
|
|
* If range_subid is zero, then this parameter is ignored.
|
|
*
|
|
* @param ss
|
|
*
|
|
* @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
|
|
*
|
|
* @see register_mib()
|
|
* @see register_mib_priority()
|
|
* @see register_mib_context()
|
|
* @see unregister_mib()
|
|
*/
|
|
int
|
|
register_mib_range(const char *moduleName,
|
|
const struct variable *var,
|
|
size_t varsize,
|
|
size_t numvars,
|
|
const 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);
|
|
}
|
|
|
|
/** Register a new module into the MIB database, with a non-default priority
|
|
*
|
|
* @param moduleName Text name of the module.
|
|
* The given name will be used to identify the module
|
|
* inside the agent.
|
|
*
|
|
* @param var Array of variables to be registered in the module.
|
|
*
|
|
* @param varsize Size of a single variable in var array.
|
|
* The size is normally equal to sizeof(struct variable),
|
|
* but if we wish to use shorter (or longer) OIDs, then we
|
|
* could use different variant of the variable structure.
|
|
*
|
|
* @param numvars Number of variables in the var array.
|
|
* This is how many variables the function will try to register.
|
|
*
|
|
* @param mibloc Base OID of the module.
|
|
* All OIDs in var array should be sub-oids of the base OID.
|
|
*
|
|
* @param mibloclen Length of the base OID.
|
|
* Number of integers making up the base OID.
|
|
*
|
|
* @param priority Registration priority.
|
|
* Used to achieve a desired configuration when different
|
|
* sessions register identical or overlapping regions.
|
|
* Primarily used with AgentX subagent registrations.
|
|
*
|
|
* @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
|
|
*
|
|
* @see register_mib()
|
|
* @see register_mib_range()
|
|
* @see register_mib_context()
|
|
* @see unregister_mib()
|
|
*/
|
|
int
|
|
register_mib_priority(const char *moduleName,
|
|
const struct variable *var,
|
|
size_t varsize,
|
|
size_t numvars,
|
|
const oid * mibloc, size_t mibloclen, int priority)
|
|
{
|
|
return register_mib_range(moduleName, var, varsize, numvars,
|
|
mibloc, mibloclen, priority, 0, 0, NULL);
|
|
}
|
|
|
|
/** Register a new module into the MIB database, using default priority and context
|
|
*
|
|
* @param moduleName Text name of the module.
|
|
* The given name will be used to identify the module
|
|
* inside the agent.
|
|
*
|
|
* @param var Array of variables to be registered in the module.
|
|
*
|
|
* @param varsize Size of a single variable in var array.
|
|
* The size is normally equal to sizeof(struct variable),
|
|
* but if we wish to use shorter (or longer) OIDs, then we
|
|
* could use different variant of the variable structure.
|
|
*
|
|
* @param numvars Number of variables in the var array.
|
|
* This is how many variables the function will try to register.
|
|
*
|
|
* @param mibloc Base OID of the module.
|
|
* All OIDs in var array should be sub-oids of the base OID.
|
|
*
|
|
* @param mibloclen Length of the base OID.
|
|
* Number of integers making up the base OID.
|
|
*
|
|
* @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
|
|
*
|
|
* @see register_mib_priority()
|
|
* @see register_mib_range()
|
|
* @see register_mib_context()
|
|
* @see unregister_mib()
|
|
*/
|
|
int
|
|
register_mib(const char *moduleName,
|
|
const struct variable *var,
|
|
size_t varsize,
|
|
size_t numvars, const oid * mibloc, size_t mibloclen)
|
|
{
|
|
return register_mib_priority(moduleName, var, varsize, numvars,
|
|
mibloc, mibloclen, DEFAULT_MIB_PRIORITY);
|
|
}
|
|
|
|
/** @private
|
|
* Unloads a subtree from MIB tree.
|
|
*
|
|
* @param sub The sub-tree which is being removed.
|
|
*
|
|
* @param prev Previous entry, before the unloaded one.
|
|
*
|
|
* @param context Name of the context which is being removed.
|
|
*
|
|
* @see unregister_mib_context()
|
|
*/
|
|
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 a module registered against a given OID (or range) in a specified context.
|
|
* Typically used when a module has multiple contexts defined.
|
|
* The parameters priority, range_subid, range_ubound and context
|
|
* should match those used to register the module originally.
|
|
*
|
|
* @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 gives MIB_UNREGISTERED_OK or MIB_* error code.
|
|
*
|
|
* @see unregister_mib()
|
|
* @see unregister_mib_priority()
|
|
* @see unregister_mib_range()
|
|
*/
|
|
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) && ((size_t)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;
|
|
}
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_UNREGISTER_MIB_TABLE_ROW
|
|
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;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_UNREGISTER_MIB_TABLE_ROW */
|
|
|
|
/**
|
|
* Unregisters a module registered against a given OID (or range) in the default context.
|
|
* Typically used when a module has multiple contexts defined.
|
|
* The parameters priority, range_subid, and range_ubound should
|
|
* match those used to register the module originally.
|
|
*
|
|
* @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.
|
|
*
|
|
* @return gives MIB_UNREGISTERED_OK or MIB_* error code.
|
|
*
|
|
* @see unregister_mib()
|
|
* @see unregister_mib_priority()
|
|
* @see unregister_mib_context()
|
|
*/
|
|
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, "");
|
|
}
|
|
|
|
/**
|
|
* Unregisters a module registered against a given OID at the specified priority.
|
|
* The priority parameter should match that used to register the module originally.
|
|
*
|
|
* @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.
|
|
*
|
|
* @return gives MIB_UNREGISTERED_OK or MIB_* error code.
|
|
*
|
|
* @see unregister_mib()
|
|
* @see unregister_mib_range()
|
|
* @see unregister_mib_context()
|
|
*/
|
|
int
|
|
unregister_mib_priority(oid * name, size_t len, int priority)
|
|
{
|
|
return unregister_mib_range(name, len, priority, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* Unregisters a module registered against a given OID at the default priority.
|
|
*
|
|
* @param name the specific OID to unregister if it conatins the associated
|
|
* context.
|
|
*
|
|
* @param len the length of the OID, use OID_LENGTH macro.
|
|
*
|
|
* @return gives MIB_UNREGISTERED_OK or MIB_* error code.
|
|
*
|
|
* @see unregister_mib_priority()
|
|
* @see unregister_mib_context()
|
|
* @see unregister_mib_range()
|
|
* @see unregister_agentx_list()
|
|
*/
|
|
int
|
|
unregister_mib(oid * name, size_t len)
|
|
{
|
|
return unregister_mib_priority(name, len, DEFAULT_MIB_PRIORITY);
|
|
}
|
|
|
|
/** Unregisters subtree of OIDs bounded to given session.
|
|
*
|
|
* @param ss Session which OIDs will be removed from tree.
|
|
*
|
|
* @see unregister_mib()
|
|
* @see unregister_agentx_list()
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/** Determines if given PDU is allowed to see (or update) a given OID.
|
|
*
|
|
* @param name The OID to check access for.
|
|
* On return, this parameter holds the OID actually matched
|
|
*
|
|
* @param namelen Number of sub-identifiers in the OID.
|
|
* On return, this parameter holds the length of the matched OID
|
|
*
|
|
* @param pdu PDU requesting access to the OID.
|
|
*
|
|
* @param type ANS.1 type of the value at given OID.
|
|
* (Used for catching SNMPv1 requests for SMIv2-only objects)
|
|
*
|
|
* @return gives VACM_SUCCESS if the OID is in the PDU, otherwise error code.
|
|
*/
|
|
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:
|
|
NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
|
|
snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
|
|
SNMPD_CALLBACK_ACM_CHECK, &view_parms);
|
|
return view_parms.errorcode;
|
|
}
|
|
unsupported_version:
|
|
return VACM_NOSECNAME;
|
|
}
|
|
|
|
/** Determines if the given PDU request could potentially succeed.
|
|
* (Preliminary, OID-independent validation)
|
|
*
|
|
* @param pdu PDU requesting access
|
|
*
|
|
* @return gives VACM_SUCCESS if the entire MIB tree is accessible
|
|
* VACM_NOTINVIEW if the entire MIB tree is inaccessible
|
|
* VACM_SUBTREE_UNKNOWN if some portions are accessible
|
|
* other codes may returned on error
|
|
*/
|
|
int
|
|
check_access(netsnmp_pdu *pdu)
|
|
{ /* IN - pdu being checked */
|
|
struct view_parameters view_parms;
|
|
view_parms.pdu = pdu;
|
|
view_parms.name = NULL;
|
|
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:
|
|
NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
|
|
snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
|
|
SNMPD_CALLBACK_ACM_CHECK_INITIAL, &view_parms);
|
|
return view_parms.errorcode;
|
|
}
|
|
unsupported_version:
|
|
return 1;
|
|
}
|
|
|
|
/** Determines if the given PDU request could potentially access
|
|
* the specified MIB subtree
|
|
*
|
|
* @param pdu PDU requesting access
|
|
*
|
|
* @param name The OID to check access for.
|
|
*
|
|
* @param namelen Number of sub-identifiers in the OID.
|
|
*
|
|
* @return gives VACM_SUCCESS if the entire MIB tree is accessible
|
|
* VACM_NOTINVIEW if the entire MIB tree is inaccessible
|
|
* VACM_SUBTREE_UNKNOWN if some portions are accessible
|
|
* other codes may returned on error
|
|
*/
|
|
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:
|
|
NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
|
|
snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
|
|
SNMPD_CALLBACK_ACM_CHECK_SUBTREE, &view_parms);
|
|
return view_parms.errorcode;
|
|
}
|
|
unsupported_version:
|
|
return 1;
|
|
}
|
|
|
|
netsnmp_feature_child_of(get_session_for_oid,netsnmp_unused)
|
|
#ifndef NETSNMP_FEATURE_REMOVE_GET_SESSION_FOR_OID
|
|
netsnmp_session *
|
|
get_session_for_oid(const 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;
|
|
}
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_GET_SESSION_FOR_OID */
|
|
|
|
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
|
|
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 =
|
|
(struct variable*)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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SNMP_FREE(s);
|
|
SNMP_FREE(e);
|
|
SNMP_FREE(v);
|
|
|
|
dump_idx_registry();
|
|
}
|
|
|
|
/** @} */
|
|
/* End of MIB registration code */
|
|
|
|
|
|
netsnmp_feature_child_of(register_signal, netsnmp_unused)
|
|
#ifndef NETSNMP_FEATURE_REMOVE_REGISTER_SIGNAL
|
|
|
|
/** @defgroup agent_signals POSIX signals support for agents.
|
|
* Registering and unregistering signal handlers.
|
|
* @ingroup agent_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
|
|
}
|
|
|
|
/** Registers a POSIX Signal handler.
|
|
* Implements the signal registering process for POSIX and non-POSIX
|
|
* systems. Also, unifies the way signals work.
|
|
* Note that the signal handler should register itself again with
|
|
* signal() call before end of execution to prevent possible problems.
|
|
*
|
|
* @param sig POSIX Signal ID number, as defined in signal.h.
|
|
*
|
|
* @param func New signal handler function.
|
|
*
|
|
* @return value is SIG_REGISTERED_OK for success and
|
|
* SIG_REGISTRATION_FAILED if the registration can't
|
|
* be handled.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** Unregisters a POSIX Signal handler.
|
|
*
|
|
* @param sig POSIX Signal ID number, as defined in signal.h.
|
|
*
|
|
* @return value is SIG_UNREGISTERED_OK for success, or error code.
|
|
*/
|
|
int
|
|
unregister_signal(int sig)
|
|
{
|
|
signal(sig, SIG_DFL);
|
|
DEBUGMSGTL(("unregister_signal", "unregistered signal %d\n", sig));
|
|
return SIG_UNREGISTERED_OK;
|
|
}
|
|
|
|
#endif /* !WIN32 */
|
|
|
|
/** @} */
|
|
/* End of signals support code */
|
|
|
|
#endif /* NETSNMP_FEATURE_REMOVE_REGISTER_SIGNAL */
|
|
|
|
/** @} */
|
|
|