1353 lines
47 KiB
C
1353 lines
47 KiB
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.
|
|
*/
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-features.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
|
|
#include <net-snmp/agent/bulk_to_next.h>
|
|
|
|
netsnmp_feature_child_of(agent_handler, libnetsnmpagent)
|
|
|
|
netsnmp_feature_child_of(handler_mark_requests_as_delegated, agent_handler)
|
|
|
|
static netsnmp_mib_handler *_clone_handler(netsnmp_mib_handler *it);
|
|
|
|
/***********************************************************************/
|
|
/*
|
|
* New Handler based API
|
|
*/
|
|
/***********************************************************************/
|
|
/** @defgroup handler Net-SNMP Agent handler and extensibility API
|
|
* @ingroup agent
|
|
*
|
|
* The basic theory goes something like this: In the past, with the
|
|
* original mib module api (which derived from the original CMU SNMP
|
|
* code) the underlying mib modules were passed very little
|
|
* information (only the truly most basic information about a
|
|
* request). This worked well at the time but in todays world of
|
|
* subagents, device instrumentation, low resource consumption, etc,
|
|
* it just isn't flexible enough. "handlers" are here to fix all that.
|
|
*
|
|
* With the rewrite of the agent internals for the net-snmp 5.0
|
|
* release, we introduce a modular calling scheme that allows agent
|
|
* modules to be written in a very flexible manner, and more
|
|
* importantly allows reuse of code in a decent way (and without the
|
|
* memory and speed overheads of OO languages like C++).
|
|
*
|
|
* Functionally, the notion of what a handler does is the same as the
|
|
* older api: A handler is @link netsnmp_create_handler() created@endlink and
|
|
* then @link netsnmp_register_handler() registered@endlink with the main
|
|
* agent at a given OID in the OID tree and gets called any time a
|
|
* request is made that it should respond to. You probably should
|
|
* use one of the convenience helpers instead of doing anything else
|
|
* yourself though:
|
|
*
|
|
* Most importantly, though, is that the handlers are built on the
|
|
* notion of modularity and reuse. Specifically, rather than do all
|
|
* the really hard work (like parsing table indexes out of an
|
|
* incoming oid request) in each module, the API is designed to make
|
|
* it easy to write "helper" handlers that merely process some aspect
|
|
* of the request before passing it along to the final handler that
|
|
* returns the real answer. Most people will want to make use of the
|
|
* @link instance instance@endlink, @link table table@endlink, @link
|
|
* table_iterator table_iterator@endlink, @link table_data
|
|
* table_data@endlink, or @link table_dataset table_dataset@endlink
|
|
* helpers to make their life easier. These "helpers" interpert
|
|
* important aspects of the request and pass them on to you.
|
|
*
|
|
* For instance, the @link table table@endlink helper is designed to
|
|
* hand you a list of extracted index values from an incoming
|
|
* request. THe @link table_iterator table_iterator@endlink helper
|
|
* is built on top of the table helper, and is designed to help you
|
|
* iterate through data stored elsewhere (like in a kernel) that is
|
|
* not in OID lexographical order (ie, don't write your own index/oid
|
|
* sorting routine, use this helper instead). The beauty of the
|
|
* @link table_iterator table_iterator helper@endlink, as well as the @link
|
|
* instance instance@endlink helper is that they take care of the complex
|
|
* GETNEXT processing entirely for you and hand you everything you
|
|
* need to merely return the data as if it was a GET request. Much
|
|
* less code and hair pulling. I've pulled all my hair out to help
|
|
* you so that only one of us has to be bald.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/** Creates a MIB handler structure.
|
|
* The new structure is allocated and filled using the given name
|
|
* and access method.
|
|
* The returned handler should then be @link netsnmp_register_handler()
|
|
* registered @endlink.
|
|
*
|
|
* @param name is the handler name and is copied then assigned to
|
|
* netsnmp_mib_handler->handler_name
|
|
*
|
|
* @param handler_access_method is a function pointer used as the access
|
|
* method for this handler registration instance for whatever required
|
|
* needs.
|
|
*
|
|
* @return a pointer to a populated netsnmp_mib_handler struct to be
|
|
* registered
|
|
*
|
|
* @see netsnmp_create_handler_registration()
|
|
* @see netsnmp_register_handler()
|
|
*/
|
|
netsnmp_mib_handler *
|
|
netsnmp_create_handler(const char *name,
|
|
Netsnmp_Node_Handler * handler_access_method)
|
|
{
|
|
netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler);
|
|
if (ret) {
|
|
ret->access_method = handler_access_method;
|
|
if (NULL != name) {
|
|
ret->handler_name = strdup(name);
|
|
if (NULL == ret->handler_name)
|
|
SNMP_FREE(ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/** Creates a MIB handler structure.
|
|
* The new structure is allocated and filled using the given name,
|
|
* access function, registration location OID and list of modes that
|
|
* the handler supports. If modes == 0, then modes will automatically
|
|
* be set to the default value of only HANDLER_CAN_DEFAULT, which is
|
|
* by default read-only GET and GETNEXT requests. A hander which supports
|
|
* sets but not row creation should set us a mode of HANDLER_CAN_SET_ONLY.
|
|
* @note This ends up calling netsnmp_create_handler(name, handler_access_method)
|
|
* @param name is the handler name and is copied then assigned to
|
|
* netsnmp_handler_registration->handlerName.
|
|
*
|
|
* @param handler is a function pointer used as the access
|
|
* method for this handler registration instance for whatever required
|
|
* needs.
|
|
*
|
|
* @param reg_oid is the registration location oid.
|
|
*
|
|
* @param reg_oid_len is the length of reg_oid; can use the macro,
|
|
* OID_LENGTH
|
|
*
|
|
* @param modes is used to configure read/write access. If modes == 0,
|
|
* then modes will automatically be set to the default
|
|
* value of only HANDLER_CAN_DEFAULT, which is by default read-only GET
|
|
* and GETNEXT requests. The other two mode options are read only,
|
|
* HANDLER_CAN_RONLY, and read/write, HANDLER_CAN_RWRITE.
|
|
*
|
|
* - HANDLER_CAN_GETANDGETNEXT
|
|
* - HANDLER_CAN_SET
|
|
* - HANDLER_CAN_GETBULK
|
|
*
|
|
* - HANDLER_CAN_RONLY (HANDLER_CAN_GETANDGETNEXT)
|
|
* - HANDLER_CAN_RWRITE (HANDLER_CAN_GETANDGETNEXT |
|
|
* HANDLER_CAN_SET)
|
|
* - HANDLER_CAN_DEFAULT HANDLER_CAN_RONLY
|
|
*
|
|
* @return Returns a pointer to a netsnmp_handler_registration struct.
|
|
* NULL is returned only when memory could not be allocated for the
|
|
* netsnmp_handler_registration struct.
|
|
*
|
|
*
|
|
* @see netsnmp_create_handler()
|
|
* @see netsnmp_register_handler()
|
|
*/
|
|
netsnmp_handler_registration *
|
|
netsnmp_handler_registration_create(const char *name,
|
|
netsnmp_mib_handler *handler,
|
|
const oid * reg_oid, size_t reg_oid_len,
|
|
int modes)
|
|
{
|
|
netsnmp_handler_registration *the_reg;
|
|
the_reg = SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration);
|
|
if (!the_reg)
|
|
return NULL;
|
|
|
|
if (modes)
|
|
the_reg->modes = modes;
|
|
else
|
|
the_reg->modes = HANDLER_CAN_DEFAULT;
|
|
|
|
the_reg->handler = handler;
|
|
the_reg->priority = DEFAULT_MIB_PRIORITY;
|
|
if (name)
|
|
the_reg->handlerName = strdup(name);
|
|
the_reg->rootoid = snmp_duplicate_objid(reg_oid, reg_oid_len);
|
|
the_reg->rootoid_len = reg_oid_len;
|
|
return the_reg;
|
|
}
|
|
|
|
/** Creates a handler registration structure with a new MIB handler.
|
|
* This function first @link netsnmp_create_handler() creates @endlink
|
|
* a MIB handler, then @link netsnmp_handler_registration_create()
|
|
* makes registation structure @endlink for it.
|
|
*
|
|
* @param name is the handler name for netsnmp_create_handler()
|
|
*
|
|
* @param handler_access_method is a function pointer used as the access
|
|
* method for netsnmp_create_handler()
|
|
*
|
|
* @param reg_oid is the registration location oid.
|
|
*
|
|
* @param reg_oid_len is the length of reg_oid; can use the macro,
|
|
* OID_LENGTH
|
|
*
|
|
* @param modes is used to configure read/write access, as in
|
|
* netsnmp_handler_registration_create()
|
|
*
|
|
* @return Returns a pointer to a netsnmp_handler_registration struct.
|
|
* If the structures creation failed, NULL is returned.
|
|
*
|
|
* @see netsnmp_create_handler()
|
|
* @see netsnmp_handler_registration_create()
|
|
*/
|
|
netsnmp_handler_registration *
|
|
netsnmp_create_handler_registration(const char *name,
|
|
Netsnmp_Node_Handler *
|
|
handler_access_method, const oid * reg_oid,
|
|
size_t reg_oid_len, int modes)
|
|
{
|
|
netsnmp_handler_registration *rv = NULL;
|
|
netsnmp_mib_handler *handler =
|
|
netsnmp_create_handler(name, handler_access_method);
|
|
if (handler) {
|
|
rv = netsnmp_handler_registration_create(
|
|
name, handler, reg_oid, reg_oid_len, modes);
|
|
if (!rv)
|
|
netsnmp_handler_free(handler);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/** Registers a MIB handler inside the registration structure.
|
|
* Checks given registation handler for sanity, then
|
|
* @link netsnmp_register_mib() performs registration @endlink
|
|
* in the MIB tree, as defined by the netsnmp_handler_registration
|
|
* pointer. On success, SNMP_CALLBACK_APPLICATION is called.
|
|
* The registration struct may be created by call of
|
|
* netsnmp_create_handler_registration().
|
|
*
|
|
* @param reginfo Pointer to a netsnmp_handler_registration struct.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*
|
|
* @see netsnmp_create_handler_registration()
|
|
* @see netsnmp_register_mib()
|
|
*/
|
|
int
|
|
netsnmp_register_handler(netsnmp_handler_registration *reginfo)
|
|
{
|
|
netsnmp_mib_handler *handler;
|
|
int flags = 0;
|
|
|
|
if (reginfo == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_register_handler() called illegally\n");
|
|
netsnmp_assert(reginfo != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
DEBUGIF("handler::register") {
|
|
DEBUGMSGTL(("handler::register", "Registering %s (", reginfo->handlerName));
|
|
for (handler = reginfo->handler; handler; handler = handler->next) {
|
|
DEBUGMSG(("handler::register", "::%s", handler->handler_name));
|
|
}
|
|
|
|
DEBUGMSG(("handler::register", ") at "));
|
|
if (reginfo->rootoid && reginfo->range_subid) {
|
|
DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid,
|
|
reginfo->rootoid_len, reginfo->range_subid,
|
|
reginfo->range_ubound));
|
|
} else if (reginfo->rootoid) {
|
|
DEBUGMSGOID(("handler::register", reginfo->rootoid,
|
|
reginfo->rootoid_len));
|
|
} else {
|
|
DEBUGMSG(("handler::register", "[null]"));
|
|
}
|
|
DEBUGMSG(("handler::register", "\n"));
|
|
}
|
|
|
|
/*
|
|
* don't let them register for absolutely nothing. Probably a mistake
|
|
*/
|
|
if (0 == reginfo->modes) {
|
|
reginfo->modes = HANDLER_CAN_DEFAULT;
|
|
snmp_log(LOG_WARNING, "no registration modes specified for %s. "
|
|
"Defaulting to 0x%x\n", reginfo->handlerName, reginfo->modes);
|
|
}
|
|
|
|
/*
|
|
* for handlers that can't GETBULK, force a conversion handler on them
|
|
*/
|
|
if (!(reginfo->modes & HANDLER_CAN_GETBULK)) {
|
|
handler = netsnmp_get_bulk_to_next_handler();
|
|
if (!handler ||
|
|
(netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
|
|
snmp_log(LOG_WARNING, "could not inject bulk to next handler\n");
|
|
if (handler)
|
|
netsnmp_handler_free(handler);
|
|
/** should this be a critical error? */
|
|
netsnmp_handler_registration_free(reginfo);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
}
|
|
|
|
for (handler = reginfo->handler; handler; handler = handler->next) {
|
|
if (handler->flags & MIB_HANDLER_INSTANCE)
|
|
flags = FULLY_QUALIFIED_INSTANCE;
|
|
}
|
|
|
|
return netsnmp_register_mib(reginfo->handlerName,
|
|
NULL, 0, 0,
|
|
reginfo->rootoid, reginfo->rootoid_len,
|
|
reginfo->priority,
|
|
reginfo->range_subid,
|
|
reginfo->range_ubound, NULL,
|
|
reginfo->contextName, reginfo->timeout, flags,
|
|
reginfo, 1);
|
|
}
|
|
|
|
/** Unregisters a MIB handler described inside the registration structure.
|
|
* Removes a registration, performed earlier by
|
|
* netsnmp_register_handler(), from the MIB tree.
|
|
* Uses unregister_mib_context() to do the task.
|
|
*
|
|
* @param reginfo Pointer to a netsnmp_handler_registration struct.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*
|
|
* @see netsnmp_register_handler()
|
|
* @see unregister_mib_context()
|
|
*/
|
|
int
|
|
netsnmp_unregister_handler(netsnmp_handler_registration *reginfo)
|
|
{
|
|
if (!reginfo)
|
|
return SNMPERR_SUCCESS;
|
|
return unregister_mib_context(reginfo->rootoid, reginfo->rootoid_len,
|
|
reginfo->priority,
|
|
reginfo->range_subid, reginfo->range_ubound,
|
|
reginfo->contextName);
|
|
}
|
|
|
|
/** Registers a MIB handler inside the registration structure.
|
|
* Checks given registation handler for sanity, then
|
|
* @link netsnmp_register_mib() performs registration @endlink
|
|
* in the MIB tree, as defined by the netsnmp_handler_registration
|
|
* pointer. Never calls SNMP_CALLBACK_APPLICATION.
|
|
* The registration struct may be created by call of
|
|
* netsnmp_create_handler_registration().
|
|
*
|
|
* @param reginfo Pointer to a netsnmp_handler_registration struct.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*
|
|
* @see netsnmp_create_handler_registration()
|
|
* @see netsnmp_register_mib()
|
|
*/
|
|
int
|
|
netsnmp_register_handler_nocallback(netsnmp_handler_registration *reginfo)
|
|
{
|
|
netsnmp_mib_handler *handler;
|
|
if (reginfo == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_register_handler_nocallback() called illegally\n");
|
|
netsnmp_assert(reginfo != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
DEBUGIF("handler::register") {
|
|
DEBUGMSGTL(("handler::register",
|
|
"Registering (with no callback) "));
|
|
for (handler = reginfo->handler; handler; handler = handler->next) {
|
|
DEBUGMSG(("handler::register", "::%s", handler->handler_name));
|
|
}
|
|
|
|
DEBUGMSG(("handler::register", " at "));
|
|
if (reginfo->rootoid && reginfo->range_subid) {
|
|
DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid,
|
|
reginfo->rootoid_len, reginfo->range_subid,
|
|
reginfo->range_ubound));
|
|
} else if (reginfo->rootoid) {
|
|
DEBUGMSGOID(("handler::register", reginfo->rootoid,
|
|
reginfo->rootoid_len));
|
|
} else {
|
|
DEBUGMSG(("handler::register", "[null]"));
|
|
}
|
|
DEBUGMSG(("handler::register", "\n"));
|
|
}
|
|
|
|
/*
|
|
* don't let them register for absolutely nothing. Probably a mistake
|
|
*/
|
|
if (0 == reginfo->modes) {
|
|
reginfo->modes = HANDLER_CAN_DEFAULT;
|
|
}
|
|
|
|
return netsnmp_register_mib(reginfo->handler->handler_name,
|
|
NULL, 0, 0,
|
|
reginfo->rootoid, reginfo->rootoid_len,
|
|
reginfo->priority,
|
|
reginfo->range_subid,
|
|
reginfo->range_ubound, NULL,
|
|
reginfo->contextName, reginfo->timeout, 0,
|
|
reginfo, 0);
|
|
}
|
|
|
|
/** Injects handler into the calling chain of handlers.
|
|
* The given MIB handler is inserted after the handler named before_what.
|
|
* If before_what is NULL, the handler is put at the top of the list,
|
|
* and hence will be the handler to be called first.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*
|
|
* @see netsnmp_create_handler_registration()
|
|
* @see netsnmp_inject_handler()
|
|
*/
|
|
int
|
|
netsnmp_inject_handler_before(netsnmp_handler_registration *reginfo,
|
|
netsnmp_mib_handler *handler,
|
|
const char *before_what)
|
|
{
|
|
netsnmp_mib_handler *handler2 = handler;
|
|
|
|
if (handler == NULL || reginfo == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_inject_handler() called illegally\n");
|
|
netsnmp_assert(reginfo != NULL);
|
|
netsnmp_assert(handler != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
while (handler2->next) {
|
|
handler2 = handler2->next; /* Find the end of a handler sub-chain */
|
|
}
|
|
if (reginfo->handler == NULL) {
|
|
DEBUGMSGTL(("handler:inject", "injecting %s\n", handler->handler_name));
|
|
}
|
|
else {
|
|
DEBUGMSGTL(("handler:inject", "injecting %s before %s\n",
|
|
handler->handler_name, reginfo->handler->handler_name));
|
|
}
|
|
if (before_what) {
|
|
netsnmp_mib_handler *nexth, *prevh = NULL;
|
|
if (reginfo->handler == NULL) {
|
|
snmp_log(LOG_ERR, "no handler to inject before\n");
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
for(nexth = reginfo->handler; nexth;
|
|
prevh = nexth, nexth = nexth->next) {
|
|
if (strcmp(nexth->handler_name, before_what) == 0)
|
|
break;
|
|
}
|
|
if (!nexth) {
|
|
snmp_log(LOG_ERR, "Cannot inject '%s' before '%s': not found\n", handler->handler_name, before_what);
|
|
snmp_log(LOG_ERR, "The handlers are:\n");
|
|
for (nexth = reginfo->handler; nexth; nexth = nexth->next)
|
|
snmp_log(LOG_ERR, " %s\n", nexth->handler_name);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
if (prevh) {
|
|
/* after prevh and before nexth */
|
|
prevh->next = handler;
|
|
handler2->next = nexth;
|
|
handler->prev = prevh;
|
|
nexth->prev = handler2;
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
/* else we're first, which is what we do next anyway so fall through */
|
|
}
|
|
handler2->next = reginfo->handler;
|
|
if (reginfo->handler)
|
|
reginfo->handler->prev = handler2;
|
|
reginfo->handler = handler;
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
/** Injects handler into the calling chain of handlers.
|
|
* The given MIB handler is put at the top of the list,
|
|
* and hence will be the handler to be called first.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*
|
|
* @see netsnmp_create_handler_registration()
|
|
* @see netsnmp_inject_handler_before()
|
|
*/
|
|
int
|
|
netsnmp_inject_handler(netsnmp_handler_registration *reginfo,
|
|
netsnmp_mib_handler *handler)
|
|
{
|
|
return netsnmp_inject_handler_before(reginfo, handler, NULL);
|
|
}
|
|
|
|
/** Calls a MIB handlers chain, starting with specific handler.
|
|
* The given arguments and MIB handler are checked
|
|
* for sanity, then the handlers are called, one by one,
|
|
* until next handler is NULL.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*/
|
|
NETSNMP_INLINE int
|
|
netsnmp_call_handler(netsnmp_mib_handler *next_handler,
|
|
netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
Netsnmp_Node_Handler *nh;
|
|
int ret;
|
|
|
|
if (next_handler == NULL || reginfo == NULL || reqinfo == NULL ||
|
|
requests == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_call_handler() called illegally\n");
|
|
netsnmp_assert(next_handler != NULL);
|
|
netsnmp_assert(reqinfo != NULL);
|
|
netsnmp_assert(reginfo != NULL);
|
|
netsnmp_assert(requests != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
do {
|
|
nh = next_handler->access_method;
|
|
if (!nh) {
|
|
if (next_handler->next) {
|
|
snmp_log(LOG_ERR, "no access method specified in handler %s.",
|
|
next_handler->handler_name);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
/*
|
|
* The final handler registration in the chain may well not need
|
|
* to include a handler routine, if the processing of this object
|
|
* is handled completely by the agent toolkit helpers.
|
|
*/
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
DEBUGMSGTL(("handler:calling", "calling handler %s for mode %s\n",
|
|
next_handler->handler_name,
|
|
se_find_label_in_slist("agent_mode", reqinfo->mode)));
|
|
|
|
/*
|
|
* XXX: define acceptable return statuses
|
|
*/
|
|
ret = (*nh) (next_handler, reginfo, reqinfo, requests);
|
|
|
|
DEBUGMSGTL(("handler:returned", "handler %s returned %d\n",
|
|
next_handler->handler_name, ret));
|
|
|
|
if (! (next_handler->flags & MIB_HANDLER_AUTO_NEXT))
|
|
break;
|
|
|
|
/*
|
|
* did handler signal that it didn't want auto next this time around?
|
|
*/
|
|
if(next_handler->flags & MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE) {
|
|
next_handler->flags &= ~MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
|
|
break;
|
|
}
|
|
|
|
next_handler = next_handler->next;
|
|
|
|
} while(next_handler);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** @private
|
|
* Calls all the MIB Handlers in registration struct for a given mode.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*/
|
|
int
|
|
netsnmp_call_handlers(netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
netsnmp_request_info *request;
|
|
int status;
|
|
|
|
if (reginfo == NULL || reqinfo == NULL || requests == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_call_handlers() called illegally\n");
|
|
netsnmp_assert(reqinfo != NULL);
|
|
netsnmp_assert(reginfo != NULL);
|
|
netsnmp_assert(requests != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
if (reginfo->handler == NULL) {
|
|
snmp_log(LOG_ERR, "no handler specified.");
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
switch (reqinfo->mode) {
|
|
case MODE_GETBULK:
|
|
case MODE_GET:
|
|
case MODE_GETNEXT:
|
|
if (!(reginfo->modes & HANDLER_CAN_GETANDGETNEXT))
|
|
return SNMP_ERR_NOERROR; /* legal */
|
|
break;
|
|
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
case MODE_SET_RESERVE1:
|
|
case MODE_SET_RESERVE2:
|
|
case MODE_SET_ACTION:
|
|
case MODE_SET_COMMIT:
|
|
case MODE_SET_FREE:
|
|
case MODE_SET_UNDO:
|
|
if (!(reginfo->modes & HANDLER_CAN_SET)) {
|
|
for (; requests; requests = requests->next) {
|
|
netsnmp_set_request_error(reqinfo, requests,
|
|
SNMP_ERR_NOTWRITABLE);
|
|
}
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
break;
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
default:
|
|
snmp_log(LOG_ERR, "unknown mode in netsnmp_call_handlers! bug!\n");
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
DEBUGMSGTL(("handler:calling", "main handler %s\n",
|
|
reginfo->handler->handler_name));
|
|
|
|
for (request = requests ; request; request = request->next) {
|
|
request->processed = 0;
|
|
}
|
|
|
|
status = netsnmp_call_handler(reginfo->handler, reginfo, reqinfo, requests);
|
|
|
|
return status;
|
|
}
|
|
|
|
/** @private
|
|
* Calls the next MIB handler in the chain, after the current one.
|
|
* The given arguments and MIB handler are checked
|
|
* for sanity, then the next handler is called.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*/
|
|
NETSNMP_INLINE int
|
|
netsnmp_call_next_handler(netsnmp_mib_handler *current,
|
|
netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
|
|
if (current == NULL || reginfo == NULL || reqinfo == NULL ||
|
|
requests == NULL) {
|
|
snmp_log(LOG_ERR, "netsnmp_call_next_handler() called illegally\n");
|
|
netsnmp_assert(current != NULL);
|
|
netsnmp_assert(reginfo != NULL);
|
|
netsnmp_assert(reqinfo != NULL);
|
|
netsnmp_assert(requests != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
return netsnmp_call_handler(current->next, reginfo, reqinfo, requests);
|
|
}
|
|
|
|
/** @private
|
|
* Calls the next MIB handler in the chain, after the current one.
|
|
* The given arguments and MIB handler are not validated before
|
|
* the call, only request is checked.
|
|
*
|
|
* @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
|
|
*/
|
|
netsnmp_feature_child_of(netsnmp_call_next_handler_one_request,netsnmp_unused)
|
|
#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST
|
|
NETSNMP_INLINE int
|
|
netsnmp_call_next_handler_one_request(netsnmp_mib_handler *current,
|
|
netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
netsnmp_request_info *request;
|
|
int ret;
|
|
|
|
if (!requests) {
|
|
snmp_log(LOG_ERR, "netsnmp_call_next_handler_ONE_REQUEST() called illegally\n");
|
|
netsnmp_assert(requests != NULL);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
request = requests->next;
|
|
requests->next = NULL;
|
|
ret = netsnmp_call_handler(current->next, reginfo, reqinfo, requests);
|
|
requests->next = request;
|
|
return ret;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST */
|
|
|
|
/** Deallocates resources associated with a given handler.
|
|
* The handler is removed from chain and then freed.
|
|
* After calling this function, the handler pointer is invalid
|
|
* and should be set to NULL.
|
|
*
|
|
* @param handler is the MIB Handler to be freed
|
|
*/
|
|
void
|
|
netsnmp_handler_free(netsnmp_mib_handler *handler)
|
|
{
|
|
if (handler != NULL) {
|
|
if (handler->next != NULL) {
|
|
/** make sure we aren't pointing to ourselves. */
|
|
netsnmp_assert(handler != handler->next); /* bugs caught: 1 */
|
|
netsnmp_handler_free(handler->next);
|
|
handler->next = NULL;
|
|
}
|
|
if ((handler->myvoid != NULL) && (handler->data_free != NULL))
|
|
{
|
|
handler->data_free(handler->myvoid);
|
|
}
|
|
SNMP_FREE(handler->handler_name);
|
|
SNMP_FREE(handler);
|
|
}
|
|
}
|
|
|
|
/** Duplicates a MIB handler and all subsequent handlers.
|
|
* Creates a copy of all data in given handlers chain.
|
|
*
|
|
* @param handler is the MIB Handler to be duplicated
|
|
*
|
|
* @return Returns a pointer to the complete copy,
|
|
* or NULL if any problem occured.
|
|
*
|
|
* @see _clone_handler()
|
|
*/
|
|
netsnmp_mib_handler *
|
|
netsnmp_handler_dup(netsnmp_mib_handler *handler)
|
|
{
|
|
netsnmp_mib_handler *h = NULL;
|
|
|
|
if (!handler)
|
|
goto err;
|
|
|
|
h = _clone_handler(handler);
|
|
if (!h)
|
|
goto err;
|
|
|
|
/*
|
|
* Providing a clone function without a free function is asking for
|
|
* memory leaks, and providing a free function without clone function
|
|
* is asking for memory corruption. Hence the log statement below.
|
|
*/
|
|
if (!!handler->data_clone != !!handler->data_free)
|
|
snmp_log(LOG_ERR, "data_clone / data_free inconsistent (%s)\n",
|
|
handler->handler_name);
|
|
if (handler->myvoid && handler->data_clone) {
|
|
h->myvoid = handler->data_clone(handler->myvoid);
|
|
if (!h->myvoid)
|
|
goto err;
|
|
} else
|
|
h->myvoid = handler->myvoid;
|
|
h->data_clone = handler->data_clone;
|
|
h->data_free = handler->data_free;
|
|
|
|
if (handler->next != NULL) {
|
|
h->next = netsnmp_handler_dup(handler->next);
|
|
if (!h->next)
|
|
goto err;
|
|
h->next->prev = h;
|
|
}
|
|
h->prev = NULL;
|
|
return h;
|
|
|
|
err:
|
|
netsnmp_handler_free(h);
|
|
return NULL;
|
|
}
|
|
|
|
/** Free resources associated with a handler registration object.
|
|
* The registration object and all MIB Handlers in the chain are
|
|
* freed. When the function ends, given pointer is no longer valid
|
|
* and should be set to NULL.
|
|
*
|
|
* @param reginfo is the handler registration object to be freed
|
|
*/
|
|
void
|
|
netsnmp_handler_registration_free(netsnmp_handler_registration *reginfo)
|
|
{
|
|
if (reginfo != NULL) {
|
|
netsnmp_handler_free(reginfo->handler);
|
|
SNMP_FREE(reginfo->handlerName);
|
|
SNMP_FREE(reginfo->contextName);
|
|
SNMP_FREE(reginfo->rootoid);
|
|
reginfo->rootoid_len = 0;
|
|
SNMP_FREE(reginfo);
|
|
}
|
|
}
|
|
|
|
/** Duplicates handler registration object and all subsequent handlers.
|
|
* Creates a copy of the handler registration object and all its data.
|
|
*
|
|
* @param reginfo is the handler registration object to be duplicated
|
|
*
|
|
* @return Returns a pointer to the complete copy,
|
|
* or NULL if any problem occured.
|
|
*
|
|
* @see netsnmp_handler_dup()
|
|
*/
|
|
netsnmp_handler_registration *
|
|
netsnmp_handler_registration_dup(netsnmp_handler_registration *reginfo)
|
|
{
|
|
netsnmp_handler_registration *r = NULL;
|
|
|
|
if (reginfo == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
r = (netsnmp_handler_registration *) calloc(1,
|
|
sizeof
|
|
(netsnmp_handler_registration));
|
|
|
|
if (r != NULL) {
|
|
r->modes = reginfo->modes;
|
|
r->priority = reginfo->priority;
|
|
r->range_subid = reginfo->range_subid;
|
|
r->timeout = reginfo->timeout;
|
|
r->range_ubound = reginfo->range_ubound;
|
|
r->rootoid_len = reginfo->rootoid_len;
|
|
|
|
if (reginfo->handlerName != NULL) {
|
|
r->handlerName = strdup(reginfo->handlerName);
|
|
if (r->handlerName == NULL) {
|
|
netsnmp_handler_registration_free(r);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (reginfo->contextName != NULL) {
|
|
r->contextName = strdup(reginfo->contextName);
|
|
if (r->contextName == NULL) {
|
|
netsnmp_handler_registration_free(r);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (reginfo->rootoid != NULL) {
|
|
r->rootoid =
|
|
snmp_duplicate_objid(reginfo->rootoid, reginfo->rootoid_len);
|
|
if (r->rootoid == NULL) {
|
|
netsnmp_handler_registration_free(r);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
r->handler = netsnmp_handler_dup(reginfo->handler);
|
|
if (r->handler == NULL) {
|
|
netsnmp_handler_registration_free(r);
|
|
return NULL;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** Creates a cache of information which can be saved for future reference.
|
|
* The cache is filled with pointers from parameters. Note that
|
|
* the input structures are not duplicated, but put directly into
|
|
* the new cache struct.
|
|
* Use netsnmp_handler_check_cache() later to make sure it's still
|
|
* valid before referencing it in the future.
|
|
*
|
|
* @see netsnmp_handler_check_cache()
|
|
* @see netsnmp_free_delegated_cache()
|
|
*/
|
|
NETSNMP_INLINE netsnmp_delegated_cache *
|
|
netsnmp_create_delegated_cache(netsnmp_mib_handler *handler,
|
|
netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests,
|
|
void *localinfo)
|
|
{
|
|
netsnmp_delegated_cache *ret;
|
|
|
|
ret = SNMP_MALLOC_TYPEDEF(netsnmp_delegated_cache);
|
|
if (ret) {
|
|
ret->transaction_id = reqinfo->asp->pdu->transid;
|
|
ret->handler = handler;
|
|
ret->reginfo = reginfo;
|
|
ret->reqinfo = reqinfo;
|
|
ret->requests = requests;
|
|
ret->localinfo = localinfo;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/** Check if a given delegated cache is still valid.
|
|
* The cache is valid if it's a part of transaction
|
|
* (ie, the agent still considers it to be an outstanding request).
|
|
*
|
|
* @param dcache is the delegated cache to be checked.
|
|
*
|
|
* @return Returns the cache pointer if it is still valid, NULL otherwise.
|
|
*
|
|
* @see netsnmp_create_delegated_cache()
|
|
* @see netsnmp_free_delegated_cache()
|
|
*/
|
|
NETSNMP_INLINE netsnmp_delegated_cache *
|
|
netsnmp_handler_check_cache(netsnmp_delegated_cache *dcache)
|
|
{
|
|
if (!dcache)
|
|
return dcache;
|
|
|
|
if (netsnmp_check_transaction_id(dcache->transaction_id) ==
|
|
SNMPERR_SUCCESS)
|
|
return dcache;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** Free a cache once it's no longer used.
|
|
* Deletes the data allocated by netsnmp_create_delegated_cache().
|
|
* Structures which were not allocated by netsnmp_create_delegated_cache()
|
|
* are not freed (pointers are dropped).
|
|
*
|
|
* @param dcache is the delegated cache to be freed.
|
|
*
|
|
* @see netsnmp_create_delegated_cache()
|
|
* @see netsnmp_handler_check_cache()
|
|
*/
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_delegated_cache(netsnmp_delegated_cache *dcache)
|
|
{
|
|
/*
|
|
* right now, no extra data is there that needs to be freed
|
|
*/
|
|
if (dcache)
|
|
SNMP_FREE(dcache);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED
|
|
/** Sets a list of requests as delegated or not delegated.
|
|
* Sweeps through given chain of requests and sets 'delegated'
|
|
* flag accordingly to the isdelegaded parameter.
|
|
*
|
|
* @param requests Request list.
|
|
* @param isdelegated New value of the 'delegated' flag.
|
|
*/
|
|
void
|
|
netsnmp_handler_mark_requests_as_delegated(netsnmp_request_info *requests,
|
|
int isdelegated)
|
|
{
|
|
while (requests) {
|
|
requests->delegated = isdelegated;
|
|
requests = requests->next;
|
|
}
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED */
|
|
|
|
/** Adds data from node list to the request information structure.
|
|
* Data in the request can be later extracted and used by submodules.
|
|
*
|
|
* @param request Destination request information structure.
|
|
*
|
|
* @param node The data to be added to the linked list
|
|
* request->parent_data.
|
|
*
|
|
* @see netsnmp_request_remove_list_data()
|
|
* @see netsnmp_request_get_list_data()
|
|
*/
|
|
NETSNMP_INLINE void
|
|
netsnmp_request_add_list_data(netsnmp_request_info *request,
|
|
netsnmp_data_list *node)
|
|
{
|
|
if (request) {
|
|
if (request->parent_data)
|
|
netsnmp_add_list_data(&request->parent_data, node);
|
|
else
|
|
request->parent_data = node;
|
|
}
|
|
}
|
|
|
|
/** Removes all data from a request.
|
|
*
|
|
* @param request the netsnmp request info structure
|
|
*
|
|
* @param name this is the name of the previously added data
|
|
*
|
|
* @return 0 on successful find-and-delete, 1 otherwise.
|
|
*
|
|
* @see netsnmp_request_add_list_data()
|
|
* @see netsnmp_request_get_list_data()
|
|
*/
|
|
NETSNMP_INLINE int
|
|
netsnmp_request_remove_list_data(netsnmp_request_info *request,
|
|
const char *name)
|
|
{
|
|
if ((NULL == request) || (NULL ==request->parent_data))
|
|
return 1;
|
|
|
|
return netsnmp_remove_list_node(&request->parent_data, name);
|
|
}
|
|
|
|
/** Extracts data from a request.
|
|
* Retrieves data that was previously added to the request,
|
|
* usually by a parent module.
|
|
*
|
|
* @param request Source request information structure.
|
|
*
|
|
* @param name Used to compare against the request->parent_data->name value;
|
|
* if a match is found, request->parent_data->data is returned.
|
|
*
|
|
* @return Gives a void pointer(request->parent_data->data); NULL is
|
|
* returned if source data is NULL or the object is not found.
|
|
*
|
|
* @see netsnmp_request_add_list_data()
|
|
* @see netsnmp_request_remove_list_data()
|
|
*/
|
|
void *
|
|
netsnmp_request_get_list_data(netsnmp_request_info *request,
|
|
const char *name)
|
|
{
|
|
if (request)
|
|
return netsnmp_get_list_data(request->parent_data, name);
|
|
return NULL;
|
|
}
|
|
|
|
/** Free the extra data stored in a request.
|
|
* Deletes the data in given request only. Other chain items
|
|
* are unaffected.
|
|
*
|
|
* @param request Request information structure to be modified.
|
|
*
|
|
* @see netsnmp_request_add_list_data()
|
|
* @see netsnmp_free_list_data()
|
|
*/
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_request_data_set(netsnmp_request_info *request)
|
|
{
|
|
if (request)
|
|
netsnmp_free_list_data(request->parent_data);
|
|
}
|
|
|
|
/** Free the extra data stored in a bunch of requests.
|
|
* Deletes all data in the chain inside request.
|
|
*
|
|
* @param request Request information structure to be modified.
|
|
*
|
|
* @see netsnmp_request_add_list_data()
|
|
* @see netsnmp_free_request_data_set()
|
|
*/
|
|
NETSNMP_INLINE void
|
|
netsnmp_free_request_data_sets(netsnmp_request_info *request)
|
|
{
|
|
if (request && request->parent_data) {
|
|
netsnmp_free_all_list_data(request->parent_data);
|
|
request->parent_data = NULL;
|
|
}
|
|
}
|
|
|
|
/** Returns a MIB handler from a chain based on the name.
|
|
*
|
|
* @param reginfo Handler registration struct which contains the chain.
|
|
*
|
|
* @param name Target MIB Handler name string. The name is case
|
|
* sensitive.
|
|
*
|
|
* @return The MIB Handler is returned, or NULL if not found.
|
|
*
|
|
* @see netsnmp_request_add_list_data()
|
|
*/
|
|
netsnmp_mib_handler *
|
|
netsnmp_find_handler_by_name(netsnmp_handler_registration *reginfo,
|
|
const char *name)
|
|
{
|
|
netsnmp_mib_handler *it;
|
|
if (reginfo == NULL || name == NULL )
|
|
return NULL;
|
|
for (it = reginfo->handler; it; it = it->next) {
|
|
if (strcmp(it->handler_name, name) == 0) {
|
|
return it;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/** Returns a handler's void pointer from a chain based on the name.
|
|
*
|
|
* @warning The void pointer data may change as a handler evolves.
|
|
* Handlers should really advertise some function for you
|
|
* to use instead.
|
|
*
|
|
* @param reginfo Handler registration struct which contains the chain.
|
|
*
|
|
* @param name Target MIB Handler name string. The name is case
|
|
* sensitive.
|
|
*
|
|
* @return The MIB Handler's void * pointer is returned,
|
|
* or NULL if the handler is not found.
|
|
*
|
|
* @see netsnmp_find_handler_by_name()
|
|
*/
|
|
void *
|
|
netsnmp_find_handler_data_by_name(netsnmp_handler_registration *reginfo,
|
|
const char *name)
|
|
{
|
|
netsnmp_mib_handler *it = netsnmp_find_handler_by_name(reginfo, name);
|
|
if (it)
|
|
return it->myvoid;
|
|
return NULL;
|
|
}
|
|
|
|
/** @private
|
|
* Clones a MIB Handler with its basic properties.
|
|
* Creates a copy of the given MIB Handler. Copies name, flags and
|
|
* access methods only; not myvoid.
|
|
*
|
|
* @see netsnmp_handler_dup()
|
|
*/
|
|
static netsnmp_mib_handler *
|
|
_clone_handler(netsnmp_mib_handler *it)
|
|
{
|
|
netsnmp_mib_handler *dup;
|
|
|
|
if(NULL == it)
|
|
return NULL;
|
|
|
|
dup = netsnmp_create_handler(it->handler_name, it->access_method);
|
|
if(NULL != dup)
|
|
dup->flags = it->flags;
|
|
|
|
return dup;
|
|
}
|
|
|
|
static netsnmp_data_list *handler_reg = NULL;
|
|
|
|
void
|
|
handler_free_callback(void *handler)
|
|
{
|
|
netsnmp_handler_free((netsnmp_mib_handler *)handler);
|
|
}
|
|
|
|
/** Registers a given handler by name, so that it can be found easily later.
|
|
* Pointer to the handler is put into a list where it can be easily located
|
|
* at any time.
|
|
*
|
|
* @param name Name string to be associated with the handler.
|
|
*
|
|
* @param handler Pointer the MIB Handler.
|
|
*
|
|
* @see netsnmp_clear_handler_list()
|
|
*/
|
|
void
|
|
netsnmp_register_handler_by_name(const char *name,
|
|
netsnmp_mib_handler *handler)
|
|
{
|
|
netsnmp_add_list_data(&handler_reg,
|
|
netsnmp_create_data_list(name, (void *) handler,
|
|
handler_free_callback));
|
|
DEBUGMSGTL(("handler_registry", "registering helper %s\n", name));
|
|
}
|
|
|
|
/** Clears the entire MIB Handlers registration list.
|
|
* MIB Handlers registration list is used to access any MIB Handler by
|
|
* its name. The function frees the list memory and sets pointer to NULL.
|
|
* Instead of calling this function directly, use shutdown_agent().
|
|
*
|
|
* @see shutdown_agent()
|
|
* @see netsnmp_register_handler_by_name()
|
|
*/
|
|
void
|
|
netsnmp_clear_handler_list(void)
|
|
{
|
|
DEBUGMSGTL(("agent_handler", "netsnmp_clear_handler_list() called\n"));
|
|
netsnmp_free_all_list_data(handler_reg);
|
|
handler_reg = NULL;
|
|
}
|
|
|
|
/** @private
|
|
* Injects a handler into a subtree, peers and children when a given
|
|
* subtrees name matches a passed in name.
|
|
*/
|
|
void
|
|
netsnmp_inject_handler_into_subtree(netsnmp_subtree *tp, const char *name,
|
|
netsnmp_mib_handler *handler,
|
|
const char *before_what)
|
|
{
|
|
netsnmp_subtree *tptr;
|
|
netsnmp_mib_handler *mh;
|
|
|
|
for (tptr = tp; tptr != NULL; tptr = tptr->next) {
|
|
/* if (tptr->children) {
|
|
netsnmp_inject_handler_into_subtree(tptr->children,name,handler);
|
|
} */
|
|
if (strcmp(tptr->label_a, name) == 0) {
|
|
DEBUGMSGTL(("injectHandler", "injecting handler %s into %s\n",
|
|
handler->handler_name, tptr->label_a));
|
|
netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler),
|
|
before_what);
|
|
} else if (tptr->reginfo != NULL &&
|
|
tptr->reginfo->handlerName != NULL &&
|
|
strcmp(tptr->reginfo->handlerName, name) == 0) {
|
|
DEBUGMSGTL(("injectHandler", "injecting handler into %s/%s\n",
|
|
tptr->label_a, tptr->reginfo->handlerName));
|
|
netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler),
|
|
before_what);
|
|
} else if (tptr->reginfo != NULL) {
|
|
for (mh = tptr->reginfo->handler; mh != NULL; mh = mh->next) {
|
|
if (mh->handler_name && strcmp(mh->handler_name, name) == 0) {
|
|
DEBUGMSGTL(("injectHandler", "injecting handler into %s\n",
|
|
tptr->label_a));
|
|
netsnmp_inject_handler_before(tptr->reginfo,
|
|
_clone_handler(handler),
|
|
before_what);
|
|
break;
|
|
} else {
|
|
DEBUGMSGTL(("injectHandler",
|
|
"not injecting handler into %s\n",
|
|
mh->handler_name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int doneit = 0;
|
|
/** @private
|
|
* Parses the "injectHandler" token line.
|
|
*/
|
|
void
|
|
parse_injectHandler_conf(const char *token, char *cptr)
|
|
{
|
|
char handler_to_insert[256], reg_name[256];
|
|
subtree_context_cache *stc;
|
|
netsnmp_mib_handler *handler;
|
|
|
|
/*
|
|
* XXXWWW: ensure instead that handler isn't inserted twice
|
|
*/
|
|
if (doneit) /* we only do this once without restart the agent */
|
|
return;
|
|
|
|
cptr = copy_nword(cptr, handler_to_insert, sizeof(handler_to_insert));
|
|
handler = (netsnmp_mib_handler*)netsnmp_get_list_data(handler_reg, handler_to_insert);
|
|
if (!handler) {
|
|
netsnmp_config_error("no \"%s\" handler registered.",
|
|
handler_to_insert);
|
|
return;
|
|
}
|
|
|
|
if (!cptr) {
|
|
config_perror("no INTONAME specified. Can't do insertion.");
|
|
return;
|
|
}
|
|
cptr = copy_nword(cptr, reg_name, sizeof(reg_name));
|
|
|
|
for (stc = get_top_context_cache(); stc; stc = stc->next) {
|
|
DEBUGMSGTL(("injectHandler", "Checking context tree %s (before=%s)\n",
|
|
stc->context_name, (cptr)?cptr:"null"));
|
|
netsnmp_inject_handler_into_subtree(stc->first_subtree, reg_name,
|
|
handler, cptr);
|
|
}
|
|
}
|
|
|
|
/** @private
|
|
* Callback to ensure injectHandler parser doesn't do things twice.
|
|
* @todo replace this with a method to check the handler chain instead.
|
|
*/
|
|
static int
|
|
handler_mark_inject_handler_done(int majorID, int minorID,
|
|
void *serverarg, void *clientarg)
|
|
{
|
|
doneit = 1;
|
|
return 0;
|
|
}
|
|
|
|
/** @private
|
|
* Registers the injectHandle parser token.
|
|
* Used in init_agent_read_config().
|
|
*
|
|
* @see init_agent_read_config()
|
|
*/
|
|
void
|
|
netsnmp_init_handler_conf(void)
|
|
{
|
|
snmpd_register_config_handler("injectHandler",
|
|
parse_injectHandler_conf,
|
|
NULL, "injectHandler NAME INTONAME [BEFORE_OTHER_NAME]");
|
|
snmp_register_callback(SNMP_CALLBACK_LIBRARY,
|
|
SNMP_CALLBACK_POST_READ_CONFIG,
|
|
handler_mark_inject_handler_done, NULL);
|
|
|
|
se_add_pair_to_slist("agent_mode", strdup("GET"), MODE_GET);
|
|
se_add_pair_to_slist("agent_mode", strdup("GETNEXT"), MODE_GETNEXT);
|
|
se_add_pair_to_slist("agent_mode", strdup("GETBULK"), MODE_GETBULK);
|
|
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_BEGIN"),
|
|
MODE_SET_BEGIN);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE1"),
|
|
MODE_SET_RESERVE1);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE2"),
|
|
MODE_SET_RESERVE2);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_ACTION"),
|
|
MODE_SET_ACTION);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_COMMIT"),
|
|
MODE_SET_COMMIT);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_FREE"), MODE_SET_FREE);
|
|
se_add_pair_to_slist("agent_mode", strdup("SET_UNDO"), MODE_SET_UNDO);
|
|
|
|
se_add_pair_to_slist("babystep_mode", strdup("pre-request"),
|
|
MODE_BSTEP_PRE_REQUEST);
|
|
se_add_pair_to_slist("babystep_mode", strdup("object_lookup"),
|
|
MODE_BSTEP_OBJECT_LOOKUP);
|
|
se_add_pair_to_slist("babystep_mode", strdup("check_value"),
|
|
MODE_BSTEP_CHECK_VALUE);
|
|
se_add_pair_to_slist("babystep_mode", strdup("row_create"),
|
|
MODE_BSTEP_ROW_CREATE);
|
|
se_add_pair_to_slist("babystep_mode", strdup("undo_setup"),
|
|
MODE_BSTEP_UNDO_SETUP);
|
|
se_add_pair_to_slist("babystep_mode", strdup("set_value"),
|
|
MODE_BSTEP_SET_VALUE);
|
|
se_add_pair_to_slist("babystep_mode", strdup("check_consistency"),
|
|
MODE_BSTEP_CHECK_CONSISTENCY);
|
|
se_add_pair_to_slist("babystep_mode", strdup("undo_set"),
|
|
MODE_BSTEP_UNDO_SET);
|
|
se_add_pair_to_slist("babystep_mode", strdup("commit"),
|
|
MODE_BSTEP_COMMIT);
|
|
se_add_pair_to_slist("babystep_mode", strdup("undo_commit"),
|
|
MODE_BSTEP_UNDO_COMMIT);
|
|
se_add_pair_to_slist("babystep_mode", strdup("irreversible_commit"),
|
|
MODE_BSTEP_IRREVERSIBLE_COMMIT);
|
|
se_add_pair_to_slist("babystep_mode", strdup("undo_cleanup"),
|
|
MODE_BSTEP_UNDO_CLEANUP);
|
|
se_add_pair_to_slist("babystep_mode", strdup("post_request"),
|
|
MODE_BSTEP_POST_REQUEST);
|
|
se_add_pair_to_slist("babystep_mode", strdup("original"), 0xffff);
|
|
#endif /* NETSNMP_NO_WRITE_SUPPORT */
|
|
|
|
/*
|
|
* xxx-rks: hmmm.. will this work for modes which are or'd together?
|
|
* I'm betting not...
|
|
*/
|
|
se_add_pair_to_slist("handler_can_mode", strdup("GET/GETNEXT"),
|
|
HANDLER_CAN_GETANDGETNEXT);
|
|
se_add_pair_to_slist("handler_can_mode", strdup("SET"),
|
|
HANDLER_CAN_SET);
|
|
se_add_pair_to_slist("handler_can_mode", strdup("GETBULK"),
|
|
HANDLER_CAN_GETBULK);
|
|
se_add_pair_to_slist("handler_can_mode", strdup("BABY_STEP"),
|
|
HANDLER_CAN_BABY_STEP);
|
|
}
|
|
|
|
/** @} */
|