1 /* $NetBSD: msyslog.c,v 1.8 2024/08/18 20:47:13 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 31 int syslogit = TRUE; 32 int msyslog_term = FALSE; /* duplicate to stdout/err */ 33 int msyslog_term_pid = TRUE; 34 int msyslog_include_timestamp = TRUE; 35 FILE * syslog_file; 36 char * syslog_fname; 37 char * syslog_abs_fname; 38 39 /* libntp default ntp_syslogmask is all bits lit */ 40 #define INIT_NTP_SYSLOGMASK ~(u_int32)0 41 u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK; 42 43 extern char const * progname; 44 45 /* Declare the local functions */ 46 void addto_syslog (int, const char *); 47 #ifdef VSNPRINTF_PERCENT_M 48 #define format_errmsg(buf, len, fmt, error) (fmt) 49 #else 50 static 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 */ 54 static const char * 55 format_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 104 void 105 errno_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 */ 148 void 149 addto_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 231 int 232 mvsnprintf( 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 261 int 262 mvfprintf( 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 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 va_list ap; 352 353 va_start(ap, fmt); 354 mvsyslog(level, fmt, ap); 355 va_end(ap); 356 } 357 358 359 void 360 mvsyslog( 361 int level, 362 const char * fmt, 363 va_list ap 364 ) 365 { 366 char buf[1024]; 367 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