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