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