/*
 * 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>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#ifdef 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);
            if (!ptr->context) {
                free(ptr);
                return NULL;
            }
            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);
    if (!ptr->context_name) {
        free(ptr);
        return NULL;
    }

    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(&reg_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.session = ss;
        reg_parms.reginfo = reginfo;
        reg_parms.contextName = context;
        snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
                            SNMPD_CALLBACK_REGISTER_OID, &reg_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(&reg_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, &reg_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(&reg_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, &reg_parms);

    /*
     * netsnmp_subtree_free() may free the 'context' pointer. Hence, call
     * invalidate_lookup_cache() before calling netsnmp_subtree_free().
     */
    invalidate_lookup_cache(context);
    netsnmp_subtree_free(myptr);
    netsnmp_set_lookup_cache_size(old_lookup_cache_val);
    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(&reg_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, &reg_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 */

/**  @} */

