/* * logging.c - generic logging for snmp-agent * * Contributed by Ragnar Kjørstad, ucd@ragnark.vestdata.no 1999-06-26 */ /* 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 snmp_logging generic logging for net-snmp * @ingroup library * * @{ */ #include #include #include #if HAVE_MALLOC_H #include #endif #if HAVE_STRING_H #include #else #include #endif #include #if HAVE_STDLIB_H #include #endif #include #include #include #include #if HAVE_SYSLOG_H #include #ifndef LOG_CONS /* Interesting Ultrix feature */ #include #endif #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_NETINET_IN_H #include #endif #include #if HAVE_UNISTD_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include /* For this file's "internal" definitions */ #include #include #include #ifdef va_copy #define NEED_VA_END_AFTER_VA_COPY #else #ifdef __vacopy #define vacopy __vacopy #define NEED_VA_END_AFTER_VA_COPY #else #define va_copy(dest, src) memcpy (&dest, &src, sizeof (va_list)) #endif #endif #ifndef HAVE_VSNPRINTF #include "snprintf.h" #endif netsnmp_feature_child_of(logging_all, libnetsnmp) netsnmp_feature_child_of(logging_outputs, logging_all) netsnmp_feature_child_of(logging_file, logging_outputs) netsnmp_feature_child_of(logging_stdio, logging_outputs) netsnmp_feature_child_of(logging_syslog, logging_outputs) netsnmp_feature_child_of(logging_external, logging_all) netsnmp_feature_child_of(enable_stderrlog, logging_all) netsnmp_feature_child_of(logging_enable_calllog, netsnmp_unused) netsnmp_feature_child_of(logging_enable_loghandler, netsnmp_unused) /* default to the file/stdio/syslog set */ netsnmp_feature_want(logging_outputs) /* * logh_head: A list of all log handlers, in increasing order of priority * logh_priorities: 'Indexes' into this list, by priority */ netsnmp_log_handler *logh_head = NULL; netsnmp_log_handler *logh_priorities[LOG_DEBUG+1]; static int logh_enabled = 0; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG static char syslogname[64] = DEFAULT_LOG_ID; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO netsnmp_log_handler * netsnmp_register_stdio_loghandler(int is_stdout, int priority, int priority_max, const char *tok); #endif #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE netsnmp_log_handler * netsnmp_register_filelog_handler(const char* logfilename, int priority, int priority_max, int dont_zero_log); #endif void netsnmp_disable_this_loghandler(netsnmp_log_handler *logh) { if (!logh || (0 == logh->enabled)) return; logh->enabled = 0; --logh_enabled; netsnmp_assert(logh_enabled >= 0); } void netsnmp_enable_this_loghandler(netsnmp_log_handler *logh) { if (!logh || (0 != logh->enabled)) return; logh->enabled = 1; ++logh_enabled; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE void netsnmp_enable_filelog(netsnmp_log_handler *logh, int dont_zero_log); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ void parse_config_logOption(const char *token, char *cptr) { int my_argc = 0 ; char **my_argv = NULL; snmp_log_options( cptr, my_argc, my_argv ); } void init_snmp_logging(void) { netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "logTimestamp", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_LOG_TIMESTAMP); register_prenetsnmp_mib_handler("snmp", "logOption", parse_config_logOption, NULL, "string"); } void shutdown_snmp_logging(void) { snmp_disable_log(); while(NULL != logh_head) netsnmp_remove_loghandler( logh_head ); } /* * These definitions handle 4.2 systems without additional syslog facilities. */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifndef LOG_CONS #define LOG_CONS 0 /* Don't bother if not defined... */ #endif #ifndef LOG_PID #define LOG_PID 0 /* Don't bother if not defined... */ #endif #ifndef LOG_LOCAL0 #define LOG_LOCAL0 0 #endif #ifndef LOG_LOCAL1 #define LOG_LOCAL1 0 #endif #ifndef LOG_LOCAL2 #define LOG_LOCAL2 0 #endif #ifndef LOG_LOCAL3 #define LOG_LOCAL3 0 #endif #ifndef LOG_LOCAL4 #define LOG_LOCAL4 0 #endif #ifndef LOG_LOCAL5 #define LOG_LOCAL5 0 #endif #ifndef LOG_LOCAL6 #define LOG_LOCAL6 0 #endif #ifndef LOG_LOCAL7 #define LOG_LOCAL7 0 #endif #ifndef LOG_DAEMON #define LOG_DAEMON 0 #endif #ifndef LOG_USER #define LOG_USER 0 #endif #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ /* Set line buffering mode for a stream. */ void netsnmp_set_line_buffering(FILE *stream) { #if defined(WIN32) /* * According to MSDN, the Microsoft Visual Studio C runtime library does * not support line buffering, so turn off buffering completely. * See also http://msdn.microsoft.com/en-us/library/86cebhfs(VS.71).aspx. */ setvbuf(stream, NULL, _IONBF, BUFSIZ); #elif defined(HAVE_SETLINEBUF) /* setlinefunction() is a function from the BSD Unix API. */ setlinebuf(stream); #else /* See also the C89 or C99 standard for more information about setvbuf(). */ setvbuf(stream, NULL, _IOLBF, BUFSIZ); #endif } /* * Decodes log priority. * @param optarg - IN - priority to decode, "0" or "0-7" * OUT - points to last character after the decoded priority * @param pri_max - OUT - maximum priority (i.e. 0x7 from "0-7") */ static int decode_priority( char **optarg, int *pri_max ) { int pri_low = LOG_DEBUG; if (*optarg == NULL) return -1; switch (**optarg) { case '0': case '!': pri_low = LOG_EMERG; break; case '1': case 'a': case 'A': pri_low = LOG_ALERT; break; case '2': case 'c': case 'C': pri_low = LOG_CRIT; break; case '3': case 'e': case 'E': pri_low = LOG_ERR; break; case '4': case 'w': case 'W': pri_low = LOG_WARNING; break; case '5': case 'n': case 'N': pri_low = LOG_NOTICE; break; case '6': case 'i': case 'I': pri_low = LOG_INFO; break; case '7': case 'd': case 'D': pri_low = LOG_DEBUG; break; default: fprintf(stderr, "invalid priority: %c\n",**optarg); return -1; } *optarg = *optarg+1; if (pri_max && **optarg=='-') { *optarg = *optarg + 1; /* skip '-' */ *pri_max = decode_priority( optarg, NULL ); if (*pri_max == -1) return -1; if (pri_low < *pri_max) { int tmp = pri_low; pri_low = *pri_max; *pri_max = tmp; } } return pri_low; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG static int decode_facility( char *optarg ) { if (optarg == NULL) return -1; switch (*optarg) { case 'd': case 'D': return LOG_DAEMON; case 'u': case 'U': return LOG_USER; case '0': return LOG_LOCAL0; case '1': return LOG_LOCAL1; case '2': return LOG_LOCAL2; case '3': return LOG_LOCAL3; case '4': return LOG_LOCAL4; case '5': return LOG_LOCAL5; case '6': return LOG_LOCAL6; case '7': return LOG_LOCAL7; default: fprintf(stderr, "invalid syslog facility: %c\n",*optarg); return -1; } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ int snmp_log_options(char *optarg, int argc, char *const *argv) { char *cp = optarg; /* * Hmmm... this doesn't seem to work. * The main agent 'getopt' handling assumes * that the -L option takes an argument, * and objects if this is missing. * Trying to differentiate between * new-style "-Lx", and old-style "-L xx" * is likely to be a major headache. */ char missing_opt = 'e'; /* old -L is new -Le */ int priority = LOG_DEBUG; int pri_max = LOG_EMERG; int inc_optind = 0; netsnmp_log_handler *logh; DEBUGMSGT(("logging:options", "optarg: '%s', argc %d, argv '%s'\n", optarg, argc, argv ? argv[0] : "NULL")); optarg++; if (!*cp) cp = &missing_opt; /* * Support '... -Lx=value ....' syntax */ if (*optarg == '=') { optarg++; } /* * and '.... "-Lx value" ....' (*with* the quotes) */ while (*optarg && isspace((unsigned char)(*optarg))) { optarg++; } /* * Finally, handle ".... -Lx value ...." syntax * (*without* surrounding quotes) */ if ((!*optarg) && (NULL != argv)) { /* * We've run off the end of the argument * so move on to the next. * But we might not actually need it, so don't * increment optind just yet! */ optarg = argv[optind]; inc_optind = 1; } DEBUGMSGT(("logging:options", "*cp: '%c'\n", *cp)); switch (*cp) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO /* * Log to Standard Error */ case 'E': priority = decode_priority( &optarg, &pri_max ); if (priority == -1) return -1; if (inc_optind) optind++; /* Fallthrough */ case 'e': logh = netsnmp_register_stdio_loghandler(0, priority, pri_max, "stderr"); break; /* * Log to Standard Output */ case 'O': priority = decode_priority( &optarg, &pri_max ); if (priority == -1) return -1; if (inc_optind) optind++; /* Fallthrough */ case 'o': logh = netsnmp_register_stdio_loghandler( 1, priority, pri_max, "stdout" ); break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ /* * Log to a named file */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE case 'F': priority = decode_priority( &optarg, &pri_max ); if (priority == -1) return -1; while (*optarg == ' ') optarg++; if (!*optarg && !argv) return -1; else if (!*optarg) optarg = argv[++optind]; /* FALL THROUGH */ case 'f': if (inc_optind) optind++; if (!optarg) { fprintf(stderr, "Missing log file\n"); return -1; } DEBUGMSGTL(("logging:options", "%d-%d: '%s'\n", priority, pri_max, optarg)); logh = netsnmp_register_filelog_handler(optarg, priority, pri_max, -1); break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG /* * Log to syslog */ case 'S': priority = decode_priority( &optarg, &pri_max ); if (priority == -1 || !argv) return -1; if (!optarg[0]) { /* The command line argument with priority does not contain log * facility. The facility must be in next argument then. */ optind++; if (optind < argc) optarg = argv[optind]; } /* Fallthrough */ case 's': if (inc_optind) optind++; if (!optarg) { fprintf(stderr, "Missing syslog facility\n"); return -1; } logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_SYSLOG, priority); if (logh) { int facility = decode_facility(optarg); if (facility == -1) { netsnmp_remove_loghandler(logh); return -1; } logh->pri_max = pri_max; logh->token = strdup(snmp_log_syslogname(NULL)); logh->magic = (void *)(intptr_t)facility; snmp_enable_syslog_ident(snmp_log_syslogname(NULL), facility); } break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ /* * Don't log */ case 'N': priority = decode_priority( &optarg, &pri_max ); if (priority == -1) return -1; if (inc_optind) optind++; /* Fallthrough */ case 'n': /* * disable all logs to clean them up (close files, etc), * remove all log handlers, then register a null handler. */ snmp_disable_log(); while(NULL != logh_head) netsnmp_remove_loghandler( logh_head ); logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_NONE, priority); if (logh) { logh->pri_max = pri_max; } break; default: fprintf(stderr, "Unknown logging option passed to -L: %c.\n", *cp); return -1; } return 0; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG char * snmp_log_syslogname(const char *pstr) { if (pstr) strlcpy (syslogname, pstr, sizeof(syslogname)); return syslogname; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ void snmp_log_options_usage(const char *lead, FILE * outf) { const char *pri1_msg = " for level 'pri' and above"; const char *pri2_msg = " for levels 'p1' to 'p2'"; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO fprintf(outf, "%se: log to standard error\n", lead); fprintf(outf, "%so: log to standard output\n", lead); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ fprintf(outf, "%sn: don't log at all\n", lead); #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE fprintf(outf, "%sf file: log to the specified file\n", lead); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG fprintf(outf, "%ss facility: log to syslog (via the specified facility)\n", lead); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ fprintf(outf, "\n%s(variants)\n", lead); #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE fprintf(outf, "%s[EON] pri: log to standard error, output or /dev/null%s\n", lead, pri1_msg); fprintf(outf, "%s[EON] p1-p2: log to standard error, output or /dev/null%s\n", lead, pri2_msg); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ fprintf(outf, "%s[FS] pri token: log to file/syslog%s\n", lead, pri1_msg); fprintf(outf, "%s[FS] p1-p2 token: log to file/syslog%s\n", lead, pri2_msg); } /** * Is logging done? * * @return Returns 0 if logging is off, 1 when it is done. * */ int snmp_get_do_logging(void) { return (logh_enabled > 0); } static char * sprintf_stamp(time_t * now, char *sbuf) { time_t Now; struct tm *tm; if (now == NULL) { now = &Now; time(now); } tm = localtime(now); sprintf(sbuf, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return sbuf; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG void snmp_disable_syslog_entry(netsnmp_log_handler *logh) { if (!logh || !logh->enabled || logh->type != NETSNMP_LOGHANDLER_SYSLOG) return; #ifdef WIN32 if (logh->magic) { HANDLE eventlog_h = (HANDLE)logh->magic; CloseEventLog(eventlog_h); logh->magic = NULL; } #else closelog(); logh->imagic = 0; #endif netsnmp_disable_this_loghandler(logh); } void snmp_disable_syslog(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_SYSLOG) snmp_disable_syslog_entry(logh); } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE void snmp_disable_filelog_entry(netsnmp_log_handler *logh) { if (!logh /* || !logh->enabled */ || logh->type != NETSNMP_LOGHANDLER_FILE) return; if (logh->magic) { fputs("\n", (FILE*)logh->magic); /* XXX - why? */ fclose((FILE*)logh->magic); logh->magic = NULL; } netsnmp_disable_this_loghandler(logh); } void snmp_disable_filelog(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_FILE) snmp_disable_filelog_entry(logh); } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO netsnmp_log_handler * netsnmp_register_stdio_loghandler(int is_stdout, int priority, int priority_max, const char *tok) { netsnmp_log_handler *logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR, priority); if (NULL == logh) { return NULL; } if (is_stdout) { netsnmp_set_line_buffering(stdout); logh->imagic = 1; /* stdout, not stderr */ } else netsnmp_set_line_buffering(stderr); logh->pri_max = priority_max; if (tok) logh->token = strdup(tok); return logh; } /* * returns that status of stderr logging * * @retval 0 : stderr logging disabled * @retval 1 : stderr logging enabled */ int snmp_stderrlog_status(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled && (logh->type == NETSNMP_LOGHANDLER_STDOUT || logh->type == NETSNMP_LOGHANDLER_STDERR)) { return 1; } return 0; } void snmp_disable_stderrlog(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled && (logh->type == NETSNMP_LOGHANDLER_STDOUT || logh->type == NETSNMP_LOGHANDLER_STDERR)) { netsnmp_disable_this_loghandler(logh); } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG void snmp_disable_calllog(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_CALLBACK) { netsnmp_disable_this_loghandler(logh); } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG */ void snmp_disable_log(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) snmp_disable_syslog_entry(logh); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE if (logh->type == NETSNMP_LOGHANDLER_FILE) snmp_disable_filelog_entry(logh); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ netsnmp_disable_this_loghandler(logh); } } /* * close and reopen all file based logs, to allow logfile * rotation. */ void netsnmp_logging_restart(void) { netsnmp_log_handler *logh; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG int doneone = 0; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ for (logh = logh_head; logh; logh = logh->next) { if (0 == logh->enabled) continue; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) { snmp_disable_syslog_entry(logh); snmp_enable_syslog_ident(logh->token,(int)(intptr_t)logh->magic); doneone = 1; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE if (logh->type == NETSNMP_LOGHANDLER_FILE && !doneone) { snmp_disable_filelog_entry(logh); /** hmm, don't zero status isn't saved.. i think it's * safer not to overwrite, in case a hup is just to * re-read config files... */ netsnmp_enable_filelog(logh, 1); } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ } } /* ================================================== */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG void snmp_enable_syslog(void) { snmp_enable_syslog_ident(snmp_log_syslogname(NULL), LOG_DAEMON); } void snmp_enable_syslog_ident(const char *ident, const int facility) { netsnmp_log_handler *logh; int found = 0; int enable = 1; #ifdef WIN32 HANDLE eventlog_h; #else void *eventlog_h = NULL; #endif snmp_disable_syslog(); /* only one syslog at a time */ #ifdef WIN32 eventlog_h = OpenEventLog(NULL, ident); if (eventlog_h == NULL) { /* * Hmmm..... * Maybe disable this handler, and log the error ? */ fprintf(stderr, "Could not open event log for %s. " "Last error: 0x%x\n", ident, GetLastError()); enable = 0; } #else openlog(snmp_log_syslogname(ident), LOG_CONS | LOG_PID, facility); #endif for (logh = logh_head; logh; logh = logh->next) if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) { logh->magic = (void*)eventlog_h; logh->imagic = enable; /* syslog open */ if (logh->enabled && (0 == enable)) netsnmp_disable_this_loghandler(logh); else if ((0 == logh->enabled) && enable) netsnmp_enable_this_loghandler(logh); found = 1; } if (!found) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_SYSLOG, LOG_DEBUG ); if (logh) { logh->magic = (void*)eventlog_h; logh->token = strdup(ident); logh->imagic = enable; /* syslog open */ if (logh->enabled && (0 == enable)) netsnmp_disable_this_loghandler(logh); else if ((0 == logh->enabled) && enable) netsnmp_enable_this_loghandler(logh); } } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE void netsnmp_enable_filelog(netsnmp_log_handler *logh, int dont_zero_log) { FILE *logfile; if (!logh) return; if (!logh->magic) { logfile = fopen(logh->token, dont_zero_log ? "a" : "w"); if (!logfile) { snmp_log_perror(logh->token); return; } logh->magic = (void*)logfile; netsnmp_set_line_buffering(logfile); } netsnmp_enable_this_loghandler(logh); } netsnmp_log_handler * netsnmp_register_filelog_handler(const char* logfilename, int priority, int priority_max, int dont_zero_log) { netsnmp_log_handler *logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, priority ); if (NULL == logh) return NULL; logh->pri_max = priority_max; logh->token = strdup(logfilename); if (-1 == dont_zero_log) dont_zero_log = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES); netsnmp_enable_filelog(logh, dont_zero_log); return logh; } void snmp_enable_filelog(const char *logfilename, int dont_zero_log) { netsnmp_log_handler *logh; /* * don't disable ALL filelogs whenever a new one is enabled. * this prevents '-Lf file' from working in snmpd, as the * call to set up /var/log/snmpd.log will disable the previous * log setup. * snmp_disable_filelog(); */ if (logfilename) { logh = netsnmp_find_loghandler( logfilename ); if (!logh) logh = netsnmp_register_filelog_handler( logfilename, LOG_DEBUG, 0, dont_zero_log ); else netsnmp_enable_filelog(logh, dont_zero_log); } else { for (logh = logh_head; logh; logh = logh->next) if (logh->type == NETSNMP_LOGHANDLER_FILE) netsnmp_enable_filelog(logh, dont_zero_log); } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #ifndef NETSNMP_FEATURE_REMOVE_ENABLE_STDERRLOG /* used in the perl modules and ip-mib/ipv4InterfaceTable/ipv4InterfaceTable_subagent.c */ void snmp_enable_stderrlog(void) { netsnmp_log_handler *logh; int found = 0; for (logh = logh_head; logh; logh = logh->next) if (logh->type == NETSNMP_LOGHANDLER_STDOUT || logh->type == NETSNMP_LOGHANDLER_STDERR) { netsnmp_enable_this_loghandler(logh); found = 1; } if (!found) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR, LOG_DEBUG ); if (logh) logh->token = strdup("stderr"); } } #endif /* NETSNMP_FEATURE_REMOVE_ENABLE_STDERRLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG void snmp_enable_calllog(void) /* XXX - or take a callback routine ??? */ { netsnmp_log_handler *logh; int found = 0; for (logh = logh_head; logh; logh = logh->next) if (logh->type == NETSNMP_LOGHANDLER_CALLBACK) { netsnmp_enable_this_loghandler(logh); found = 1; } if (!found) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG ); if (logh) logh->token = strdup("callback"); } } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG */ /* ==================================================== */ netsnmp_log_handler * netsnmp_find_loghandler( const char *token ) { netsnmp_log_handler *logh; if (!token) return NULL; for (logh = logh_head; logh; logh = logh->next) if (logh->token && !strcmp( token, logh->token )) break; return logh; } int netsnmp_add_loghandler( netsnmp_log_handler *logh ) { int i; netsnmp_log_handler *logh2; if (!logh) return 0; /* * Find the appropriate point for the new entry... * (logh2 will point to the entry immediately following) */ for (logh2 = logh_head; logh2; logh2 = logh2->next) if ( logh2->priority >= logh->priority ) break; /* * ... and link it into the main list. */ if (logh2) { if (logh2->prev) logh2->prev->next = logh; else logh_head = logh; logh->next = logh2; logh2->prev = logh; } else if (logh_head ) { /* * If logh2 is NULL, we're tagging on to the end */ for (logh2 = logh_head; logh2->next; logh2 = logh2->next) ; logh2->next = logh; } else { logh_head = logh; } /* * Also tweak the relevant priority-'index' array. */ for (i=LOG_EMERG; i<=logh->priority; i++) if (!logh_priorities[i] || logh_priorities[i]->priority >= logh->priority) logh_priorities[i] = logh; return 1; } netsnmp_log_handler * netsnmp_register_loghandler( int type, int priority ) { netsnmp_log_handler *logh; logh = SNMP_MALLOC_TYPEDEF(netsnmp_log_handler); if (!logh) return NULL; DEBUGMSGT(("logging:register", "registering log type %d with pri %d\n", type, priority)); if (priority > LOG_DEBUG) { DEBUGMSGT(("logging:register", " limiting pri %d to %d\n", priority, LOG_DEBUG)); priority = LOG_DEBUG; } logh->type = type; switch ( type ) { case NETSNMP_LOGHANDLER_STDOUT: logh->imagic = 1; logh->handler = log_handler_stdouterr; break; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO case NETSNMP_LOGHANDLER_STDERR: logh->handler = log_handler_stdouterr; break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE case NETSNMP_LOGHANDLER_FILE: logh->handler = log_handler_file; logh->imagic = 1; break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG case NETSNMP_LOGHANDLER_SYSLOG: logh->handler = log_handler_syslog; break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ case NETSNMP_LOGHANDLER_CALLBACK: logh->handler = log_handler_callback; break; case NETSNMP_LOGHANDLER_NONE: logh->handler = log_handler_null; break; default: free(logh); return NULL; } logh->priority = priority; netsnmp_enable_this_loghandler(logh); netsnmp_add_loghandler( logh ); return logh; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_LOGHANDLER int netsnmp_enable_loghandler( const char *token ) { netsnmp_log_handler *logh; logh = netsnmp_find_loghandler( token ); if (!logh) return 0; netsnmp_enable_this_loghandler(logh); return 1; } int netsnmp_disable_loghandler( const char *token ) { netsnmp_log_handler *logh; logh = netsnmp_find_loghandler( token ); if (!logh) return 0; netsnmp_disable_this_loghandler(logh); return 1; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_LOGHANDLER */ int netsnmp_remove_loghandler( netsnmp_log_handler *logh ) { int i; if (!logh) return 0; if (logh->prev) logh->prev->next = logh->next; else logh_head = logh->next; if (logh->next) logh->next->prev = logh->prev; for (i=LOG_EMERG; i<=logh->priority; i++) if (logh == logh_priorities[i]) logh_priorities[i] = logh->next; free(NETSNMP_REMOVE_CONST(char*, logh->token)); SNMP_FREE(logh); return 1; } /* ==================================================== */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO int log_handler_stdouterr( netsnmp_log_handler* logh, int pri, const char *str) { static int newline = 1; /* MTCRITICAL_RESOURCE */ const char *newline_ptr; char sbuf[40]; if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_LOG_TIMESTAMP) && newline) { sprintf_stamp(NULL, sbuf); } else { strcpy(sbuf, ""); } /* * Remember whether or not the current line ends with a newline for the * next call of log_handler_stdouterr(). */ newline_ptr = strrchr(str, '\n'); newline = newline_ptr && newline_ptr[1] == 0; if (logh->imagic) printf( "%s%s", sbuf, str); else fprintf(stderr, "%s%s", sbuf, str); return 1; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifdef WIN32 int log_handler_syslog( netsnmp_log_handler* logh, int pri, const char *str) { WORD etype; LPCTSTR event_msg[2]; HANDLE eventlog_h = logh->magic; /* ** EVENT TYPES: ** ** Information (EVENTLOG_INFORMATION_TYPE) ** Information events indicate infrequent but significant ** successful operations. ** Warning (EVENTLOG_WARNING_TYPE) ** Warning events indicate problems that are not immediately ** significant, but that may indicate conditions that could ** cause future problems. Resource consumption is a good ** candidate for a warning event. ** Error (EVENTLOG_ERROR_TYPE) ** Error events indicate significant problems that the user ** should know about. Error events usually indicate a loss of ** functionality or data. */ switch (pri) { case LOG_EMERG: case LOG_ALERT: case LOG_CRIT: case LOG_ERR: etype = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: etype = EVENTLOG_WARNING_TYPE; break; case LOG_NOTICE: case LOG_INFO: case LOG_DEBUG: etype = EVENTLOG_INFORMATION_TYPE; break; default: etype = EVENTLOG_INFORMATION_TYPE; break; } event_msg[0] = str; event_msg[1] = NULL; /* NOTE: 4th parameter must match winservice.mc:MessageId value */ if (!ReportEvent(eventlog_h, etype, 0, 100, NULL, 1, 0, event_msg, NULL)) { /* * Hmmm..... * Maybe disable this handler, and log the error ? */ fprintf(stderr, "Could not report event. Last error: 0x%x\n", GetLastError()); return 0; } return 1; } #else int log_handler_syslog( netsnmp_log_handler* logh, int pri, const char *str) { /* * XXX * We've got three items of information to work with: * Is the syslog currently open? * What ident string to use? * What facility to log to? * * We've got two "magic" locations (imagic & magic) plus the token */ if (!(logh->imagic)) { const char *ident = logh->token; int facility = (int)(intptr_t)logh->magic; if (!ident) ident = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE); openlog(ident, LOG_CONS | LOG_PID, facility); logh->imagic = 1; } syslog( pri, "%s", str ); return 1; } #endif /* !WIN32 */ #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE int log_handler_file( netsnmp_log_handler* logh, int pri, const char *str) { FILE *fhandle; char sbuf[40]; /* * We use imagic to save information about whether the next output * will start a new line, and thus might need a timestamp */ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_LOG_TIMESTAMP) && logh->imagic) { sprintf_stamp(NULL, sbuf); } else { strcpy(sbuf, ""); } /* * If we haven't already opened the file, then do so. * Save the filehandle pointer for next time. * * Note that this should still work, even if the file * is closed in the meantime (e.g. a regular "cleanup" sweep) */ fhandle = (FILE*)logh->magic; if (!logh->magic) { fhandle = fopen(logh->token, "a+"); if (!fhandle) return 0; logh->magic = (void*)fhandle; } fprintf(fhandle, "%s%s", sbuf, str); fflush(fhandle); logh->imagic = str[strlen(str) - 1] == '\n'; return 1; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ int log_handler_callback(netsnmp_log_handler* logh, int pri, const char *str) { /* * XXX - perhaps replace 'snmp_call_callbacks' processing * with individual callback log_handlers ?? */ struct snmp_log_message slm; int dodebug = snmp_get_do_debugging(); slm.priority = pri; slm.msg = str; if (dodebug) /* turn off debugging inside the callbacks else will loop */ snmp_set_do_debugging(0); snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, &slm); if (dodebug) snmp_set_do_debugging(dodebug); return 1; } int log_handler_null( netsnmp_log_handler* logh, int pri, const char *str) { /* * Dummy log handler - just throw away the error completely * You probably don't really want to do this! */ return 1; } void snmp_log_string(int priority, const char *str) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO static int stderr_enabled = 0; static netsnmp_log_handler lh = { 1, 0, 0, 0, "stderr", log_handler_stdouterr, 0, NULL, NULL, NULL }; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ netsnmp_log_handler *logh; /* * We've got to be able to log messages *somewhere*! * If you don't want stderr logging, then enable something else. */ if (0 == logh_enabled) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO if (!stderr_enabled) { ++stderr_enabled; netsnmp_set_line_buffering(stderr); } log_handler_stdouterr( &lh, priority, str ); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ return; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO else if (stderr_enabled) { stderr_enabled = 0; log_handler_stdouterr( &lh, LOG_INFO, "Log handling defined - disabling stderr\n" ); } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ /* * Start at the given priority, and work "upwards".... */ if (priority > LOG_DEBUG) priority = LOG_DEBUG; logh = logh_priorities[priority]; for ( ; logh; logh = logh->next ) { /* * ... but skipping any handlers with a "maximum priority" * that we have already exceeded. And don't forget to * ensure this logging is turned on (see snmp_disable_stderrlog * and its cohorts). */ if (logh->enabled && (priority >= logh->pri_max)) logh->handler( logh, priority, str ); } } /* ==================================================== */ /** * This snmp logging function allows variable argument list given the * specified priority, format and a populated va_list structure. * The default logfile this function writes to is /var/log/snmpd.log. * * @param priority is an integer representing the type of message to be written * to the snmp log file. The types are errors, warning, and information. * - The error types are: * - LOG_EMERG system is unusable * - LOG_ALERT action must be taken immediately * - LOG_CRIT critical conditions * - LOG_ERR error conditions * - The warning type is: * - LOG_WARNING warning conditions * - The information types are: * - LOG_NOTICE normal but significant condition * - LOG_INFO informational * - LOG_DEBUG debug-level messages * * @param format is a pointer to a char representing the variable argument list * format used. * * @param ap is a va_list type used to traverse the list of arguments. * * @return Returns 0 on success, -1 when the code could not format the log- * string, -2 when dynamic memory could not be allocated if the length * of the log buffer is greater then 1024 bytes. For each of these * errors a LOG_ERR messgae is written to the logfile. * * @see snmp_log */ int snmp_vlog(int priority, const char *format, va_list ap) { char *buffer = NULL; int length; length = vasprintf(&buffer, format, ap); if (length < 0) { snmp_log_string(LOG_ERR, "Could not format log-string\n"); return -1; } snmp_log_string(priority, buffer); free(buffer); return 0; } /** * This snmp logging function allows variable argument list given the * specified format and priority. Calls the snmp_vlog function. * The default logfile this function writes to is /var/log/snmpd.log. * * @see snmp_vlog */ int snmp_log(int priority, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = snmp_vlog(priority, format, ap); va_end(ap); return (ret); } /* * log a critical error. */ void snmp_log_perror(const char *s) { char *error = strerror(errno); if (s) { if (error) snmp_log(LOG_ERR, "%s: %s\n", s, error); else snmp_log(LOG_ERR, "%s: Error %d out-of-range\n", s, errno); } else { if (error) snmp_log(LOG_ERR, "%s\n", error); else snmp_log(LOG_ERR, "Error %d out-of-range\n", errno); } } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_EXTERNAL /* external access to logh_head variable */ netsnmp_log_handler * get_logh_head(void) { return logh_head; } #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_EXTERNAL */ /** @} */