blob: d1b45381ff924137fc88f29f75b1adc7cdcf1df1 [file] [log] [blame]
#include <net-snmp/net-snmp-config.h>
#include <stdio.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/types.h>
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_STDARG_H
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#if HAVE_WINSOCK_H
#include <winsock.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <net-snmp/types.h>
#include <net-snmp/output_api.h>
#include <net-snmp/library/snmp_debug.h> /* For this file's "internal" definitions */
#include <net-snmp/config_api.h>
#include <net-snmp/utilities.h>
#include <net-snmp/library/mib.h>
#include <net-snmp/library/snmp_api.h>
#define SNMP_DEBUG_DISABLED 0
#define SNMP_DEBUG_ACTIVE 1
#define SNMP_DEBUG_EXCLUDED 2
static int dodebug = NETSNMP_ALWAYS_DEBUG;
int debug_num_tokens = 0;
int debug_num_excluded = 0;
static int debug_print_everything = 0;
netsnmp_token_descr dbg_tokens[MAX_DEBUG_TOKENS];
#ifdef NETSNMP_DEBUG_STATS
netsnmp_container *dbg_stats = NULL;
static int _debug_cmp( const void *lhs, const void *rhs );
static int _save_debug_stat(netsnmp_token_descr *tb, void *type);
static int _debug_stats_callback(int majorID, int minorID,
void *serverarg, void *clientarg);
#endif
/*
* indent debugging: provide a space padded section to return an indent for
*/
static int debugindent = 0;
#define INDENTMAX 80
static char debugindentchars[] =
" ";
char *
debug_indent(void)
{
return debugindentchars;
}
void
debug_indent_add(int amount)
{
if (debugindent + amount >= 0 && debugindent + amount < 80) {
debugindentchars[debugindent] = ' ';
debugindent += amount;
debugindentchars[debugindent] = '\0';
}
}
void
debug_config_register_tokens(const char *configtoken, char *tokens)
{
debug_register_tokens(tokens);
}
void
debug_config_turn_on_debugging(const char *configtoken, char *line)
{
snmp_set_do_debugging(atoi(line));
}
void
snmp_debug_init(void)
{
debugindentchars[0] = '\0'; /* zero out the debugging indent array. */
/*
* Hmmm....
* this "init" routine seems to be called *after* processing
* the command line options. So we can't clear the debug
* token array here, and will just have to rely on it being
* initialised to 0 automatically.
* So much for trying to program responsibly :-)
*/
/* memset(dbg_tokens, 0, MAX_DEBUG_TOKENS*sizeof(struct token_dscr)); */
register_prenetsnmp_mib_handler("snmp", "doDebugging",
debug_config_turn_on_debugging, NULL,
"(1|0)");
register_prenetsnmp_mib_handler("snmp", "debugTokens",
debug_config_register_tokens, NULL,
"token[,token...]");
#ifdef NETSNMP_DEBUG_STATS
/*
* debug stats
*/
dbg_stats = netsnmp_container_find("debug_exclude:table_container");
if (NULL != dbg_stats) {
dbg_stats->compare = _debug_cmp;
netsnmp_register_callback(SNMP_CALLBACK_LIBRARY,
SNMP_CALLBACK_STORE_DATA,
_debug_stats_callback, dbg_stats, 1024);
}
#endif
}
void
debug_register_tokens(char *tokens)
{
char *newp, *cp;
char *st = NULL;
int status;
if (tokens == 0 || *tokens == 0)
return;
newp = strdup(tokens); /* strtok_r messes it up */
cp = strtok_r(newp, DEBUG_TOKEN_DELIMITER, &st);
while (cp) {
if (strlen(cp) < MAX_DEBUG_TOKEN_LEN) {
if (strcasecmp(cp, DEBUG_ALWAYS_TOKEN) == 0) {
debug_print_everything = 1;
} else if (debug_num_tokens < MAX_DEBUG_TOKENS) {
if ('-' == *cp) {
++cp;
status = SNMP_DEBUG_EXCLUDED;
}
else
status = SNMP_DEBUG_ACTIVE;
dbg_tokens[debug_num_tokens].token_name = strdup(cp);
dbg_tokens[debug_num_tokens++].enabled = status;
snmp_log(LOG_NOTICE, "registered debug token %s, %d\n", cp, status);
} else {
snmp_log(LOG_NOTICE, "Unable to register debug token %s\n", cp);
}
} else {
snmp_log(LOG_NOTICE, "Debug token %s over length\n", cp);
}
cp = strtok_r(NULL, DEBUG_TOKEN_DELIMITER, &st);
}
free(newp);
}
/*
* Print all registered tokens along with their current status
*/
void
debug_print_registered_tokens(void) {
int i;
snmp_log(LOG_INFO, "%d tokens registered :\n", debug_num_tokens);
for (i=0; i<debug_num_tokens; i++) {
snmp_log( LOG_INFO, "%d) %s : %d\n",
i, dbg_tokens [i].token_name, dbg_tokens [i].enabled);
}
}
/*
* Enable logs on a given token
*/
int
debug_enable_token_logs (const char *token) {
int i;
/* debugging flag is on or off */
if (!dodebug)
return SNMPERR_GENERR;
if (debug_num_tokens == 0 || debug_print_everything) {
/* no tokens specified, print everything */
return SNMPERR_SUCCESS;
} else {
for(i=0; i < debug_num_tokens; i++) {
if (dbg_tokens[i].token_name &&
strncmp(dbg_tokens[i].token_name, token,
strlen(dbg_tokens[i].token_name)) == 0) {
dbg_tokens[i].enabled = SNMP_DEBUG_ACTIVE;
return SNMPERR_SUCCESS;
}
}
}
return SNMPERR_GENERR;
}
/*
* Diable logs on a given token
*/
int
debug_disable_token_logs (const char *token) {
int i;
/* debugging flag is on or off */
if (!dodebug)
return SNMPERR_GENERR;
if (debug_num_tokens == 0 || debug_print_everything) {
/* no tokens specified, print everything */
return SNMPERR_SUCCESS;
} else {
for(i=0; i < debug_num_tokens; i++) {
if (strncmp(dbg_tokens[i].token_name, token,
strlen(dbg_tokens[i].token_name)) == 0) {
dbg_tokens[i].enabled = SNMP_DEBUG_DISABLED;
return SNMPERR_SUCCESS;
}
}
}
return SNMPERR_GENERR;
}
/*
* debug_is_token_registered(char *TOKEN):
*
* returns SNMPERR_SUCCESS
* or SNMPERR_GENERR
*
* if TOKEN has been registered and debugging support is turned on.
*/
int
debug_is_token_registered(const char *token)
{
int i, rc;
/*
* debugging flag is on or off
*/
if (!dodebug)
return SNMPERR_GENERR;
if (debug_num_tokens == 0 || debug_print_everything) {
/*
* no tokens specified, print everything
* (unless something might be excluded)
*/
if (debug_num_excluded) {
rc = SNMPERR_SUCCESS; /* ! found = success */
} else {
return SNMPERR_SUCCESS;
}
}
else
rc = SNMPERR_GENERR; /* ! found = err */
for (i = 0; i < debug_num_tokens; i++) {
if (SNMP_DEBUG_DISABLED == dbg_tokens[i].enabled)
continue;
if (dbg_tokens[i].token_name &&
strncmp(dbg_tokens[i].token_name, token,
strlen(dbg_tokens[i].token_name)) == 0) {
if (SNMP_DEBUG_ACTIVE == dbg_tokens[i].enabled)
return SNMPERR_SUCCESS; /* active */
else
return SNMPERR_GENERR; /* excluded */
}
}
#ifdef NETSNMP_DEBUG_STATS
if ((SNMPERR_SUCCESS == rc) && (NULL != dbg_stats)) {
netsnmp_token_descr td, *found;
td.token_name = token;
found = CONTAINER_FIND(dbg_stats, &td);
if (NULL == found) {
found = SNMP_MALLOC_TYPEDEF(netsnmp_token_descr);
netsnmp_assert(NULL != found);
found->token_name = strdup(token);
netsnmp_assert(0 == found->enabled);
CONTAINER_INSERT(dbg_stats, found);
}
++found->enabled;
/* snmp_log(LOG_ERR,"tok %s, %d hits\n", token, found->enabled); */
}
#endif
return rc;
}
void
#if HAVE_STDARG_H
debugmsg(const char *token, const char *format, ...)
#else
debugmsg(va_alist)
va_dcl
#endif
{
va_list debugargs;
#if HAVE_STDARG_H
va_start(debugargs, format);
#else
const char *format;
const char *token;
va_start(debugargs);
token = va_arg(debugargs, const char *);
format = va_arg(debugargs, const char *); /* ??? */
#endif
if (debug_is_token_registered(token) == SNMPERR_SUCCESS) {
snmp_vlog(LOG_DEBUG, format, debugargs);
}
va_end(debugargs);
}
void
debugmsg_oid(const char *token, const oid * theoid, size_t len)
{
u_char *buf = NULL;
size_t buf_len = 0, out_len = 0;
if (sprint_realloc_objid(&buf, &buf_len, &out_len, 1, theoid, len)) {
if (buf != NULL) {
debugmsg(token, "%s", buf);
}
} else {
if (buf != NULL) {
debugmsg(token, "%s [TRUNCATED]", buf);
}
}
if (buf != NULL) {
free(buf);
}
}
void
debugmsg_suboid(const char *token, const oid * theoid, size_t len)
{
u_char *buf = NULL;
size_t buf_len = 0, out_len = 0;
int buf_overflow = 0;
netsnmp_sprint_realloc_objid(&buf, &buf_len, &out_len, 1,
&buf_overflow, theoid, len);
if(buf_overflow) {
if (buf != NULL) {
debugmsg(token, "%s [TRUNCATED]", buf);
}
} else {
if (buf != NULL) {
debugmsg(token, "%s", buf);
}
}
if (buf != NULL) {
free(buf);
}
}
void
debugmsg_var(const char *token, netsnmp_variable_list * var)
{
u_char *buf = NULL;
size_t buf_len = 0, out_len = 0;
if (var == NULL || token == NULL) {
return;
}
if (sprint_realloc_variable(&buf, &buf_len, &out_len, 1,
var->name, var->name_length, var)) {
if (buf != NULL) {
debugmsg(token, "%s", buf);
}
} else {
if (buf != NULL) {
debugmsg(token, "%s [TRUNCATED]", buf);
}
}
if (buf != NULL) {
free(buf);
}
}
void
debugmsg_oidrange(const char *token, const oid * theoid, size_t len,
size_t var_subid, oid range_ubound)
{
u_char *buf = NULL;
size_t buf_len = 0, out_len = 0, i = 0;
int rc = 0;
if (var_subid == 0) {
rc = sprint_realloc_objid(&buf, &buf_len, &out_len, 1, theoid,
len);
} else {
char tmpbuf[128];
/* XXX - ? check for 0 == var_subid -1 ? */
rc = sprint_realloc_objid(&buf, &buf_len, &out_len, 1, theoid,
var_subid-1); /* Adjust for C's 0-based array indexing */
if (rc) {
sprintf(tmpbuf, ".%lu--%lu", theoid[var_subid - 1],
range_ubound);
rc = snmp_cstrcat(&buf, &buf_len, &out_len, 1, tmpbuf);
if (rc) {
for (i = var_subid; i < len; i++) {
sprintf(tmpbuf, ".%lu", theoid[i]);
if (!snmp_cstrcat(&buf, &buf_len, &out_len, 1, tmpbuf)) {
break;
}
}
}
}
}
if (buf != NULL) {
debugmsg(token, "%s%s", buf, rc ? "" : " [TRUNCATED]");
free(buf);
}
}
void
debugmsg_hex(const char *token, u_char * thedata, size_t len)
{
u_char *buf = NULL;
size_t buf_len = 0, out_len = 0;
if (sprint_realloc_hexstring
(&buf, &buf_len, &out_len, 1, thedata, len)) {
if (buf != NULL) {
debugmsg(token, "%s", buf);
}
} else {
if (buf != NULL) {
debugmsg(token, "%s [TRUNCATED]", buf);
}
}
if (buf != NULL) {
free(buf);
}
}
void
debugmsg_hextli(const char *token, u_char * thedata, size_t len)
{
char buf[SPRINT_MAX_LEN], token2[SPRINT_MAX_LEN];
u_char *b3 = NULL;
size_t b3_len = 0, o3_len = 0;
int incr;
sprintf(token2, "dumpx_%s", token);
/*
* XX tracing lines removed from this function DEBUGTRACE;
*/
DEBUGIF(token2) {
for (incr = 16; len > 0; len -= incr, thedata += incr) {
if ((int) len < incr) {
incr = len;
}
/*
* XXnext two lines were DEBUGPRINTINDENT(token);
*/
sprintf(buf, "dumpx%s", token);
debugmsg(buf, "%s: %s", token2, debug_indent());
if (sprint_realloc_hexstring
(&b3, &b3_len, &o3_len, 1, thedata, incr)) {
if (b3 != NULL) {
debugmsg(token2, "%s", b3);
}
} else {
if (b3 != NULL) {
debugmsg(token2, "%s [TRUNCATED]", b3);
}
}
o3_len = 0;
}
}
if (b3 != NULL) {
free(b3);
}
}
void
#if HAVE_STDARG_H
debugmsgtoken(const char *token, const char *format, ...)
#else
debugmsgtoken(va_alist)
va_dcl
#endif
{
va_list debugargs;
#if HAVE_STDARG_H
va_start(debugargs, format);
#else
const char *token;
va_start(debugargs);
token = va_arg(debugargs, const char *);
#endif
debugmsg(token, "%s: ", token);
va_end(debugargs);
}
void
#if HAVE_STDARG_H
debug_combo_nc(const char *token, const char *format, ...)
#else
debug_combo_nc(va_alist)
va_dcl
#endif
{
va_list debugargs;
#if HAVE_STDARG_H
va_start(debugargs, format);
#else
const char *format;
const char *token;
va_start(debugargs);
token = va_arg(debugargs, const char *);
format = va_arg(debugargs, const char *); /* ??? */
#endif
snmp_log(LOG_DEBUG, "%s: ", token);
snmp_vlog(LOG_DEBUG, format, debugargs);
va_end(debugargs);
}
/*
* for speed, these shouldn't be in default_storage space
*/
void
snmp_set_do_debugging(int val)
{
dodebug = val;
}
int
snmp_get_do_debugging(void)
{
return dodebug;
}
#ifdef NETSNMP_DEBUG_STATS
/************************************************************
* compare two context pointers here. Return -1 if lhs < rhs,
* 0 if lhs == rhs, and 1 if lhs > rhs.
*/
static int
_debug_cmp( const void *lhs, const void *rhs )
{
netsnmp_token_descr *dbg_l = (netsnmp_token_descr *)lhs;
netsnmp_token_descr *dbg_r = (netsnmp_token_descr *)rhs;
/* snmp_log(LOG_ERR,"%s/%s\n",dbg_l->token_name, dbg_r->token_name); */
return strcmp(dbg_l->token_name, dbg_r->token_name);
}
static int
_save_debug_stat(netsnmp_token_descr *tb, void *type)
{
char buf[256];
snprintf(buf, sizeof(buf), "debug_hits %s %d",
tb->token_name, tb->enabled);
read_config_store((char *) type, buf);
return SNMP_ERR_NOERROR;
}
static int
_debug_stats_callback(int majorID, int minorID,
void *serverarg, void *clientarg)
{
char sep[] =
"##############################################################";
char buf[] =
"#\n" "# debug stats\n" "#";
char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_APPTYPE);
read_config_store((char *) type, sep);
read_config_store((char *) type, buf);
/*
* save all rows
*/
CONTAINER_FOR_EACH((netsnmp_container *) clientarg,
(netsnmp_container_obj_func *)
_save_debug_stat, type);
read_config_store((char *) type, sep);
read_config_store((char *) type, "\n");
/*
* never fails
*/
return SNMPERR_SUCCESS;
}
#endif