msyslog.c revision 358659
1/* 2 * msyslog - either send a message to the terminal or print it on 3 * the standard output. 4 * 5 * Converted to use varargs, much better ... jks 6 */ 7 8#ifdef HAVE_CONFIG_H 9# include <config.h> 10#endif 11 12#include <sys/types.h> 13#ifdef HAVE_UNISTD_H 14# include <unistd.h> 15#endif 16#include <stdio.h> 17 18#include "ntp_string.h" 19#include "ntp.h" 20#include "ntp_debug.h" 21#include "ntp_syslog.h" 22 23#ifdef SYS_WINNT 24# include <stdarg.h> 25# include "..\ports\winnt\libntp\messages.h" 26#endif 27 28 29int syslogit = TRUE; 30int msyslog_term = FALSE; /* duplicate to stdout/err */ 31int msyslog_term_pid = TRUE; 32int msyslog_include_timestamp = TRUE; 33FILE * syslog_file; 34char * syslog_fname; 35char * syslog_abs_fname; 36 37/* libntp default ntp_syslogmask is all bits lit */ 38#define INIT_NTP_SYSLOGMASK ~(u_int32)0 39u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK; 40 41extern char const * progname; 42 43/* Declare the local functions */ 44void addto_syslog (int, const char *); 45#ifndef VSNPRINTF_PERCENT_M 46void format_errmsg (char *, size_t, const char *, int); 47 48/* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */ 49void 50format_errmsg( 51 char * nfmt, 52 size_t lennfmt, 53 const char * fmt, 54 int errval 55 ) 56{ 57 char errmsg[256]; 58 char c; 59 char *n; 60 const char *f; 61 size_t len; 62 63 n = nfmt; 64 f = fmt; 65 while ((c = *f++) != '\0' && n < (nfmt + lennfmt - 1)) { 66 if (c != '%') { 67 *n++ = c; 68 continue; 69 } 70 if ((c = *f++) != 'm') { 71 *n++ = '%'; 72 if ('\0' == c) 73 break; 74 *n++ = c; 75 continue; 76 } 77 errno_to_str(errval, errmsg, sizeof(errmsg)); 78 len = strlen(errmsg); 79 80 /* Make sure we have enough space for the error message */ 81 if ((n + len) < (nfmt + lennfmt - 1)) { 82 memcpy(n, errmsg, len); 83 n += len; 84 } 85 } 86 *n = '\0'; 87} 88#endif /* VSNPRINTF_PERCENT_M */ 89 90 91/* 92 * errno_to_str() - a thread-safe strerror() replacement. 93 * Hides the varied signatures of strerror_r(). 94 * For Windows, we have: 95 * #define errno_to_str isc_strerror 96 */ 97#ifndef errno_to_str 98void 99errno_to_str( 100 int err, 101 char * buf, 102 size_t bufsiz 103 ) 104{ 105# if defined(STRERROR_R_CHAR_P) || !HAVE_DECL_STRERROR_R 106 char * pstatic; 107 108 buf[0] = '\0'; 109# ifdef STRERROR_R_CHAR_P 110 pstatic = strerror_r(err, buf, bufsiz); 111# else 112 pstatic = strerror(err); 113# endif 114 if (NULL == pstatic && '\0' == buf[0]) 115 snprintf(buf, bufsiz, "%s(%d): errno %d", 116# ifdef STRERROR_R_CHAR_P 117 "strerror_r", 118# else 119 "strerror", 120# endif 121 err, errno); 122 /* protect against believing an int return is a pointer */ 123 else if (pstatic != buf && pstatic > (char *)bufsiz) 124 strlcpy(buf, pstatic, bufsiz); 125# else 126 int rc; 127 128 rc = strerror_r(err, buf, bufsiz); 129 if (rc < 0) 130 snprintf(buf, bufsiz, "strerror_r(%d): errno %d", 131 err, errno); 132# endif 133} 134#endif /* errno_to_str */ 135 136 137/* 138 * addto_syslog() 139 * This routine adds the contents of a buffer to the syslog or an 140 * application-specific logfile. 141 */ 142void 143addto_syslog( 144 int level, 145 const char * msg 146 ) 147{ 148 static char const * prevcall_progname; 149 static char const * prog; 150 const char nl[] = "\n"; 151 const char empty[] = ""; 152 FILE * term_file; 153 int log_to_term; 154 int log_to_file; 155 int pid; 156 const char * nl_or_empty; 157 const char * human_time; 158 159 /* setup program basename static var prog if needed */ 160 if (progname != prevcall_progname) { 161 prevcall_progname = progname; 162 prog = strrchr(progname, DIR_SEP); 163 if (prog != NULL) 164 prog++; 165 else 166 prog = progname; 167 } 168 169 log_to_term = msyslog_term; 170 log_to_file = FALSE; 171#if !defined(VMS) && !defined(SYS_VXWORKS) 172 if (syslogit) 173 syslog(level, "%s", msg); 174 else 175#endif 176 if (syslog_file != NULL) 177 log_to_file = TRUE; 178 else 179 log_to_term = TRUE; 180#if DEBUG 181 if (debug > 0) 182 log_to_term = TRUE; 183#endif 184 if (!(log_to_file || log_to_term)) 185 return; 186 187 /* syslog() adds the timestamp, name, and pid */ 188 if (msyslog_include_timestamp) 189 human_time = humanlogtime(); 190 else /* suppress gcc pot. uninit. warning */ 191 human_time = NULL; 192 if (msyslog_term_pid || log_to_file) 193 pid = getpid(); 194 else /* suppress gcc pot. uninit. warning */ 195 pid = -1; 196 197 /* syslog() adds trailing \n if not present */ 198 if ('\n' != msg[strlen(msg) - 1]) 199 nl_or_empty = nl; 200 else 201 nl_or_empty = empty; 202 203 if (log_to_term) { 204 term_file = (level <= LOG_ERR) 205 ? stderr 206 : stdout; 207 if (msyslog_include_timestamp) 208 fprintf(term_file, "%s ", human_time); 209 if (msyslog_term_pid) 210 fprintf(term_file, "%s[%d]: ", prog, pid); 211 fprintf(term_file, "%s%s", msg, nl_or_empty); 212 fflush(term_file); 213 } 214 215 if (log_to_file) { 216 if (msyslog_include_timestamp) 217 fprintf(syslog_file, "%s ", human_time); 218 fprintf(syslog_file, "%s[%d]: %s%s", prog, pid, msg, 219 nl_or_empty); 220 fflush(syslog_file); 221 } 222} 223 224 225int 226mvsnprintf( 227 char * buf, 228 size_t bufsiz, 229 const char * fmt, 230 va_list ap 231 ) 232{ 233#ifndef VSNPRINTF_PERCENT_M 234 char nfmt[256]; 235#else 236 const char * nfmt = fmt; 237#endif 238 int errval; 239 240 /* 241 * Save the error value as soon as possible 242 */ 243#ifdef SYS_WINNT 244 errval = GetLastError(); 245 if (NO_ERROR == errval) 246#endif /* SYS_WINNT */ 247 errval = errno; 248 249#ifndef VSNPRINTF_PERCENT_M 250 format_errmsg(nfmt, sizeof(nfmt), fmt, errval); 251#else 252 errno = errval; 253#endif 254 return vsnprintf(buf, bufsiz, nfmt, ap); 255} 256 257 258int 259mvfprintf( 260 FILE * fp, 261 const char * fmt, 262 va_list ap 263 ) 264{ 265#ifndef VSNPRINTF_PERCENT_M 266 char nfmt[256]; 267#else 268 const char * nfmt = fmt; 269#endif 270 int errval; 271 272 /* 273 * Save the error value as soon as possible 274 */ 275#ifdef SYS_WINNT 276 errval = GetLastError(); 277 if (NO_ERROR == errval) 278#endif /* SYS_WINNT */ 279 errval = errno; 280 281#ifndef VSNPRINTF_PERCENT_M 282 format_errmsg(nfmt, sizeof(nfmt), fmt, errval); 283#else 284 errno = errval; 285#endif 286 return vfprintf(fp, nfmt, ap); 287} 288 289 290int 291mfprintf( 292 FILE * fp, 293 const char * fmt, 294 ... 295 ) 296{ 297 va_list ap; 298 int rc; 299 300 va_start(ap, fmt); 301 rc = mvfprintf(fp, fmt, ap); 302 va_end(ap); 303 304 return rc; 305} 306 307 308int 309mprintf( 310 const char * fmt, 311 ... 312 ) 313{ 314 va_list ap; 315 int rc; 316 317 va_start(ap, fmt); 318 rc = mvfprintf(stdout, fmt, ap); 319 va_end(ap); 320 321 return rc; 322} 323 324 325int 326msnprintf( 327 char * buf, 328 size_t bufsiz, 329 const char * fmt, 330 ... 331 ) 332{ 333 va_list ap; 334 int rc; 335 336 va_start(ap, fmt); 337 rc = mvsnprintf(buf, bufsiz, fmt, ap); 338 va_end(ap); 339 340 return rc; 341} 342 343 344void 345msyslog( 346 int level, 347 const char * fmt, 348 ... 349 ) 350{ 351 char buf[1024]; 352 va_list ap; 353 354 va_start(ap, fmt); 355 mvsnprintf(buf, sizeof(buf), fmt, ap); 356 va_end(ap); 357 addto_syslog(level, buf); 358} 359 360void 361mvsyslog( 362 int level, 363 const char * fmt, 364 va_list ap 365 ) 366{ 367 char buf[1024]; 368 mvsnprintf(buf, sizeof(buf), fmt, ap); 369 addto_syslog(level, buf); 370} 371 372 373/* 374 * Initialize the logging 375 * 376 * Called once per process, including forked children. 377 */ 378void 379init_logging( 380 const char * name, 381 u_int32 def_syslogmask, 382 int is_daemon 383 ) 384{ 385 static int was_daemon; 386 char * cp; 387 const char * pname; 388 389 /* 390 * ntpd defaults to only logging sync-category events, when 391 * NLOG() is used to conditionalize. Other libntp clients 392 * leave it alone so that all NLOG() conditionals will fire. 393 * This presumes all bits lit in ntp_syslogmask can't be 394 * configured via logconfig and all lit is thereby a sentinel 395 * that ntp_syslogmask is still at its default from libntp, 396 * keeping in mind this function is called in forked children 397 * where it has already been called in the parent earlier. 398 * Forked children pass 0 for def_syslogmask. 399 */ 400 if (INIT_NTP_SYSLOGMASK == ntp_syslogmask && 401 0 != def_syslogmask) 402 ntp_syslogmask = def_syslogmask; /* set more via logconfig */ 403 404 /* 405 * Logging. This may actually work on the gizmo board. Find a name 406 * to log with by using the basename 407 */ 408 cp = strrchr(name, DIR_SEP); 409 if (NULL == cp) 410 pname = name; 411 else 412 pname = 1 + cp; /* skip DIR_SEP */ 413 progname = estrdup(pname); 414#ifdef SYS_WINNT /* strip ".exe" */ 415 cp = strrchr(progname, '.'); 416 if (NULL != cp && !strcasecmp(cp, ".exe")) 417 *cp = '\0'; 418#endif 419 420#if !defined(VMS) 421 422 if (is_daemon) 423 was_daemon = TRUE; 424# ifndef LOG_DAEMON 425 openlog(progname, LOG_PID); 426# else /* LOG_DAEMON */ 427 428# ifndef LOG_NTP 429# define LOG_NTP LOG_DAEMON 430# endif 431 openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon) 432 ? LOG_NTP 433 : 0); 434# ifdef DEBUG 435 if (debug) 436 setlogmask(LOG_UPTO(LOG_DEBUG)); 437 else 438# endif /* DEBUG */ 439 setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ 440# endif /* LOG_DAEMON */ 441#endif /* !VMS */ 442} 443 444 445/* 446 * change_logfile() 447 * 448 * Used to change from syslog to a logfile, or from one logfile to 449 * another, and to reopen logfiles after forking. On systems where 450 * ntpd forks, deals with converting relative logfile paths to 451 * absolute (root-based) because we reopen logfiles after the current 452 * directory has changed. 453 */ 454int 455change_logfile( 456 const char * fname, 457 int leave_crumbs 458 ) 459{ 460 FILE * new_file; 461 const char * log_fname; 462 char * abs_fname; 463#if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS) 464 char curdir[512]; 465 size_t cd_octets; 466 size_t octets; 467#endif /* POSIX */ 468 469 REQUIRE(fname != NULL); 470 log_fname = fname; 471 472 /* 473 * In a forked child of a parent which is logging to a file 474 * instead of syslog, syslog_file will be NULL and both 475 * syslog_fname and syslog_abs_fname will be non-NULL. 476 * If we are given the same filename previously opened 477 * and it's still open, there's nothing to do here. 478 */ 479 if (syslog_file != NULL && syslog_fname != NULL && 480 0 == strcmp(syslog_fname, log_fname)) 481 return 0; 482 483 if (0 == strcmp(log_fname, "stderr")) { 484 new_file = stderr; 485 abs_fname = estrdup(log_fname); 486 } else if (0 == strcmp(log_fname, "stdout")) { 487 new_file = stdout; 488 abs_fname = estrdup(log_fname); 489 } else { 490 if (syslog_fname != NULL && 491 0 == strcmp(log_fname, syslog_fname)) 492 log_fname = syslog_abs_fname; 493#if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS) 494 if (log_fname != syslog_abs_fname && 495 DIR_SEP != log_fname[0] && 496 0 != strcmp(log_fname, "stderr") && 497 0 != strcmp(log_fname, "stdout") && 498 NULL != getcwd(curdir, sizeof(curdir))) { 499 cd_octets = strlen(curdir); 500 /* trim any trailing '/' */ 501 if (cd_octets > 1 && 502 DIR_SEP == curdir[cd_octets - 1]) 503 cd_octets--; 504 octets = cd_octets; 505 octets += 1; /* separator '/' */ 506 octets += strlen(log_fname); 507 octets += 1; /* NUL terminator */ 508 abs_fname = emalloc(octets); 509 snprintf(abs_fname, octets, "%.*s%c%s", 510 (int)cd_octets, curdir, DIR_SEP, 511 log_fname); 512 } else 513#endif 514 abs_fname = estrdup(log_fname); 515 TRACE(1, ("attempting to open log %s\n", abs_fname)); 516 new_file = fopen(abs_fname, "a"); 517 } 518 519 if (NULL == new_file) { 520 free(abs_fname); 521 return -1; 522 } 523 524 /* leave a pointer in the old log */ 525 if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname)) 526 msyslog(LOG_NOTICE, "switching logging to file %s", 527 abs_fname); 528 529 if (syslog_file != NULL && 530 syslog_file != stderr && syslog_file != stdout && 531 fileno(syslog_file) != fileno(new_file)) 532 fclose(syslog_file); 533 syslog_file = new_file; 534 if (log_fname == syslog_abs_fname) { 535 free(abs_fname); 536 } else { 537 if (syslog_abs_fname != NULL && 538 syslog_abs_fname != syslog_fname) 539 free(syslog_abs_fname); 540 if (syslog_fname != NULL) 541 free(syslog_fname); 542 syslog_fname = estrdup(log_fname); 543 syslog_abs_fname = abs_fname; 544 } 545 syslogit = FALSE; 546 547 return 0; 548} 549 550 551/* 552 * setup_logfile() 553 * 554 * Redirect logging to a file if requested with -l/--logfile or via 555 * ntp.conf logfile directive. 556 * 557 * This routine is invoked three different times in the sequence of a 558 * typical daemon ntpd with DNS lookups to do. First it is invoked in 559 * the original ntpd process, then again in the daemon after closing 560 * all descriptors. In both of those cases, ntp.conf has not been 561 * processed, so only -l/--logfile will trigger logfile redirection in 562 * those invocations. Finally, if DNS names are resolved, the worker 563 * child invokes this routine after its fork and close of all 564 * descriptors. In this case, ntp.conf has been processed and any 565 * "logfile" directive needs to be honored in the child as well. 566 */ 567void 568setup_logfile( 569 const char * name 570 ) 571{ 572 if (NULL == syslog_fname && NULL != name) { 573 if (-1 == change_logfile(name, TRUE)) 574 msyslog(LOG_ERR, "Cannot open log file %s, %m", 575 name); 576 return ; 577 } 578 if (NULL == syslog_fname) 579 return; 580 581 if (-1 == change_logfile(syslog_fname, FALSE)) 582 msyslog(LOG_ERR, "Cannot reopen log file %s, %m", 583 syslog_fname); 584} 585 586/* Helper for unit tests, where stdout + stderr are piped to the same 587 * stream. This works moderately reliable only if both streams are 588 * unbuffered or line buffered. Unfortunately stdout can be fully 589 * buffered on pipes or files... 590 */ 591int 592change_iobufs( 593 int how 594 ) 595{ 596 int retv = 0; 597 598# ifdef HAVE_SETVBUF 599 600 int mode; 601 602 switch (how) { 603 case 0 : mode = _IONBF; break; /* no buffering */ 604 case 1 : mode = _IOLBF; break; /* line buffering */ 605 case 2 : mode = _IOFBF; break; /* full buffering */ 606 default: mode = _IOLBF; break; /* line buffering */ 607 } 608 609 retv = 1; 610 if (setvbuf(stdout, NULL, mode, BUFSIZ) != 0) 611 retv = -1; 612 if (setvbuf(stderr, NULL, mode, BUFSIZ) != 0) 613 retv = -1; 614 615# else 616 617 UNUSED_ARG(how); 618 619# endif 620 621 return retv; 622} 623