Home | History | Annotate | Line # | Download | only in libntp
      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