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 29 int syslogit = TRUE; 30 int msyslog_term = FALSE; /* duplicate to stdout/err */ 31 int msyslog_term_pid = TRUE; 32 int msyslog_include_timestamp = TRUE; 33 FILE * syslog_file; 34 char * syslog_fname; 35 char * syslog_abs_fname; 36 37 /* libntp default ntp_syslogmask is all bits lit */ 38 #define INIT_NTP_SYSLOGMASK ~(u_int32)0 39 u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK; 40 41 extern char const * progname; 42 43 /* Declare the local functions */ 44 void addto_syslog (int, const char *); 45 #ifndef VSNPRINTF_PERCENT_M 46 void format_errmsg (char *, size_t, const char *, int); 47 48 /* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */ 49 void 50 format_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 98 void 99 errno_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 */ 142 void 143 addto_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 225 int 226 mvsnprintf( 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 258 int 259 mvfprintf( 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 290 int 291 mfprintf( 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 308 int 309 mprintf( 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 325 int 326 msnprintf( 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 344 void 345 msyslog( 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 360 void 361 mvsyslog( 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 */ 378 void 379 init_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 */ 454 int 455 change_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 */ 567 void 568 setup_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 /* 587 * Helper for unit tests, where stdout + stderr are piped to the same 588 * stream. This works moderately reliably only if both streams are 589 * unbuffered or line buffered. Unfortunately stdout can be fully 590 * buffered on pipes or files... 591 */ 592 int 593 change_iobufs( 594 int how 595 ) 596 { 597 int retv = 0; 598 599 # ifdef HAVE_SETVBUF 600 601 int mode; 602 603 switch (how) { 604 case 0 : mode = _IONBF; break; /* no buffering */ 605 case 1 : mode = _IOLBF; break; /* line buffering */ 606 case 2 : mode = _IOFBF; break; /* full buffering */ 607 default: mode = _IOLBF; break; /* line buffering */ 608 } 609 610 retv = 1; 611 if (setvbuf(stdout, NULL, mode, BUFSIZ) != 0) 612 retv = -1; 613 if (setvbuf(stderr, NULL, mode, BUFSIZ) != 0) 614 retv = -1; 615 616 # else 617 618 UNUSED_ARG(how); 619 620 # endif 621 622 return retv; 623 } 624