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