/* * 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. */ /** @defgroup snmp_logging generic logging for net-snmp * @ingroup library * * @{ */ #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 # ifdef WIN32 # include # else # include # endif # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_STDARG_H #include #else #include #endif #if HAVE_DMALLOC_H #include #endif #if HAVE_WINSOCK_H #include #endif #if HAVE_WINDOWS_H #include #endif #include #include #include /* For this file's "internal" definitions */ #include #include #include #define LOGLENGTH 1024 /* * 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 newline = 1; /* MTCRITICAL_RESOURCE */ static char syslogname[64] = DEFAULT_LOG_ID; #ifndef HAVE_VSNPRINTF /* * Need to use the UCD-provided one */ int vsnprintf(char *str, size_t count, const char *fmt, va_list arg); #endif void init_snmp_logging(void) { netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "logTimestamp", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_LOG_TIMESTAMP); } /* * These definitions handle 4.2 systems without additional syslog facilities. */ #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 int decode_priority( char *optarg, int *pri_max ) { int pri_low = LOG_DEBUG; 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; } if (pri_max && *(optarg+1)=='-') { *pri_max = decode_priority( optarg+2, NULL ); if (*pri_max == -1) return -1; } return pri_low; } 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; } } 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; optarg++; if (!*cp) cp = &missing_opt; /* * Support '... -Lx=value ....' syntax */ if (*optarg == '=') { optarg++; } /* * and '.... "-Lx value" ....' (*with* the quotes) */ while (*optarg && isspace(*optarg)) { optarg++; } /* * Finally, handle ".... -Lx value ...." syntax * (*without* surrounding quotes) */ if (!*optarg) { /* * 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; } switch (*cp) { /* * 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_loghandler(NETSNMP_LOGHANDLER_STDERR, priority); if (logh) { logh->pri_max = pri_max; logh->token = strdup("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_loghandler(NETSNMP_LOGHANDLER_STDERR, priority); if (logh) { logh->pri_max = pri_max; logh->token = strdup("stdout"); logh->imagic = 1; /* stdout, not stderr */ } break; /* * Log to a named file */ case 'F': priority = decode_priority( optarg, &pri_max ); if (priority == -1) return -1; optarg = argv[++optind]; /* Fallthrough */ case 'f': if (inc_optind) optind++; if (!optarg) { fprintf(stderr, "Missing log file\n"); return -1; } logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, priority); if (logh) { logh->pri_max = pri_max; logh->token = strdup(optarg); } break; /* * Log to syslog */ case 'S': priority = decode_priority( optarg, &pri_max ); if (priority == -1) return -1; 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) return -1; logh->pri_max = pri_max; logh->token = strdup(snmp_log_syslogname(0)); logh->magic = (void *)(ptrdiff_t)facility; snmp_enable_syslog_ident(snmp_log_syslogname(0), facility); } break; /* * 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; } const char * snmp_log_syslogname(const char *pstr) { if (pstr) strncpy (syslogname, pstr, sizeof(syslogname)); return syslogname; } 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'"; fprintf(outf, "%se: log to standard error\n", lead); fprintf(outf, "%so: log to standard output\n", lead); fprintf(outf, "%sn: don't log at all\n", lead); fprintf(outf, "%sf file: log to the specified file\n", lead); fprintf(outf, "%ss facility: log to syslog (via the specified facility)\n", lead); fprintf(outf, "\n%s(variants)\n", lead); 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); 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); } /** * 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_get_do_logging(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) if (logh->enabled) return 1; return 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; } 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 logh->enabled = 0; } 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); } 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; } logh->enabled = 0; } 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); } 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)) { logh->enabled = 0; } } 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) { logh->enabled = 0; } } void snmp_disable_log(void) { netsnmp_log_handler *logh; for (logh = logh_head; logh; logh = logh->next) { if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) snmp_disable_syslog_entry(logh); if (logh->type == NETSNMP_LOGHANDLER_FILE) snmp_disable_filelog_entry(logh); logh->enabled = 0; } } /* ================================================== */ void snmp_enable_syslog(void) { snmp_enable_syslog_ident(snmp_log_syslogname(0), 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(); /* ??? */ #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 */ logh->enabled = enable; 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 */ logh->enabled = enable; } } } 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) return; logh->magic = (void*)logfile; #ifdef WIN32 /* * Apparently, "line buffering" under Windows is * actually implemented as "full buffering". * Let's try turning off buffering completely. */ setvbuf(logfile, NULL, _IONBF, BUFSIZ); #else setvbuf(logfile, NULL, _IOLBF, BUFSIZ); #endif } logh->enabled = 1; } void snmp_enable_filelog(const char *logfilename, int dont_zero_log) { netsnmp_log_handler *logh; snmp_disable_filelog(); /* XXX ??? */ if (logfilename) { logh = netsnmp_find_loghandler( logfilename ); if (!logh) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, LOG_DEBUG ); if (logh) logh->token = strdup(logfilename); } if (logh) 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); } } 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) { logh->enabled = 1; found = 1; } if (!found) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR, LOG_DEBUG ); if (logh) logh->token = strdup("stderr"); } } 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) logh->enabled = 1; found = 1; } if (!found) { logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG ); if (logh) logh->token = strdup("callback"); } } /* ==================================================== */ 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; logh->type = type; switch ( type ) { case NETSNMP_LOGHANDLER_STDOUT: logh->imagic = 1; /* fallthrough */ case NETSNMP_LOGHANDLER_STDERR: logh->handler = log_handler_stdouterr; break; case NETSNMP_LOGHANDLER_FILE: logh->handler = log_handler_file; logh->imagic = 1; break; case NETSNMP_LOGHANDLER_SYSLOG: logh->handler = log_handler_syslog; break; 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; logh->enabled = 1; netsnmp_add_loghandler( logh ); return logh; } int netsnmp_enable_loghandler( const char *token ) { netsnmp_log_handler *logh; logh = netsnmp_find_loghandler( token ); if (!logh) return 0; logh->enabled = 1; return 1; } int netsnmp_disable_loghandler( const char *token ) { netsnmp_log_handler *logh; logh = netsnmp_find_loghandler( token ); if (!logh) return 0; logh->enabled = 0; return 1; } int netsnmp_remove_loghandler( netsnmp_log_handler *logh ) { 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; return 1; } /* ==================================================== */ int log_handler_stdouterr( netsnmp_log_handler* logh, int pri, const char *string) { 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, ""); } newline = string[strlen(string) - 1] == '\n'; /* XXX - Eh ? */ if (logh->imagic) printf( "%s%s", sbuf, string); else fprintf(stderr, "%s%s", sbuf, string); return 1; } #ifdef WIN32 int log_handler_syslog( netsnmp_log_handler* logh, int pri, const char *string) { 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] = string; 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 *string) { /* * 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)(ptrdiff_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", string ); return 1; } #endif /* !WIN32 */ int log_handler_file( netsnmp_log_handler* logh, int pri, const char *string) { 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, string); fflush(fhandle); logh->imagic = string[strlen(string) - 1] == '\n'; return 1; } int log_handler_callback(netsnmp_log_handler* logh, int pri, const char *string) { /* * 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 = string; 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 *string) { /* * 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 *string) { 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 (!logh_head) { snmp_enable_stderrlog(); snmp_log_string(LOG_WARNING, "No log handling enabled - turning on stderr logging\n"); } /* * Start at the given priority, and work "upwards".... */ logh = logh_priorities[priority]; for ( ; logh; logh = logh->next ) { /* * ... but skipping any handlers with a "maximum priority" * that we have already exceeded. */ if (priority >= logh->pri_max) logh->handler( logh, priority, string ); } } /* ==================================================== */ int snmp_vlog(int priority, const char *format, va_list ap) { char buffer[LOGLENGTH]; int length; char *dynamic; length = vsnprintf(buffer, LOGLENGTH, format, ap); if (length == 0) return (0); /* Empty string */ if (length == -1) { snmp_log_string(LOG_ERR, "Could not format log-string\n"); return (-1); } if (length < LOGLENGTH) { snmp_log_string(priority, buffer); return (0); } dynamic = (char *) malloc(length + 1); if (dynamic == NULL) { snmp_log_string(LOG_ERR, "Could not allocate memory for log-message\n"); snmp_log_string(priority, buffer); return (-2); } vsnprintf(dynamic, length + 1, format, ap); snmp_log_string(priority, dynamic); free(dynamic); 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 #if HAVE_STDARG_H snmp_log(int priority, const char *format, ...) #else snmp_log(va_alist) va_dcl #endif { va_list ap; int ret; #if HAVE_STDARG_H va_start(ap, format); #else int priority; const char *format; va_start(ap); priority = va_arg(ap, int); format = va_arg(ap, const char *); #endif 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); } } /* external access to logh_head variable */ netsnmp_log_handler * get_logh_head(void) { return logh_head; } /** @} */