1/* $NetBSD: msyslog.c,v 1.7 2020/05/29 20:15:38 christos Exp $ */ 2 3/* 4 * msyslog - either send a message to the terminal or print it on 5 * the standard output. 6 * 7 * Converted to use varargs, much better ... jks 8 */ 9 10#ifdef HAVE_CONFIG_H 11# include <config.h> 12#endif 13 14#include <sys/types.h> 15#ifdef HAVE_UNISTD_H 16# include <unistd.h> 17#endif 18#include <stdio.h> 19 20#include "ntp_string.h" 21#include "ntp.h" 22#include "ntp_debug.h" 23#include "ntp_syslog.h" 24 25#ifdef SYS_WINNT 26# include <stdarg.h> 27# include "..\ports\winnt\libntp\messages.h" 28#endif 29 30 31int syslogit = TRUE; 32int msyslog_term = FALSE; /* duplicate to stdout/err */ 33int msyslog_term_pid = TRUE; 34int msyslog_include_timestamp = TRUE; 35FILE * syslog_file; 36char * syslog_fname; 37char * syslog_abs_fname; 38 39/* libntp default ntp_syslogmask is all bits lit */ 40#define INIT_NTP_SYSLOGMASK ~(u_int32)0 41u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK; 42 43extern char const * progname; 44 45/* Declare the local functions */ 46void addto_syslog (int, const char *); 47#ifdef VSNPRINTF_PERCENT_M 48#define format_errmsg(buf, len, fmt, error) (fmt) 49#else 50static const char *format_errmsg(char *, size_t, const char *, int) 51 NTP_FORMAT_ARG(3); 52 53/* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */ 54static const char * 55format_errmsg( 56 char * nfmt, 57 size_t lennfmt, 58 const char * fmt, 59 int errval 60 ) 61{ 62 char errmsg[256]; 63 char c; 64 char *n; 65 const char *f; 66 size_t len; 67 68 n = nfmt; 69 f = fmt; 70 while ((c = *f++) != '\0' && n < (nfmt + lennfmt - 1)) { 71 if (c != '%') { 72 *n++ = c; 73 continue; 74 } 75 if ((c = *f++) != 'm') { 76 *n++ = '%'; 77 if ('\0' == c) 78 break; 79 *n++ = c; 80 continue; 81 } 82 errno_to_str(errval, errmsg, sizeof(errmsg)); 83 len = strlen(errmsg); 84 85 /* Make sure we have enough space for the error message */ 86 if ((n + len) < (nfmt + lennfmt - 1)) { 87 memcpy(n, errmsg, len); 88 n += len; 89 } 90 } 91 *n = '\0'; 92 return nfmt; 93} 94#endif /* VSNPRINTF_PERCENT_M */ 95 96 97/* 98 * errno_to_str() - a thread-safe strerror() replacement. 99 * Hides the varied signatures of strerror_r(). 100 * For Windows, we have: 101 * #define errno_to_str isc_strerror 102 */ 103#ifndef errno_to_str 104void 105errno_to_str( 106 int err, 107 char * buf, 108 size_t bufsiz 109 ) 110{ 111# if defined(STRERROR_R_CHAR_P) || !HAVE_DECL_STRERROR_R 112 char * pstatic; 113 114 buf[0] = '\0'; 115# ifdef STRERROR_R_CHAR_P 116 pstatic = strerror_r(err, buf, bufsiz); 117# else 118 pstatic = strerror(err); 119# endif 120 if (NULL == pstatic && '\0' == buf[0]) 121 snprintf(buf, bufsiz, "%s(%d): errno %d", 122# ifdef STRERROR_R_CHAR_P 123 "strerror_r", 124# else 125 "strerror", 126# endif 127 err, errno); 128 /* protect against believing an int return is a pointer */ 129 else if (pstatic != buf && pstatic > (char *)bufsiz) 130 strlcpy(buf, pstatic, bufsiz); 131# else 132 int rc; 133 134 rc = strerror_r(err, buf, bufsiz); 135 if (rc < 0) 136 snprintf(buf, bufsiz, "strerror_r(%d): errno %d", 137 err, errno); 138# endif 139} 140#endif /* errno_to_str */ 141 142 143/* 144 * addto_syslog() 145 * This routine adds the contents of a buffer to the syslog or an 146 * application-specific logfile. 147 */ 148void 149addto_syslog( 150 int level, 151 const char * msg 152 ) 153{ 154 static char const * prevcall_progname; 155 static char const * prog; 156 const char nl[] = "\n"; 157 const char empty[] = ""; 158 FILE * term_file; 159 int log_to_term; 160 int log_to_file; 161 int pid; 162 const char * nl_or_empty; 163 const char * human_time; 164 165 /* setup program basename static var prog if needed */ 166 if (progname != prevcall_progname) { 167 prevcall_progname = progname; 168 prog = strrchr(progname, DIR_SEP); 169 if (prog != NULL) 170 prog++; 171 else 172 prog = progname; 173 } 174 175 log_to_term = msyslog_term; 176 log_to_file = FALSE; 177#if !defined(VMS) && !defined(SYS_VXWORKS) 178 if (syslogit) 179 syslog(level, "%s", msg); 180 else 181#endif 182 if (syslog_file != NULL) 183 log_to_file = TRUE; 184 else 185 log_to_term = TRUE; 186#if DEBUG 187 if (debug > 0) 188 log_to_term = TRUE; 189#endif 190 if (!(log_to_file || log_to_term)) 191 return; 192 193 /* syslog() adds the timestamp, name, and pid */ 194 if (msyslog_include_timestamp) 195 human_time = humanlogtime(); 196 else /* suppress gcc pot. uninit. warning */ 197 human_time = NULL; 198 if (msyslog_term_pid || log_to_file) 199 pid = getpid(); 200 else /* suppress gcc pot. uninit. warning */ 201 pid = -1; 202 203 /* syslog() adds trailing \n if not present */ 204 if ('\n' != msg[strlen(msg) - 1]) 205 nl_or_empty = nl; 206 else 207 nl_or_empty = empty; 208 209 if (log_to_term) { 210 term_file = (level <= LOG_ERR) 211 ? stderr 212 : stdout; 213 if (msyslog_include_timestamp) 214 fprintf(term_file, "%s ", human_time); 215 if (msyslog_term_pid) 216 fprintf(term_file, "%s[%d]: ", prog, pid); 217 fprintf(term_file, "%s%s", msg, nl_or_empty); 218 fflush(term_file); 219 } 220 221 if (log_to_file) { 222 if (msyslog_include_timestamp) 223 fprintf(syslog_file, "%s ", human_time); 224 fprintf(syslog_file, "%s[%d]: %s%s", prog, pid, msg, 225 nl_or_empty); 226 fflush(syslog_file); 227 } 228} 229 230 231int 232mvsnprintf( 233 char * buf, 234 size_t bufsiz, 235 const char * fmt, 236 va_list ap 237 ) 238{ 239#ifndef VSNPRINTF_PERCENT_M 240 char fmtbuf[256]; 241#endif 242 int errval; 243 244 /* 245 * Save the error value as soon as possible 246 */ 247#ifdef SYS_WINNT 248 errval = GetLastError(); 249 if (NO_ERROR == errval) 250#endif /* SYS_WINNT */ 251 errval = errno; 252 253#ifdef VSNPRINTF_PERCENT_M 254 errno = errval; 255#endif 256 return vsnprintf(buf, bufsiz, 257 format_errmsg(fmtbuf, sizeof(fmtbuf), fmt, errval), ap); 258} 259 260 261int 262mvfprintf( 263 FILE * fp, 264 const char * fmt, 265 va_list ap 266 ) 267{ 268#ifndef VSNPRINTF_PERCENT_M 269 char fmtbuf[256]; 270#endif 271 int errval; 272 273 /* 274 * Save the error value as soon as possible 275 */ 276#ifdef SYS_WINNT 277 errval = GetLastError(); 278 if (NO_ERROR == errval) 279#endif /* SYS_WINNT */ 280 errval = errno; 281 282#ifdef VSNPRINTF_PERCENT_M 283 errno = errval; 284#endif 285 return vfprintf(fp, 286 format_errmsg(fmtbuf, sizeof(fmtbuf), fmt, errval), 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