net-snmp 5.7
snmp_logging.c
00001 /*
00002  * logging.c - generic logging for snmp-agent
00003  * * Contributed by Ragnar Kjørstad, ucd@ragnark.vestdata.no 1999-06-26 
00004  */
00005 /* Portions of this file are subject to the following copyright(s).  See
00006  * the Net-SNMP's COPYING file for more details and other copyrights
00007  * that may apply:
00008  */
00009 /*
00010  * Portions of this file are copyrighted by:
00011  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00012  * Use is subject to license terms specified in the COPYING file
00013  * distributed with the Net-SNMP package.
00014  */
00020 #include <net-snmp/net-snmp-config.h>
00021 #include <net-snmp/net-snmp-features.h>
00022 #include <stdio.h>
00023 #if HAVE_MALLOC_H
00024 #include <malloc.h>
00025 #endif
00026 #if HAVE_STRING_H
00027 #include <string.h>
00028 #else
00029 #include <strings.h>
00030 #endif
00031 #include <ctype.h>
00032 #if HAVE_STDLIB_H
00033 #include <stdlib.h>
00034 #endif
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include <fcntl.h>
00038 #include <errno.h>
00039 #if HAVE_SYSLOG_H
00040 #include <syslog.h>
00041 #ifndef LOG_CONS                /* Interesting Ultrix feature */
00042 #include <sys/syslog.h>
00043 #endif
00044 #endif
00045 #if TIME_WITH_SYS_TIME
00046 # include <sys/time.h>
00047 # include <time.h>
00048 #else
00049 # if HAVE_SYS_TIME_H
00050 #  include <sys/time.h>
00051 # else
00052 #  include <time.h>
00053 # endif
00054 #endif
00055 #if HAVE_NETINET_IN_H
00056 #include <netinet/in.h>
00057 #endif
00058 
00059 #include <stdarg.h>
00060 
00061 #if HAVE_UNISTD_H
00062 #include <unistd.h>
00063 #endif
00064 #if HAVE_DMALLOC_H
00065 #include <dmalloc.h>
00066 #endif
00067 
00068 #include <net-snmp/types.h>
00069 #include <net-snmp/output_api.h>
00070 #include <net-snmp/library/snmp_logging.h>      /* For this file's "internal" definitions */
00071 #include <net-snmp/config_api.h>
00072 #include <net-snmp/utilities.h>
00073 
00074 #include <net-snmp/library/callback.h>
00075 #define LOGLENGTH 1024
00076 
00077 #ifdef va_copy
00078 #define NEED_VA_END_AFTER_VA_COPY
00079 #else
00080 #ifdef __vacopy
00081 #define vacopy __vacopy
00082 #define NEED_VA_END_AFTER_VA_COPY
00083 #else
00084 #define va_copy(dest, src) memcpy (&dest, &src, sizeof (va_list))
00085 #endif
00086 #endif
00087 
00088 netsnmp_feature_child_of(logging_all, libnetsnmp)
00089 
00090 netsnmp_feature_child_of(logging_outputs, logging_all)
00091 netsnmp_feature_child_of(logging_file, logging_outputs)
00092 netsnmp_feature_child_of(logging_stdio, logging_outputs)
00093 netsnmp_feature_child_of(logging_syslog, logging_outputs)
00094 netsnmp_feature_child_of(logging_external, logging_all)
00095 
00096 netsnmp_feature_child_of(enable_stderrlog, logging_all)
00097 
00098 netsnmp_feature_child_of(logging_enable_calllog, netsnmp_unused)
00099 netsnmp_feature_child_of(logging_enable_loghandler, netsnmp_unused)
00100 
00101 /* default to the file/stdio/syslog set */
00102 netsnmp_feature_want(logging_outputs)
00103 
00104 /*
00105  * logh_head:  A list of all log handlers, in increasing order of priority
00106  * logh_priorities:  'Indexes' into this list, by priority
00107  */
00108 netsnmp_log_handler *logh_head = NULL;
00109 netsnmp_log_handler *logh_priorities[LOG_DEBUG+1];
00110 static int  logh_enabled = 0;
00111 
00112 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00113 static char syslogname[64] = DEFAULT_LOG_ID;
00114 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00115 
00116 void
00117 netsnmp_disable_this_loghandler(netsnmp_log_handler *logh)
00118 {
00119     if (!logh || (0 == logh->enabled))
00120         return;
00121     logh->enabled = 0;
00122     --logh_enabled;
00123     netsnmp_assert(logh_enabled >= 0);
00124 }
00125 
00126 void
00127 netsnmp_enable_this_loghandler(netsnmp_log_handler *logh)
00128 {
00129     if (!logh || (0 != logh->enabled))
00130         return;
00131     logh->enabled = 1;
00132     ++logh_enabled;
00133 }
00134 
00135 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00136 void
00137 netsnmp_enable_filelog(netsnmp_log_handler *logh, int dont_zero_log);
00138 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00139 
00140 #ifndef HAVE_VSNPRINTF
00141                 /*
00142                  * Need to use the UCD-provided one 
00143                  */
00144 int             vsnprintf(char *str, size_t count, const char *fmt,
00145                           va_list arg);
00146 #endif
00147 
00148 void
00149 parse_config_logOption(const char *token, char *cptr)
00150 {
00151   int my_argc = 0 ;
00152   char **my_argv = NULL;
00153 
00154   snmp_log_options( cptr, my_argc, my_argv );
00155 }
00156 
00157 void
00158 init_snmp_logging(void)
00159 {
00160     netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "logTimestamp", 
00161                          NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_LOG_TIMESTAMP);
00162     register_prenetsnmp_mib_handler("snmp", "logOption",
00163                                     parse_config_logOption, NULL, "string");
00164 
00165 }
00166 
00167 void
00168 shutdown_snmp_logging(void)
00169 {
00170    snmp_disable_log();
00171    while(NULL != logh_head)
00172       netsnmp_remove_loghandler( logh_head );
00173 }
00174 
00175 /*
00176  * These definitions handle 4.2 systems without additional syslog facilities.
00177  */
00178 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00179 #ifndef LOG_CONS
00180 #define LOG_CONS        0       /* Don't bother if not defined... */
00181 #endif
00182 #ifndef LOG_PID
00183 #define LOG_PID         0       /* Don't bother if not defined... */
00184 #endif
00185 #ifndef LOG_LOCAL0
00186 #define LOG_LOCAL0      0
00187 #endif
00188 #ifndef LOG_LOCAL1
00189 #define LOG_LOCAL1      0
00190 #endif
00191 #ifndef LOG_LOCAL2
00192 #define LOG_LOCAL2      0
00193 #endif
00194 #ifndef LOG_LOCAL3
00195 #define LOG_LOCAL3      0
00196 #endif
00197 #ifndef LOG_LOCAL4
00198 #define LOG_LOCAL4      0
00199 #endif
00200 #ifndef LOG_LOCAL5
00201 #define LOG_LOCAL5      0
00202 #endif
00203 #ifndef LOG_LOCAL6
00204 #define LOG_LOCAL6      0
00205 #endif
00206 #ifndef LOG_LOCAL7
00207 #define LOG_LOCAL7      0
00208 #endif
00209 #ifndef LOG_DAEMON
00210 #define LOG_DAEMON      0
00211 #endif
00212 #ifndef LOG_USER
00213 #define LOG_USER        0
00214 #endif
00215 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00216 
00217 /* Set line buffering mode for a stream. */
00218 void
00219 netsnmp_set_line_buffering(FILE *stream)
00220 {
00221 #if defined(WIN32)
00222     /*
00223      * According to MSDN, the Microsoft Visual Studio C runtime library does
00224      * not support line buffering, so turn off buffering completely.
00225      * See also http://msdn.microsoft.com/en-us/library/86cebhfs(VS.71).aspx.
00226      */
00227     setvbuf(stream, NULL, _IONBF, BUFSIZ);
00228 #elif defined(HAVE_SETLINEBUF)
00229     /* setlinefunction() is a function from the BSD Unix API. */
00230     setlinebuf(stream);
00231 #else
00232     /* See also the C89 or C99 standard for more information about setvbuf(). */
00233     setvbuf(stream, NULL, _IOLBF, BUFSIZ);
00234 #endif
00235 }
00236 
00237 /*
00238  * Decodes log priority.
00239  * @param optarg - IN - priority to decode, "0" or "0-7"
00240  *                 OUT - points to last character after the decoded priority
00241  * @param pri_max - OUT - maximum priority (i.e. 0x7 from "0-7")
00242  */
00243 int
00244 decode_priority( char **optarg, int *pri_max )
00245 {
00246     int pri_low = LOG_DEBUG;
00247 
00248     if (*optarg == NULL)
00249         return -1;
00250 
00251     switch (**optarg) {
00252         case '0': 
00253         case '!': 
00254             pri_low = LOG_EMERG;
00255             break;
00256         case '1': 
00257         case 'a': 
00258         case 'A': 
00259             pri_low = LOG_ALERT;
00260             break;
00261         case '2': 
00262         case 'c': 
00263         case 'C': 
00264             pri_low = LOG_CRIT;
00265             break;
00266         case '3': 
00267         case 'e': 
00268         case 'E': 
00269             pri_low = LOG_ERR;
00270             break;
00271         case '4': 
00272         case 'w': 
00273         case 'W': 
00274             pri_low = LOG_WARNING;
00275             break;
00276         case '5': 
00277         case 'n': 
00278         case 'N': 
00279             pri_low = LOG_NOTICE;
00280             break;
00281         case '6': 
00282         case 'i': 
00283         case 'I': 
00284             pri_low = LOG_INFO;
00285             break;
00286         case '7': 
00287         case 'd': 
00288         case 'D': 
00289             pri_low = LOG_DEBUG;
00290             break;
00291         default: 
00292             fprintf(stderr, "invalid priority: %c\n",**optarg);
00293             return -1;
00294     }
00295     *optarg = *optarg+1;
00296 
00297     if (pri_max && **optarg=='-') {
00298         *optarg = *optarg + 1; /* skip '-' */
00299         *pri_max = decode_priority( optarg, NULL );
00300         if (*pri_max == -1) return -1;
00301         if (pri_low < *pri_max) { 
00302             int tmp = pri_low; 
00303             pri_low = *pri_max; 
00304             *pri_max = tmp; 
00305         }
00306 
00307     }
00308     return pri_low;
00309 }
00310 
00311 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00312 int
00313 decode_facility( char *optarg )
00314 {
00315     if (optarg == NULL)
00316         return -1;
00317 
00318     switch (*optarg) {
00319     case 'd':
00320     case 'D':
00321         return LOG_DAEMON;
00322     case 'u':
00323     case 'U':
00324         return LOG_USER;
00325     case '0':
00326         return LOG_LOCAL0;
00327     case '1':
00328         return LOG_LOCAL1;
00329     case '2':
00330         return LOG_LOCAL2;
00331     case '3':
00332         return LOG_LOCAL3;
00333     case '4':
00334         return LOG_LOCAL4;
00335     case '5':
00336         return LOG_LOCAL5;
00337     case '6':
00338         return LOG_LOCAL6;
00339     case '7':
00340         return LOG_LOCAL7;
00341     default:
00342         fprintf(stderr, "invalid syslog facility: %c\n",*optarg);
00343         return -1;
00344     }
00345 }
00346 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00347 
00348 int
00349 snmp_log_options(char *optarg, int argc, char *const *argv)
00350 {
00351     char           *cp = optarg;
00352         /*
00353          * Hmmm... this doesn't seem to work.
00354          * The main agent 'getopt' handling assumes
00355          *   that the -L option takes an argument,
00356          *   and objects if this is missing.
00357          * Trying to differentiate between
00358          *   new-style "-Lx", and old-style "-L xx"
00359          *   is likely to be a major headache.
00360          */
00361     char            missing_opt = 'e';  /* old -L is new -Le */
00362     int             priority = LOG_DEBUG;
00363     int             pri_max  = LOG_EMERG;
00364     int             inc_optind = 0;
00365     netsnmp_log_handler *logh;
00366 
00367     DEBUGMSGT(("logging:options", "optarg: '%s', argc %d, argv '%s'\n",
00368                optarg, argc, argv ? argv[0] : "NULL"));
00369     optarg++;
00370     if (!*cp)
00371         cp = &missing_opt;
00372 
00373     /*
00374      * Support '... -Lx=value ....' syntax
00375      */
00376     if (*optarg == '=') {
00377         optarg++;
00378     }
00379     /*
00380      * and '.... "-Lx value" ....'  (*with* the quotes)
00381      */
00382     while (*optarg && isspace((unsigned char)(*optarg))) {
00383         optarg++;
00384     }
00385     /*
00386      * Finally, handle ".... -Lx value ...." syntax
00387      *   (*without* surrounding quotes)
00388      */
00389     if ((!*optarg) && (NULL != argv)) {
00390         /*
00391          * We've run off the end of the argument
00392          *  so move on to the next.
00393          * But we might not actually need it, so don't
00394          *  increment optind just yet!
00395          */
00396         optarg = argv[optind];
00397         inc_optind = 1;
00398     }
00399 
00400     DEBUGMSGT(("logging:options", "*cp: '%c'\n", *cp));
00401     switch (*cp) {
00402 
00403 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
00404     /*
00405      * Log to Standard Error
00406      */
00407     case 'E':
00408         priority = decode_priority( &optarg, &pri_max );
00409         if (priority == -1)  return -1;
00410         if (inc_optind)
00411             optind++;
00412         /* Fallthrough */
00413     case 'e':
00414         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR, priority);
00415         if (logh) {
00416             netsnmp_set_line_buffering(stderr);
00417             logh->pri_max = pri_max;
00418             logh->token   = strdup("stderr");
00419         }
00420         break;
00421 
00422     /*
00423      * Log to Standard Output
00424      */
00425     case 'O':
00426         priority = decode_priority( &optarg, &pri_max );
00427         if (priority == -1)  return -1;
00428         if (inc_optind)
00429             optind++;
00430         /* Fallthrough */
00431     case 'o':
00432         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR, priority);
00433         if (logh) {
00434             netsnmp_set_line_buffering(stdout);
00435             logh->pri_max = pri_max;
00436             logh->token   = strdup("stdout");
00437             logh->imagic  = 1;      /* stdout, not stderr */
00438         }
00439         break;
00440 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
00441 
00442     /*
00443      * Log to a named file
00444      */
00445     case 'F':
00446         priority = decode_priority( &optarg, &pri_max );
00447         if (priority == -1 || !argv)  return -1;
00448         optarg = argv[++optind];
00449         /* Fallthrough */
00450 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00451     case 'f':
00452         if (inc_optind)
00453             optind++;
00454         if (!optarg) {
00455             fprintf(stderr, "Missing log file\n");
00456             return -1;
00457         }
00458         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, priority);
00459         if (logh) {
00460             logh->pri_max = pri_max;
00461             logh->token   = strdup(optarg);
00462             netsnmp_enable_filelog(logh,
00463                                    netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00464                                                           NETSNMP_DS_LIB_APPEND_LOGFILES));
00465         }
00466         break;
00467 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00468 
00469 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00470     /*
00471      * Log to syslog
00472      */
00473     case 'S':
00474         priority = decode_priority( &optarg, &pri_max );
00475         if (priority == -1 || !argv)  return -1;
00476         if (!optarg[0]) {
00477             /* The command line argument with priority does not contain log
00478              * facility. The facility must be in next argument then. */
00479             optind++;
00480             if (optind < argc)
00481                 optarg = argv[optind];
00482         }
00483         /* Fallthrough */
00484     case 's':
00485         if (inc_optind)
00486             optind++;
00487         if (!optarg) {
00488             fprintf(stderr, "Missing syslog facility\n");
00489             return -1;
00490         }
00491         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_SYSLOG, priority);
00492         if (logh) {
00493             int facility = decode_facility(optarg);
00494             if (facility == -1)  return -1;
00495             logh->pri_max = pri_max;
00496             logh->token   = strdup(snmp_log_syslogname(NULL));
00497             logh->magic   = (void *)(intptr_t)facility;
00498             snmp_enable_syslog_ident(snmp_log_syslogname(NULL), facility);
00499         }
00500         break;
00501 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00502 
00503     /*
00504      * Don't log 
00505      */
00506     case 'N':
00507         priority = decode_priority( &optarg, &pri_max );
00508         if (priority == -1)  return -1;
00509         if (inc_optind)
00510             optind++;
00511         /* Fallthrough */
00512     case 'n':
00513         /*
00514          * disable all logs to clean them up (close files, etc),
00515          * remove all log handlers, then register a null handler.
00516          */
00517         snmp_disable_log();
00518         while(NULL != logh_head)
00519             netsnmp_remove_loghandler( logh_head );
00520         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_NONE, priority);
00521         if (logh) {
00522             logh->pri_max = pri_max;
00523         }
00524         break;
00525 
00526     default:
00527         fprintf(stderr, "Unknown logging option passed to -L: %c.\n", *cp);
00528         return -1;
00529     }
00530     return 0;
00531 }
00532 
00533 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00534 char *
00535 snmp_log_syslogname(const char *pstr)
00536 {
00537   if (pstr)
00538     strncpy (syslogname, pstr, sizeof(syslogname));
00539 
00540   return syslogname;
00541 }
00542 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00543 
00544 void
00545 snmp_log_options_usage(const char *lead, FILE * outf)
00546 {
00547     const char *pri1_msg = " for level 'pri' and above";
00548     const char *pri2_msg = " for levels 'p1' to 'p2'";
00549 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
00550     fprintf(outf, "%se:           log to standard error\n", lead);
00551     fprintf(outf, "%so:           log to standard output\n", lead);
00552 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
00553     fprintf(outf, "%sn:           don't log at all\n", lead);
00554 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00555     fprintf(outf, "%sf file:      log to the specified file\n", lead);
00556 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00557 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00558     fprintf(outf, "%ss facility:  log to syslog (via the specified facility)\n", lead);
00559 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00560     fprintf(outf, "\n%s(variants)\n", lead);
00561 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00562     fprintf(outf, "%s[EON] pri:   log to standard error, output or /dev/null%s\n", lead, pri1_msg);
00563     fprintf(outf, "%s[EON] p1-p2: log to standard error, output or /dev/null%s\n", lead, pri2_msg);
00564 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00565     fprintf(outf, "%s[FS] pri token:    log to file/syslog%s\n", lead, pri1_msg);
00566     fprintf(outf, "%s[FS] p1-p2 token:  log to file/syslog%s\n", lead, pri2_msg);
00567 }
00568 
00575 int
00576 snmp_get_do_logging(void)
00577 {
00578     return (logh_enabled > 0);
00579 }
00580 
00581 
00582 static char    *
00583 sprintf_stamp(time_t * now, char *sbuf)
00584 {
00585     time_t          Now;
00586     struct tm      *tm;
00587 
00588     if (now == NULL) {
00589         now = &Now;
00590         time(now);
00591     }
00592     tm = localtime(now);
00593     sprintf(sbuf, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d ",
00594             tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
00595             tm->tm_hour, tm->tm_min, tm->tm_sec);
00596     return sbuf;
00597 }
00598 
00599 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00600 void
00601 snmp_disable_syslog_entry(netsnmp_log_handler *logh)
00602 {
00603     if (!logh || !logh->enabled || logh->type != NETSNMP_LOGHANDLER_SYSLOG)
00604         return;
00605 
00606 #ifdef WIN32
00607     if (logh->magic) {
00608         HANDLE eventlog_h = (HANDLE)logh->magic;
00609         CloseEventLog(eventlog_h);
00610         logh->magic = NULL;
00611     }
00612 #else
00613     closelog();
00614     logh->imagic  = 0;
00615 #endif
00616 
00617     netsnmp_disable_this_loghandler(logh);
00618 }
00619 
00620 void
00621 snmp_disable_syslog(void)
00622 {
00623     netsnmp_log_handler *logh;
00624 
00625     for (logh = logh_head; logh; logh = logh->next)
00626         if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_SYSLOG)
00627             snmp_disable_syslog_entry(logh);
00628 }
00629 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00630 
00631 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00632 void
00633 snmp_disable_filelog_entry(netsnmp_log_handler *logh)
00634 {
00635     if (!logh /* || !logh->enabled */ || logh->type != NETSNMP_LOGHANDLER_FILE)
00636         return;
00637 
00638     if (logh->magic) {
00639         fputs("\n", (FILE*)logh->magic);        /* XXX - why? */
00640         fclose((FILE*)logh->magic);
00641         logh->magic   = NULL;
00642     }
00643     netsnmp_disable_this_loghandler(logh);
00644 }
00645 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00646 
00647 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00648 void
00649 snmp_disable_filelog(void)
00650 {
00651     netsnmp_log_handler *logh;
00652 
00653     for (logh = logh_head; logh; logh = logh->next)
00654         if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_FILE)
00655             snmp_disable_filelog_entry(logh);
00656 }
00657 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00658 
00659 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
00660 /*
00661  * returns that status of stderr logging
00662  *
00663  * @retval 0 : stderr logging disabled
00664  * @retval 1 : stderr logging enabled
00665  */
00666 int
00667 snmp_stderrlog_status(void)
00668 {
00669     netsnmp_log_handler *logh;
00670 
00671     for (logh = logh_head; logh; logh = logh->next)
00672         if (logh->enabled && (logh->type == NETSNMP_LOGHANDLER_STDOUT ||
00673                               logh->type == NETSNMP_LOGHANDLER_STDERR)) {
00674             return 1;
00675        }
00676 
00677     return 0;
00678 }
00679 
00680 void
00681 snmp_disable_stderrlog(void)
00682 {
00683     netsnmp_log_handler *logh;
00684 
00685     for (logh = logh_head; logh; logh = logh->next)
00686         if (logh->enabled && (logh->type == NETSNMP_LOGHANDLER_STDOUT ||
00687                               logh->type == NETSNMP_LOGHANDLER_STDERR)) {
00688             netsnmp_disable_this_loghandler(logh);
00689         }
00690 }
00691 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
00692 
00693 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG
00694 void
00695 snmp_disable_calllog(void)
00696 {
00697     netsnmp_log_handler *logh;
00698 
00699     for (logh = logh_head; logh; logh = logh->next)
00700         if (logh->enabled && logh->type == NETSNMP_LOGHANDLER_CALLBACK) {
00701             netsnmp_disable_this_loghandler(logh);
00702         }
00703 }
00704 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG */
00705 
00706 void
00707 snmp_disable_log(void)
00708 {
00709     netsnmp_log_handler *logh;
00710 
00711     for (logh = logh_head; logh; logh = logh->next) {
00712 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00713         if (logh->type == NETSNMP_LOGHANDLER_SYSLOG)
00714             snmp_disable_syslog_entry(logh);
00715 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00716 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00717         if (logh->type == NETSNMP_LOGHANDLER_FILE)
00718             snmp_disable_filelog_entry(logh);
00719 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00720         netsnmp_disable_this_loghandler(logh);
00721     }
00722 }
00723 
00724 /*
00725  * close and reopen all file based logs, to allow logfile
00726  * rotation.
00727  */
00728 void
00729 netsnmp_logging_restart(void)
00730 {
00731     netsnmp_log_handler *logh;
00732     int doneone = 0;
00733 
00734     for (logh = logh_head; logh; logh = logh->next) {
00735         if (0 == logh->enabled)
00736             continue;
00737 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00738         if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) {
00739             snmp_disable_syslog_entry(logh);
00740             snmp_enable_syslog_ident(logh->token,(int)(intptr_t)logh->magic);
00741             doneone = 1;
00742         }
00743 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00744 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00745         if (logh->type == NETSNMP_LOGHANDLER_FILE && !doneone) {
00746             snmp_disable_filelog_entry(logh);
00751             netsnmp_enable_filelog(logh, 1);
00752         }
00753 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00754     }
00755 }
00756 
00757 /* ================================================== */
00758 
00759 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
00760 void
00761 snmp_enable_syslog(void)
00762 {
00763     snmp_enable_syslog_ident(snmp_log_syslogname(NULL), LOG_DAEMON);
00764 }
00765 
00766 void
00767 snmp_enable_syslog_ident(const char *ident, const int facility)
00768 {
00769     netsnmp_log_handler *logh;
00770     int                  found = 0;
00771     int                  enable = 1;
00772 #ifdef WIN32
00773     HANDLE               eventlog_h;
00774 #else
00775     void                *eventlog_h = NULL;
00776 #endif
00777 
00778     snmp_disable_syslog();      /* ??? */
00779 #ifdef WIN32
00780     eventlog_h = OpenEventLog(NULL, ident);
00781     if (eventlog_h == NULL) {
00782             /*
00783              * Hmmm.....
00784              * Maybe disable this handler, and log the error ?
00785              */
00786         fprintf(stderr, "Could not open event log for %s. "
00787                 "Last error: 0x%x\n", ident, GetLastError());
00788         enable = 0;
00789     }
00790 #else
00791     openlog(snmp_log_syslogname(ident), LOG_CONS | LOG_PID, facility);
00792 #endif
00793 
00794     for (logh = logh_head; logh; logh = logh->next)
00795         if (logh->type == NETSNMP_LOGHANDLER_SYSLOG) {
00796             logh->magic   = (void*)eventlog_h;
00797             logh->imagic  = enable;     /* syslog open */
00798             if (logh->enabled && (0 == enable))
00799                 netsnmp_disable_this_loghandler(logh);
00800             else if ((0 == logh->enabled) && enable)
00801                 netsnmp_enable_this_loghandler(logh);
00802             found         = 1;
00803         }
00804 
00805     if (!found) {
00806         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_SYSLOG,
00807                                            LOG_DEBUG );
00808         if (logh) {
00809             logh->magic    = (void*)eventlog_h;
00810             logh->token    = strdup(ident);
00811             logh->imagic   = enable;    /* syslog open */
00812             if (logh->enabled && (0 == enable))
00813                 netsnmp_disable_this_loghandler(logh);
00814             else if ((0 == logh->enabled) && enable)
00815                 netsnmp_enable_this_loghandler(logh);
00816         }
00817     }
00818 }
00819 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
00820 
00821 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
00822 void
00823 netsnmp_enable_filelog(netsnmp_log_handler *logh, int dont_zero_log)
00824 {
00825     FILE *logfile;
00826 
00827     if (!logh)
00828         return;
00829 
00830     if (!logh->magic) {
00831         logfile = fopen(logh->token, dont_zero_log ? "a" : "w");
00832         if (!logfile) {
00833             snmp_log_perror(logh->token);
00834             return;
00835         }
00836         logh->magic = (void*)logfile;
00837         netsnmp_set_line_buffering(logfile);
00838     }
00839     netsnmp_enable_this_loghandler(logh);
00840 }
00841 
00842 void
00843 snmp_enable_filelog(const char *logfilename, int dont_zero_log)
00844 {
00845     netsnmp_log_handler *logh;
00846 
00847     /*
00848      * don't disable ALL filelogs whenever a new one is enabled.
00849      * this prevents '-Lf file' from working in snmpd, as the
00850      * call to set up /var/log/snmpd.log will disable the previous
00851      * log setup.
00852      * snmp_disable_filelog();
00853      */
00854 
00855     if (logfilename) {
00856         logh = netsnmp_find_loghandler( logfilename );
00857         if (!logh) {
00858             logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE,
00859                                                LOG_DEBUG );
00860             if (logh)
00861                 logh->token = strdup(logfilename);
00862         }
00863         if (logh)
00864             netsnmp_enable_filelog(logh, dont_zero_log);
00865     } else {
00866         for (logh = logh_head; logh; logh = logh->next)
00867             if (logh->type == NETSNMP_LOGHANDLER_FILE)
00868                 netsnmp_enable_filelog(logh, dont_zero_log);
00869     }
00870 }
00871 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
00872 
00873 
00874 #ifndef NETSNMP_FEATURE_REMOVE_ENABLE_STDERRLOG
00875 /* used in the perl modules and ip-mib/ipv4InterfaceTable/ipv4InterfaceTable_subagent.c */
00876 void
00877 snmp_enable_stderrlog(void)
00878 {
00879     netsnmp_log_handler *logh;
00880     int                  found = 0;
00881 
00882     for (logh = logh_head; logh; logh = logh->next)
00883         if (logh->type == NETSNMP_LOGHANDLER_STDOUT ||
00884             logh->type == NETSNMP_LOGHANDLER_STDERR) {
00885             netsnmp_enable_this_loghandler(logh);
00886             found         = 1;
00887         }
00888 
00889     if (!found) {
00890         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_STDERR,
00891                                            LOG_DEBUG );
00892         if (logh)
00893             logh->token    = strdup("stderr");
00894     }
00895 }
00896 #endif /* NETSNMP_FEATURE_REMOVE_ENABLE_STDERRLOG */
00897 
00898 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG
00899 void
00900 snmp_enable_calllog(void)       /* XXX - or take a callback routine ??? */
00901 {
00902     netsnmp_log_handler *logh;
00903     int                  found = 0;
00904 
00905     for (logh = logh_head; logh; logh = logh->next)
00906         if (logh->type == NETSNMP_LOGHANDLER_CALLBACK) {
00907             netsnmp_enable_this_loghandler(logh);
00908             found         = 1;
00909         }
00910 
00911     if (!found) {
00912         logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK,
00913                                            LOG_DEBUG );
00914         if (logh)
00915             logh->token    = strdup("callback");
00916     }
00917 }
00918 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_CALLLOG */
00919 
00920 
00921 /* ==================================================== */
00922 
00923 
00924 netsnmp_log_handler *
00925 netsnmp_find_loghandler( const char *token )
00926 {
00927     netsnmp_log_handler *logh;
00928     if (!token)
00929         return NULL;
00930 
00931     for (logh = logh_head; logh; logh = logh->next)
00932         if (logh->token && !strcmp( token, logh->token ))
00933             break;
00934 
00935     return logh;
00936 }
00937 
00938 int
00939 netsnmp_add_loghandler( netsnmp_log_handler *logh )
00940 {
00941     int i;
00942     netsnmp_log_handler *logh2;
00943 
00944     if (!logh)
00945         return 0;
00946 
00947     /*
00948      * Find the appropriate point for the new entry...
00949      *   (logh2 will point to the entry immediately following)
00950      */
00951     for (logh2 = logh_head; logh2; logh2 = logh2->next)
00952         if ( logh2->priority >= logh->priority )
00953             break;
00954 
00955     /*
00956      * ... and link it into the main list.
00957      */
00958     if (logh2) {
00959         if (logh2->prev)
00960             logh2->prev->next = logh;
00961         else
00962             logh_head = logh;
00963         logh->next  = logh2;
00964         logh2->prev = logh;
00965     } else if (logh_head ) {
00966         /*
00967          * If logh2 is NULL, we're tagging on to the end
00968          */
00969         for (logh2 = logh_head; logh2->next; logh2 = logh2->next)
00970             ;
00971         logh2->next = logh;
00972     } else {
00973         logh_head = logh;
00974     }
00975 
00976     /*
00977      * Also tweak the relevant priority-'index' array.
00978      */
00979     for (i=LOG_EMERG; i<=logh->priority; i++)
00980         if (!logh_priorities[i] ||
00981              logh_priorities[i]->priority >= logh->priority)
00982              logh_priorities[i] = logh;
00983 
00984     return 1;
00985 }
00986 
00987 netsnmp_log_handler *
00988 netsnmp_register_loghandler( int type, int priority )
00989 {
00990     netsnmp_log_handler *logh;
00991 
00992     logh = SNMP_MALLOC_TYPEDEF(netsnmp_log_handler);
00993     if (!logh)
00994         return NULL;
00995 
00996     DEBUGMSGT(("logging:register", "registering log type %d with pri %d\n",
00997                type, priority));
00998 
00999     logh->type     = type;
01000     switch ( type ) {
01001     case NETSNMP_LOGHANDLER_STDOUT:
01002         logh->imagic  = 1;
01003         /* fallthrough */
01004 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
01005     case NETSNMP_LOGHANDLER_STDERR:
01006         logh->handler = log_handler_stdouterr;
01007         break;
01008 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
01009 
01010 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
01011     case NETSNMP_LOGHANDLER_FILE:
01012         logh->handler = log_handler_file;
01013         logh->imagic  = 1;
01014         break;
01015 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
01016 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
01017     case NETSNMP_LOGHANDLER_SYSLOG:
01018         logh->handler = log_handler_syslog;
01019         break;
01020 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
01021     case NETSNMP_LOGHANDLER_CALLBACK:
01022         logh->handler = log_handler_callback;
01023         break;
01024     case NETSNMP_LOGHANDLER_NONE:
01025         logh->handler = log_handler_null;
01026         break;
01027     default:
01028         free(logh);
01029         return NULL;
01030     }
01031     logh->priority = priority;
01032     netsnmp_enable_this_loghandler(logh);
01033     netsnmp_add_loghandler( logh );
01034     return logh;
01035 }
01036 
01037 
01038 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_LOGHANDLER
01039 int
01040 netsnmp_enable_loghandler( const char *token )
01041 {
01042     netsnmp_log_handler *logh;
01043 
01044     logh = netsnmp_find_loghandler( token );
01045     if (!logh)
01046         return 0;
01047     netsnmp_enable_this_loghandler(logh);
01048     return 1;
01049 }
01050 
01051 
01052 int
01053 netsnmp_disable_loghandler( const char *token )
01054 {
01055     netsnmp_log_handler *logh;
01056 
01057     logh = netsnmp_find_loghandler( token );
01058     if (!logh)
01059         return 0;
01060     netsnmp_disable_this_loghandler(logh);
01061     return 1;
01062 }
01063 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_ENABLE_LOGHANDLER */
01064 
01065 int
01066 netsnmp_remove_loghandler( netsnmp_log_handler *logh )
01067 {
01068     int i;
01069     if (!logh)
01070         return 0;
01071 
01072     if (logh->prev)
01073         logh->prev->next = logh->next;
01074     else
01075         logh_head = logh->next;
01076 
01077     if (logh->next)
01078         logh->next->prev = logh->prev;
01079 
01080     for (i=LOG_EMERG; i<=logh->priority; i++)
01081         logh_priorities[i] = NULL;
01082     free(NETSNMP_REMOVE_CONST(char*, logh->token));
01083     SNMP_FREE(logh);
01084 
01085     return 1;
01086 }
01087 
01088 /* ==================================================== */
01089 
01090 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
01091 int
01092 log_handler_stdouterr(  netsnmp_log_handler* logh, int pri, const char *str)
01093 {
01094     static int      newline = 1;         /* MTCRITICAL_RESOURCE */
01095     const char     *newline_ptr;
01096     char            sbuf[40];
01097 
01098     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
01099                                NETSNMP_DS_LIB_LOG_TIMESTAMP) && newline) {
01100         sprintf_stamp(NULL, sbuf);
01101     } else {
01102         strcpy(sbuf, "");
01103     }
01104     /*
01105      * Remember whether or not the current line ends with a newline for the
01106      * next call of log_handler_stdouterr().
01107      */
01108     newline_ptr = strrchr(str, '\n');
01109     newline = newline_ptr && newline_ptr[1] == 0;
01110 
01111     if (logh->imagic)
01112        printf(         "%s%s", sbuf, str);
01113     else
01114        fprintf(stderr, "%s%s", sbuf, str);
01115 
01116     return 1;
01117 }
01118 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
01119 
01120 
01121 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG
01122 #ifdef WIN32
01123 int
01124 log_handler_syslog(  netsnmp_log_handler* logh, int pri, const char *str)
01125 {
01126     WORD            etype;
01127     LPCTSTR         event_msg[2];
01128     HANDLE          eventlog_h = logh->magic;
01129 
01130         /*
01131          **  EVENT TYPES:
01132          **
01133          **  Information (EVENTLOG_INFORMATION_TYPE)
01134          **      Information events indicate infrequent but significant
01135          **      successful operations.
01136          **  Warning (EVENTLOG_WARNING_TYPE)
01137          **      Warning events indicate problems that are not immediately
01138          **      significant, but that may indicate conditions that could
01139          **      cause future problems. Resource consumption is a good
01140          **      candidate for a warning event.
01141          **  Error (EVENTLOG_ERROR_TYPE)
01142          **      Error events indicate significant problems that the user
01143          **      should know about. Error events usually indicate a loss of
01144          **      functionality or data.
01145          */
01146     switch (pri) {
01147         case LOG_EMERG:
01148         case LOG_ALERT:
01149         case LOG_CRIT:
01150         case LOG_ERR:
01151             etype = EVENTLOG_ERROR_TYPE;
01152             break;
01153         case LOG_WARNING:
01154             etype = EVENTLOG_WARNING_TYPE;
01155             break;
01156         case LOG_NOTICE:
01157         case LOG_INFO:
01158         case LOG_DEBUG:
01159             etype = EVENTLOG_INFORMATION_TYPE;
01160             break;
01161         default:
01162             etype = EVENTLOG_INFORMATION_TYPE;
01163             break;
01164     }
01165     event_msg[0] = str;
01166     event_msg[1] = NULL;
01167     /* NOTE: 4th parameter must match winservice.mc:MessageId value */
01168     if (!ReportEvent(eventlog_h, etype, 0, 100, NULL, 1, 0, event_msg, NULL)) {
01169             /*
01170              * Hmmm.....
01171              * Maybe disable this handler, and log the error ?
01172              */
01173         fprintf(stderr, "Could not report event.  Last error: 0x%x\n",
01174                         GetLastError());
01175         return 0;
01176     }
01177     return 1;
01178 }
01179 #else
01180 int
01181 log_handler_syslog(  netsnmp_log_handler* logh, int pri, const char *str)
01182 {
01183         /*
01184          * XXX
01185          * We've got three items of information to work with:
01186          *     Is the syslog currently open?
01187          *     What ident string to use?
01188          *     What facility to log to?
01189          *
01190          * We've got two "magic" locations (imagic & magic) plus the token
01191          */
01192     if (!(logh->imagic)) {
01193         const char *ident    = logh->token;
01194         int   facility = (int)(intptr_t)logh->magic;
01195         if (!ident)
01196             ident = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
01197                                           NETSNMP_DS_LIB_APPTYPE);
01198         openlog(ident, LOG_CONS | LOG_PID, facility);
01199         logh->imagic = 1;
01200     }
01201     syslog( pri, "%s", str );
01202     return 1;
01203 }
01204 #endif /* !WIN32 */
01205 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */
01206 
01207 
01208 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE
01209 int
01210 log_handler_file(    netsnmp_log_handler* logh, int pri, const char *str)
01211 {
01212     FILE           *fhandle;
01213     char            sbuf[40];
01214 
01215     /*
01216      * We use imagic to save information about whether the next output
01217      * will start a new line, and thus might need a timestamp
01218      */
01219     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
01220                                NETSNMP_DS_LIB_LOG_TIMESTAMP) && logh->imagic) {
01221         sprintf_stamp(NULL, sbuf);
01222     } else {
01223         strcpy(sbuf, "");
01224     }
01225 
01226     /*
01227      * If we haven't already opened the file, then do so.
01228      * Save the filehandle pointer for next time.
01229      *
01230      * Note that this should still work, even if the file
01231      * is closed in the meantime (e.g. a regular "cleanup" sweep)
01232      */
01233     fhandle = (FILE*)logh->magic;
01234     if (!logh->magic) {
01235         fhandle = fopen(logh->token, "a+");
01236         if (!fhandle)
01237             return 0;
01238         logh->magic = (void*)fhandle;
01239     }
01240     fprintf(fhandle, "%s%s", sbuf, str);
01241     fflush(fhandle);
01242     logh->imagic = str[strlen(str) - 1] == '\n';
01243     return 1;
01244 }
01245 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */
01246 
01247 int
01248 log_handler_callback(netsnmp_log_handler* logh, int pri, const char *str)
01249 {
01250         /*
01251          * XXX - perhaps replace 'snmp_call_callbacks' processing
01252          *       with individual callback log_handlers ??
01253          */
01254     struct snmp_log_message slm;
01255     int             dodebug = snmp_get_do_debugging();
01256 
01257     slm.priority = pri;
01258     slm.msg = str;
01259     if (dodebug)            /* turn off debugging inside the callbacks else will loop */
01260         snmp_set_do_debugging(0);
01261     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, &slm);
01262     if (dodebug)
01263         snmp_set_do_debugging(dodebug);
01264     return 1;
01265 }
01266 
01267 int
01268 log_handler_null(    netsnmp_log_handler* logh, int pri, const char *str)
01269 {
01270     /*
01271      * Dummy log handler - just throw away the error completely
01272      * You probably don't really want to do this!
01273      */
01274     return 1;
01275 }
01276 
01277 void
01278 snmp_log_string(int priority, const char *str)
01279 {
01280 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
01281     static int stderr_enabled = 0;
01282     static netsnmp_log_handler lh = { 1, 0, 0, 0, "stderr",
01283                                       log_handler_stdouterr, 0, NULL,  NULL,
01284                                       NULL };
01285 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
01286     netsnmp_log_handler *logh;
01287 
01288     /*
01289      * We've got to be able to log messages *somewhere*!
01290      * If you don't want stderr logging, then enable something else.
01291      */
01292     if (0 == logh_enabled) {
01293 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
01294         if (!stderr_enabled) {
01295             ++stderr_enabled;
01296             netsnmp_set_line_buffering(stderr);
01297         }
01298         log_handler_stdouterr( &lh, priority, str );
01299 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
01300 
01301         return;
01302     }
01303 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO
01304     else if (stderr_enabled) {
01305         stderr_enabled = 0;
01306         log_handler_stdouterr( &lh, LOG_INFO,
01307                                "Log handling defined - disabling stderr\n" );
01308     }
01309 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */
01310         
01311 
01312     /*
01313      * Start at the given priority, and work "upwards"....
01314      */
01315     logh = logh_priorities[priority];
01316     for ( ; logh; logh = logh->next ) {
01317         /*
01318          * ... but skipping any handlers with a "maximum priority"
01319          *     that we have already exceeded. And don't forget to
01320          *     ensure this logging is turned on (see snmp_disable_stderrlog
01321          *     and its cohorts).
01322          */
01323         if (logh->enabled && (priority >= logh->pri_max))
01324             logh->handler( logh, priority, str );
01325     }
01326 }
01327 
01328 /* ==================================================== */
01329 
01330 
01362 int
01363 snmp_vlog(int priority, const char *format, va_list ap)
01364 {
01365     char            buffer[LOGLENGTH];
01366     int             length;
01367     char           *dynamic;
01368     va_list         aq;
01369 
01370     va_copy(aq, ap);
01371     length = vsnprintf(buffer, LOGLENGTH, format, ap);
01372     va_end(ap);
01373 
01374     if (length == 0) {
01375 #ifdef NEED_VA_END_AFTER_VA_COPY
01376         va_end(aq);
01377 #endif
01378         return (0);             /* Empty string */
01379     }
01380 
01381     if (length == -1) {
01382         snmp_log_string(LOG_ERR, "Could not format log-string\n");
01383 #ifdef NEED_VA_END_AFTER_VA_COPY
01384         va_end(aq);
01385 #endif
01386         return (-1);
01387     }
01388 
01389     if (length < LOGLENGTH) {
01390         snmp_log_string(priority, buffer);
01391 #ifdef NEED_VA_END_AFTER_VA_COPY
01392         va_end(aq);
01393 #endif
01394         return (0);
01395     }
01396 
01397     dynamic = (char *) malloc(length + 1);
01398     if (dynamic == NULL) {
01399         snmp_log_string(LOG_ERR,
01400                         "Could not allocate memory for log-message\n");
01401         snmp_log_string(priority, buffer);
01402 #ifdef NEED_VA_END_AFTER_VA_COPY
01403         va_end(aq);
01404 #endif
01405         return (-2);
01406     }
01407 
01408     vsnprintf(dynamic, length + 1, format, aq);
01409     snmp_log_string(priority, dynamic);
01410     free(dynamic);
01411     va_end(aq);
01412     return 0;
01413 }
01414 
01422 int
01423 snmp_log(int priority, const char *format, ...)
01424 {
01425     va_list         ap;
01426     int             ret;
01427     va_start(ap, format);
01428     ret = snmp_vlog(priority, format, ap);
01429     va_end(ap);
01430     return (ret);
01431 }
01432 
01433 /*
01434  * log a critical error.
01435  */
01436 void
01437 snmp_log_perror(const char *s)
01438 {
01439     char           *error = strerror(errno);
01440     if (s) {
01441         if (error)
01442             snmp_log(LOG_ERR, "%s: %s\n", s, error);
01443         else
01444             snmp_log(LOG_ERR, "%s: Error %d out-of-range\n", s, errno);
01445     } else {
01446         if (error)
01447             snmp_log(LOG_ERR, "%s\n", error);
01448         else
01449             snmp_log(LOG_ERR, "Error %d out-of-range\n", errno);
01450     }
01451 }
01452 
01453 #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_EXTERNAL
01454 /* external access to logh_head variable */
01455 netsnmp_log_handler  *
01456 get_logh_head(void)
01457 {
01458         return logh_head;
01459 }
01460 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_EXTERNAL */
01461