blob: b2ac97ecc38e42e688bd55cc9e7122cfc12ca122 [file] [log] [blame]
/* 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 (C) 2007 Apple, Inc. All rights reserved.
* Use is subject to license terms specified in the COPYING file
* distributed with the Net-SNMP package.
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/library/container.h>
#include <net-snmp/library/container_binary_array.h>
#include <net-snmp/library/container_list_ssll.h>
#include <net-snmp/library/container_null.h>
netsnmp_feature_child_of(container_all, libnetsnmp)
netsnmp_feature_child_of(container_factories, container_all)
netsnmp_feature_child_of(container_types, container_all)
netsnmp_feature_child_of(container_compare, container_all)
netsnmp_feature_child_of(container_dup, container_all)
netsnmp_feature_child_of(container_free_all, container_all)
netsnmp_feature_child_of(subcontainer_find, container_all)
netsnmp_feature_child_of(container_ncompare_cstring, container_compare)
netsnmp_feature_child_of(container_compare_mem, container_compare)
netsnmp_feature_child_of(container_compare_long, container_compare)
netsnmp_feature_child_of(container_compare_ulong, container_compare)
netsnmp_feature_child_of(container_compare_int32, container_compare)
netsnmp_feature_child_of(container_compare_uint32, container_compare)
netsnmp_feature_child_of(container_find_factory, container_factories)
/** @defgroup container container
*/
/*------------------------------------------------------------------
*/
static netsnmp_container *containers = NULL;
typedef struct container_type_s {
const char *name;
netsnmp_factory *factory;
netsnmp_container_compare *compare;
} container_type;
netsnmp_factory *
netsnmp_container_get_factory(const char *type);
/*------------------------------------------------------------------
*/
static void
_factory_free(void *dat, void *context)
{
container_type *data = (container_type *)dat;
if (data == NULL)
return;
if (data->name != NULL) {
DEBUGMSGTL(("container", " _factory_free_list() called for %s\n",
data->name));
free(NETSNMP_REMOVE_CONST(void *, data->name)); /* SNMP_FREE wasted on object about to be freed */
}
free(data); /* SNMP_FREE wasted on param */
}
/*------------------------------------------------------------------
*/
void
netsnmp_container_init_list(void)
{
if (NULL != containers)
return;
/*
* create a binary arry container to hold container
* factories
*/
containers = netsnmp_container_get_binary_array();
containers->compare = netsnmp_compare_cstring;
containers->container_name = strdup("container list");
/*
* register containers
*/
netsnmp_container_binary_array_init();
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_LINKED_LIST
netsnmp_container_ssll_init();
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_LINKED_LIST */
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_NULL
netsnmp_container_null_init();
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_NULL */
/*
* default aliases for some containers
*/
netsnmp_container_register("table_container",
netsnmp_container_get_factory("binary_array"));
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_LINKED_LIST
netsnmp_container_register("linked_list",
netsnmp_container_get_factory("sorted_singly_linked_list"));
netsnmp_container_register("ssll_container",
netsnmp_container_get_factory("sorted_singly_linked_list"));
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_LINKED_LIST */
netsnmp_container_register_with_compare
("cstring", netsnmp_container_get_factory("binary_array"),
netsnmp_compare_direct_cstring);
netsnmp_container_register_with_compare
("string", netsnmp_container_get_factory("binary_array"),
netsnmp_compare_cstring);
netsnmp_container_register_with_compare
("string_binary_array", netsnmp_container_get_factory("binary_array"),
netsnmp_compare_cstring);
}
void
netsnmp_container_free_list(void)
{
DEBUGMSGTL(("container", "netsnmp_container_free_list() called\n"));
if (containers == NULL)
return;
/*
* free memory used by each factory entry
*/
CONTAINER_FOR_EACH(containers, ((netsnmp_container_obj_func *)_factory_free), NULL);
/*
* free factory container
*/
CONTAINER_FREE(containers);
containers = NULL;
}
int
netsnmp_container_register_with_compare(const char* name, netsnmp_factory *f,
netsnmp_container_compare *c)
{
container_type *ct, tmp;
if (NULL==containers)
return -1;
tmp.name = NETSNMP_REMOVE_CONST(char *, name);
ct = (container_type *)CONTAINER_FIND(containers, &tmp);
if (NULL!=ct) {
DEBUGMSGT(("container_registry",
"replacing previous container factory\n"));
ct->factory = f;
}
else {
ct = SNMP_MALLOC_TYPEDEF(container_type);
if (NULL == ct)
return -1;
ct->name = strdup(name);
ct->factory = f;
ct->compare = c;
CONTAINER_INSERT(containers, ct);
}
DEBUGMSGT(("container_registry", "registered container factory %s (%s)\n",
ct->name, f->product));
return 0;
}
int
netsnmp_container_register(const char* name, netsnmp_factory *f)
{
return netsnmp_container_register_with_compare(name, f, NULL);
}
/*------------------------------------------------------------------
*/
netsnmp_factory *
netsnmp_container_get_factory(const char *type)
{
container_type ct, *found;
if (NULL==containers)
return NULL;
ct.name = type;
found = (container_type *)CONTAINER_FIND(containers, &ct);
return found ? found->factory : NULL;
}
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_FIND_FACTORY
netsnmp_factory *
netsnmp_container_find_factory(const char *type_list)
{
netsnmp_factory *f = NULL;
char *list, *entry;
char *st = NULL;
if (NULL==type_list)
return NULL;
list = strdup(type_list);
entry = strtok_r(list, ":", &st);
while(entry) {
f = netsnmp_container_get_factory(entry);
if (NULL != f)
break;
entry = strtok_r(NULL, ":", &st);
}
free(list);
return f;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_FIND_FACTORY */
/*------------------------------------------------------------------
*/
static container_type *
netsnmp_container_get_ct(const char *type)
{
container_type ct;
if (NULL == containers)
return NULL;
ct.name = type;
return (container_type *)CONTAINER_FIND(containers, &ct);
}
static container_type *
netsnmp_container_find_ct(const char *type_list)
{
container_type *ct = NULL;
char *list, *entry;
char *st = NULL;
if (NULL==type_list)
return NULL;
list = strdup(type_list);
entry = strtok_r(list, ":", &st);
while(entry) {
ct = netsnmp_container_get_ct(entry);
if (NULL != ct)
break;
entry = strtok_r(NULL, ":", &st);
}
free(list);
return ct;
}
/*------------------------------------------------------------------
*/
netsnmp_container *
netsnmp_container_get(const char *type)
{
netsnmp_container *c;
container_type *ct = netsnmp_container_get_ct(type);
if (ct) {
c = (netsnmp_container *)(ct->factory->produce());
if (c && ct->compare)
c->compare = ct->compare;
return c;
}
return NULL;
}
/*------------------------------------------------------------------
*/
netsnmp_container *
netsnmp_container_find(const char *type)
{
container_type *ct = netsnmp_container_find_ct(type);
netsnmp_container *c = ct ? (netsnmp_container *)(ct->factory->produce()) : NULL;
/*
* provide default compare
*/
if (c) {
if (ct->compare)
c->compare = ct->compare;
else if (NULL == c->compare)
c->compare = netsnmp_compare_netsnmp_index;
}
return c;
}
/*------------------------------------------------------------------
*/
void
netsnmp_container_add_index(netsnmp_container *primary,
netsnmp_container *new_index)
{
netsnmp_container *curr = primary;
if((NULL == new_index) || (NULL == primary)) {
snmp_log(LOG_ERR, "add index called with null pointer\n");
return;
}
while(curr->next)
curr = curr->next;
curr->next = new_index;
new_index->prev = curr;
}
/*------------------------------------------------------------------
* These functions should EXACTLY match the inline version in
* container.h. If you change one, change them both.
*/
int CONTAINER_INSERT_HELPER(netsnmp_container* x, const void* k)
{
while(x && x->insert_filter && x->insert_filter(x,k) == 1)
x = x->next;
if(x) {
int rc = x->insert(x,k);
if(rc)
snmp_log(LOG_DEBUG,"error on subcontainer '%s' insert (%d)\n",
x->container_name ? x->container_name : "", rc);
else {
rc = CONTAINER_INSERT_HELPER(x->next, k);
if(rc)
x->remove(x,k);
}
return rc;
}
return 0;
}
/*------------------------------------------------------------------
* These functions should EXACTLY match the inline version in
* container.h. If you change one, change them both.
*/
int CONTAINER_INSERT(netsnmp_container* x, const void* k)
{
/** start at first container */
while(x->prev)
x = x->prev;
return CONTAINER_INSERT_HELPER(x, k);
}
/*------------------------------------------------------------------
* These functions should EXACTLY match the inline version in
* container.h. If you change one, change them both.
*/
int CONTAINER_REMOVE(netsnmp_container *x, const void *k)
{
int rc2, rc = 0;
/** start at last container */
while(x->next)
x = x->next;
while(x) {
rc2 = x->remove(x,k);
/** ignore remove errors if there is a filter in place */
if ((rc2) && (NULL == x->insert_filter)) {
snmp_log(LOG_ERR,"error on subcontainer '%s' remove (%d)\n",
x->container_name ? x->container_name : "", rc2);
rc = rc2;
}
x = x->prev;
}
return rc;
}
/*------------------------------------------------------------------
* These functions should EXACTLY match the function version in
* container.c. If you change one, change them both.
*/
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_DUP
netsnmp_container *CONTAINER_DUP(netsnmp_container *x, void *ctx, u_int flags)
{
if (NULL == x->duplicate) {
snmp_log(LOG_ERR, "container '%s' does not support duplicate\n",
x->container_name ? x->container_name : "");
return NULL;
}
return x->duplicate(x, ctx, flags);
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_DUP */
/*------------------------------------------------------------------
* These functions should EXACTLY match the inline version in
* container.h. If you change one, change them both.
*/
int CONTAINER_FREE(netsnmp_container *x)
{
int rc2, rc = 0;
/** start at last container */
while(x->next)
x = x->next;
while(x) {
netsnmp_container *tmp;
char *name;
tmp = x->prev;
name = x->container_name;
x->container_name = NULL;
rc2 = x->cfree(x);
if (rc2) {
snmp_log(LOG_ERR,"error on subcontainer '%s' cfree (%d)\n",
name ? name : "", rc2);
rc = rc2;
}
SNMP_FREE(name);
x = tmp;
}
return rc;
}
/*------------------------------------------------------------------
* These functions should EXACTLY match the function version in
* container.c. If you change one, change them both.
*/
/*
* clear all containers. When clearing the *first* container, and
* *only* the first container, call the function f for each item.
* After calling this function, all containers should be empty.
*/
void CONTAINER_CLEAR(netsnmp_container *x, netsnmp_container_obj_func *f,
void *c)
{
/** start at last container */
while(x->next)
x = x->next;
while(x->prev) {
x->clear(x, NULL, c);
x = x->prev;
}
x->clear(x, f, c);
}
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_FREE_ALL
/*
* clear all containers. When clearing the *first* container, and
* *only* the first container, call the free_item function for each item.
* After calling this function, all containers should be empty.
*/
void CONTAINER_FREE_ALL(netsnmp_container *x, void *c)
{
CONTAINER_CLEAR(x, x->free_item, c);
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_FREE_ALL */
#ifndef NETSNMP_FEATURE_REMOVE_SUBCONTAINER_FIND
/*------------------------------------------------------------------
* These functions should EXACTLY match the function version in
* container.c. If you change one, change them both.
*/
/*
* Find a sub-container with the given name
*/
netsnmp_container *SUBCONTAINER_FIND(netsnmp_container *x,
const char* name)
{
if ((NULL == x) || (NULL == name))
return NULL;
/** start at first container */
while(x->prev)
x = x->prev;
while(x) {
if ((NULL != x->container_name) && (0 == strcmp(name,x->container_name)))
break;
x = x->next;
}
return x;
}
#endif /* NETSNMP_FEATURE_REMOVE_SUBCONTAINER_FIND */
/*------------------------------------------------------------------
*/
void
netsnmp_init_container(netsnmp_container *c,
netsnmp_container_rc *init,
netsnmp_container_rc *cfree,
netsnmp_container_size *size,
netsnmp_container_compare *cmp,
netsnmp_container_op *ins,
netsnmp_container_op *rem,
netsnmp_container_rtn *fnd)
{
if (c == NULL)
return;
c->init = init;
c->cfree = cfree;
c->get_size = size;
c->compare = cmp;
c->insert = ins;
c->remove = rem;
c->find = fnd;
c->free_item = netsnmp_container_simple_free;
}
int
netsnmp_container_data_dup(netsnmp_container *dup, netsnmp_container *c)
{
if (!dup || !c)
return -1;
if (c->container_name)
dup->container_name = strdup(c->container_name);
dup->compare = c->compare;
dup->ncompare = c->ncompare;
dup->release = c->release;
dup->insert_filter = c->insert_filter;
dup->free_item = c->free_item;
dup->sync = c->sync;
dup->flags = c->flags;
return 0;
}
/*------------------------------------------------------------------
*
* simple comparison routines
*
*/
int
netsnmp_compare_netsnmp_index(const void *lhs, const void *rhs)
{
int rc;
netsnmp_assert((NULL != lhs) && (NULL != rhs));
DEBUGIF("compare:index") {
DEBUGMSGT(("compare:index", "compare "));
DEBUGMSGSUBOID(("compare:index", ((const netsnmp_index *) lhs)->oids,
((const netsnmp_index *) lhs)->len));
DEBUGMSG(("compare:index", " to "));
DEBUGMSGSUBOID(("compare:index", ((const netsnmp_index *) rhs)->oids,
((const netsnmp_index *) rhs)->len));
DEBUGMSG(("compare:index", "\n"));
}
rc = snmp_oid_compare(((const netsnmp_index *) lhs)->oids,
((const netsnmp_index *) lhs)->len,
((const netsnmp_index *) rhs)->oids,
((const netsnmp_index *) rhs)->len);
DEBUGMSGT(("compare:index", "result was %d\n", rc));
return rc;
}
int
netsnmp_ncompare_netsnmp_index(const void *lhs, const void *rhs)
{
int rc;
netsnmp_assert((NULL != lhs) && (NULL != rhs));
DEBUGIF("compare:index") {
DEBUGMSGT(("compare:index", "compare "));
DEBUGMSGSUBOID(("compare:index", ((const netsnmp_index *) lhs)->oids,
((const netsnmp_index *) lhs)->len));
DEBUGMSG(("compare:index", " to "));
DEBUGMSGSUBOID(("compare:index", ((const netsnmp_index *) rhs)->oids,
((const netsnmp_index *) rhs)->len));
DEBUGMSG(("compare:index", "\n"));
}
rc = snmp_oid_ncompare(((const netsnmp_index *) lhs)->oids,
((const netsnmp_index *) lhs)->len,
((const netsnmp_index *) rhs)->oids,
((const netsnmp_index *) rhs)->len,
((const netsnmp_index *) rhs)->len);
DEBUGMSGT(("compare:index", "result was %d\n", rc));
return rc;
}
int
netsnmp_compare_cstring(const void * lhs, const void * rhs)
{
return strcmp(((const container_type*)lhs)->name,
((const container_type*)rhs)->name);
}
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_NCOMPARE_CSTRING
int
netsnmp_ncompare_cstring(const void * lhs, const void * rhs)
{
return strncmp(((const container_type*)lhs)->name,
((const container_type*)rhs)->name,
strlen(((const container_type*)rhs)->name));
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_NCOMPARE_CSTRING */
int
netsnmp_compare_direct_cstring(const void * lhs, const void * rhs)
{
return strcmp((const char*)lhs, (const char*)rhs);
}
/*
* compare two memory buffers
*
* since snmp strings aren't NULL terminated, we can't use strcmp. So
* compare up to the length of the smaller, and then use length to
* break any ties.
*/
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_MEM
int
netsnmp_compare_mem(const char * lhs, size_t lhs_len,
const char * rhs, size_t rhs_len)
{
int rc, min = SNMP_MIN(lhs_len, rhs_len);
rc = memcmp(lhs, rhs, min);
if((rc==0) && (lhs_len != rhs_len)) {
if(lhs_len < rhs_len)
rc = -1;
else
rc = 1;
}
return rc;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_MEM */
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_LONG
int
netsnmp_compare_long(const void * lhs, const void * rhs)
{
typedef struct { long index; } dummy;
const dummy *lhd = (const dummy*)lhs;
const dummy *rhd = (const dummy*)rhs;
if (lhd->index < rhd->index)
return -1;
else if (lhd->index > rhd->index)
return 1;
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_LONG */
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_ULONG
int
netsnmp_compare_ulong(const void * lhs, const void * rhs)
{
typedef struct { u_long index; } dummy;
const dummy *lhd = (const dummy*)lhs;
const dummy *rhd = (const dummy*)rhs;
if (lhd->index < rhd->index)
return -1;
else if (lhd->index > rhd->index)
return 1;
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_ULONG */
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_INT32
int
netsnmp_compare_int32(const void * lhs, const void * rhs)
{
typedef struct { int32_t index; } dummy;
const dummy *lhd = (const dummy*)lhs;
const dummy *rhd = (const dummy*)rhs;
if (lhd->index < rhd->index)
return -1;
else if (lhd->index > rhd->index)
return 1;
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_INT32 */
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_UINT32
int
netsnmp_compare_uint32(const void * lhs, const void * rhs)
{
typedef struct { uint32_t index; } dummy;
const dummy *lhd = (const dummy*)lhs;
const dummy *rhd = (const dummy*)rhs;
if (lhd->index < rhd->index)
return -1;
else if (lhd->index > rhd->index)
return 1;
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_COMPARE_UINT32 */
/*------------------------------------------------------------------
* netsnmp_container_simple_free
*
* useful function to pass to CONTAINER_FOR_EACH, when a simple
* free is needed for every item.
*/
void
netsnmp_container_simple_free(void *data, void *context)
{
if (data == NULL)
return;
DEBUGMSGTL(("verbose:container",
"netsnmp_container_simple_free) called for %p/%p\n",
data, context));
free((void*)data); /* SNMP_FREE wasted on param */
}