xref: /netbsd-src/external/bsd/ntp/dist/libntp/msyslog.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: msyslog.c,v 1.5 2016/01/08 21:35: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 
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 #ifndef VSNPRINTF_PERCENT_M
48 void	format_errmsg	(char *, size_t, const char *, int);
49 
50 /* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */
51 void
52 format_errmsg(
53 	char *		nfmt,
54 	size_t		lennfmt,
55 	const char *	fmt,
56 	int		errval
57 	)
58 {
59 	char errmsg[256];
60 	char c;
61 	char *n;
62 	const char *f;
63 	size_t len;
64 
65 	n = nfmt;
66 	f = fmt;
67 	while ((c = *f++) != '\0' && n < (nfmt + lennfmt - 1)) {
68 		if (c != '%') {
69 			*n++ = c;
70 			continue;
71 		}
72 		if ((c = *f++) != 'm') {
73 			*n++ = '%';
74 			if ('\0' == c)
75 				break;
76 			*n++ = c;
77 			continue;
78 		}
79 		errno_to_str(errval, errmsg, sizeof(errmsg));
80 		len = strlen(errmsg);
81 
82 		/* Make sure we have enough space for the error message */
83 		if ((n + len) < (nfmt + lennfmt - 1)) {
84 			memcpy(n, errmsg, len);
85 			n += len;
86 		}
87 	}
88 	*n = '\0';
89 }
90 #endif	/* VSNPRINTF_PERCENT_M */
91 
92 
93 /*
94  * errno_to_str() - a thread-safe strerror() replacement.
95  *		    Hides the varied signatures of strerror_r().
96  *		    For Windows, we have:
97  *			#define errno_to_str isc_strerror
98  */
99 #ifndef errno_to_str
100 void
101 errno_to_str(
102 	int	err,
103 	char *	buf,
104 	size_t	bufsiz
105 	)
106 {
107 # if defined(STRERROR_R_CHAR_P) || !HAVE_DECL_STRERROR_R
108 	char *	pstatic;
109 
110 	buf[0] = '\0';
111 #  ifdef STRERROR_R_CHAR_P
112 	pstatic = strerror_r(err, buf, bufsiz);
113 #  else
114 	pstatic = strerror(err);
115 #  endif
116 	if (NULL == pstatic && '\0' == buf[0])
117 		snprintf(buf, bufsiz, "%s(%d): errno %d",
118 #  ifdef STRERROR_R_CHAR_P
119 			 "strerror_r",
120 #  else
121 			 "strerror",
122 #  endif
123 			 err, errno);
124 	/* protect against believing an int return is a pointer */
125 	else if (pstatic != buf && pstatic > (char *)bufsiz)
126 		strlcpy(buf, pstatic, bufsiz);
127 # else
128 	int	rc;
129 
130 	rc = strerror_r(err, buf, bufsiz);
131 	if (rc < 0)
132 		snprintf(buf, bufsiz, "strerror_r(%d): errno %d",
133 			 err, errno);
134 # endif
135 }
136 #endif	/* errno_to_str */
137 
138 
139 /*
140  * addto_syslog()
141  * This routine adds the contents of a buffer to the syslog or an
142  * application-specific logfile.
143  */
144 void
145 addto_syslog(
146 	int		level,
147 	const char *	msg
148 	)
149 {
150 	static char const *	prevcall_progname;
151 	static char const *	prog;
152 	const char	nl[] = "\n";
153 	const char	empty[] = "";
154 	FILE *		term_file;
155 	int		log_to_term;
156 	int		log_to_file;
157 	int		pid;
158 	const char *	nl_or_empty;
159 	const char *	human_time;
160 
161 	/* setup program basename static var prog if needed */
162 	if (progname != prevcall_progname) {
163 		prevcall_progname = progname;
164 		prog = strrchr(progname, DIR_SEP);
165 		if (prog != NULL)
166 			prog++;
167 		else
168 			prog = progname;
169 	}
170 
171 	log_to_term = msyslog_term;
172 	log_to_file = FALSE;
173 #if !defined(VMS) && !defined(SYS_VXWORKS)
174 	if (syslogit)
175 		syslog(level, "%s", msg);
176 	else
177 #endif
178 		if (syslog_file != NULL)
179 			log_to_file = TRUE;
180 		else
181 			log_to_term = TRUE;
182 #if DEBUG
183 	if (debug > 0)
184 		log_to_term = TRUE;
185 #endif
186 	if (!(log_to_file || log_to_term))
187 		return;
188 
189 	/* syslog() adds the timestamp, name, and pid */
190 	if (msyslog_include_timestamp)
191 		human_time = humanlogtime();
192 	else	/* suppress gcc pot. uninit. warning */
193 		human_time = NULL;
194 	if (msyslog_term_pid || log_to_file)
195 		pid = getpid();
196 	else	/* suppress gcc pot. uninit. warning */
197 		pid = -1;
198 
199 	/* syslog() adds trailing \n if not present */
200 	if ('\n' != msg[strlen(msg) - 1])
201 		nl_or_empty = nl;
202 	else
203 		nl_or_empty = empty;
204 
205 	if (log_to_term) {
206 		term_file = (level <= LOG_ERR)
207 				? stderr
208 				: stdout;
209 		if (msyslog_include_timestamp)
210 			fprintf(term_file, "%s ", human_time);
211 		if (msyslog_term_pid)
212 			fprintf(term_file, "%s[%d]: ", prog, pid);
213 		fprintf(term_file, "%s%s", msg, nl_or_empty);
214 		fflush(term_file);
215 	}
216 
217 	if (log_to_file) {
218 		if (msyslog_include_timestamp)
219 			fprintf(syslog_file, "%s ", human_time);
220 		fprintf(syslog_file, "%s[%d]: %s%s", prog, pid, msg,
221 			nl_or_empty);
222 		fflush(syslog_file);
223 	}
224 }
225 
226 
227 int
228 mvsnprintf(
229 	char *		buf,
230 	size_t		bufsiz,
231 	const char *	fmt,
232 	va_list		ap
233 	)
234 {
235 #ifndef VSNPRINTF_PERCENT_M
236 	char		nfmt[256];
237 #else
238 	const char *	nfmt = fmt;
239 #endif
240 	int		errval;
241 
242 	/*
243 	 * Save the error value as soon as possible
244 	 */
245 #ifdef SYS_WINNT
246 	errval = GetLastError();
247 	if (NO_ERROR == errval)
248 #endif /* SYS_WINNT */
249 		errval = errno;
250 
251 #ifndef VSNPRINTF_PERCENT_M
252 	format_errmsg(nfmt, sizeof(nfmt), fmt, errval);
253 #else
254 	errno = errval;
255 #endif
256 	return vsnprintf(buf, bufsiz, nfmt, ap);
257 }
258 
259 
260 int
261 mvfprintf(
262 	FILE *		fp,
263 	const char *	fmt,
264 	va_list		ap
265 	)
266 {
267 #ifndef VSNPRINTF_PERCENT_M
268 	char		nfmt[256];
269 #else
270 	const char *	nfmt = fmt;
271 #endif
272 	int		errval;
273 
274 	/*
275 	 * Save the error value as soon as possible
276 	 */
277 #ifdef SYS_WINNT
278 	errval = GetLastError();
279 	if (NO_ERROR == errval)
280 #endif /* SYS_WINNT */
281 		errval = errno;
282 
283 #ifndef VSNPRINTF_PERCENT_M
284 	format_errmsg(nfmt, sizeof(nfmt), fmt, errval);
285 #else
286 	errno = errval;
287 #endif
288 	return vfprintf(fp, nfmt, ap);
289 }
290 
291 
292 int
293 mfprintf(
294 	FILE *		fp,
295 	const char *	fmt,
296 	...
297 	)
298 {
299 	va_list		ap;
300 	int		rc;
301 
302 	va_start(ap, fmt);
303 	rc = mvfprintf(fp, fmt, ap);
304 	va_end(ap);
305 
306 	return rc;
307 }
308 
309 
310 int
311 mprintf(
312 	const char *	fmt,
313 	...
314 	)
315 {
316 	va_list		ap;
317 	int		rc;
318 
319 	va_start(ap, fmt);
320 	rc = mvfprintf(stdout, fmt, ap);
321 	va_end(ap);
322 
323 	return rc;
324 }
325 
326 
327 int
328 msnprintf(
329 	char *		buf,
330 	size_t		bufsiz,
331 	const char *	fmt,
332 	...
333 	)
334 {
335 	va_list	ap;
336 	int	rc;
337 
338 	va_start(ap, fmt);
339 	rc = mvsnprintf(buf, bufsiz, fmt, ap);
340 	va_end(ap);
341 
342 	return rc;
343 }
344 
345 
346 void
347 msyslog(
348 	int		level,
349 	const char *	fmt,
350 	...
351 	)
352 {
353 	char	buf[1024];
354 	va_list	ap;
355 
356 	va_start(ap, fmt);
357 	mvsnprintf(buf, sizeof(buf), fmt, ap);
358 	va_end(ap);
359 	addto_syslog(level, buf);
360 }
361 
362 void
363 mvsyslog(
364 	int		level,
365 	const char *	fmt,
366 	va_list		ap
367 	)
368 {
369 	char	buf[1024];
370 	mvsnprintf(buf, sizeof(buf), fmt, ap);
371 	addto_syslog(level, buf);
372 }
373 
374 
375 /*
376  * Initialize the logging
377  *
378  * Called once per process, including forked children.
379  */
380 void
381 init_logging(
382 	const char *	name,
383 	u_int32		def_syslogmask,
384 	int		is_daemon
385 	)
386 {
387 	static int	was_daemon;
388 	char *		cp;
389 	const char *	pname;
390 
391 	/*
392 	 * ntpd defaults to only logging sync-category events, when
393 	 * NLOG() is used to conditionalize.  Other libntp clients
394 	 * leave it alone so that all NLOG() conditionals will fire.
395 	 * This presumes all bits lit in ntp_syslogmask can't be
396 	 * configured via logconfig and all lit is thereby a sentinel
397 	 * that ntp_syslogmask is still at its default from libntp,
398 	 * keeping in mind this function is called in forked children
399 	 * where it has already been called in the parent earlier.
400 	 * Forked children pass 0 for def_syslogmask.
401 	 */
402 	if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
403 	    0 != def_syslogmask)
404 		ntp_syslogmask = def_syslogmask; /* set more via logconfig */
405 
406 	/*
407 	 * Logging.  This may actually work on the gizmo board.  Find a name
408 	 * to log with by using the basename
409 	 */
410 	cp = strrchr(name, DIR_SEP);
411 	if (NULL == cp)
412 		pname = name;
413 	else
414 		pname = 1 + cp;	/* skip DIR_SEP */
415 	progname = estrdup(pname);
416 #ifdef SYS_WINNT			/* strip ".exe" */
417 	cp = strrchr(progname, '.');
418 	if (NULL != cp && !strcasecmp(cp, ".exe"))
419 		*cp = '\0';
420 #endif
421 
422 #if !defined(VMS)
423 
424 	if (is_daemon)
425 		was_daemon = TRUE;
426 # ifndef LOG_DAEMON
427 	openlog(progname, LOG_PID);
428 # else /* LOG_DAEMON */
429 
430 #  ifndef LOG_NTP
431 #	define	LOG_NTP LOG_DAEMON
432 #  endif
433 	openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
434 						    ? LOG_NTP
435 						    : 0);
436 #  ifdef DEBUG
437 	if (debug)
438 		setlogmask(LOG_UPTO(LOG_DEBUG));
439 	else
440 #  endif /* DEBUG */
441 		setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
442 # endif /* LOG_DAEMON */
443 #endif	/* !VMS */
444 }
445 
446 
447 /*
448  * change_logfile()
449  *
450  * Used to change from syslog to a logfile, or from one logfile to
451  * another, and to reopen logfiles after forking.  On systems where
452  * ntpd forks, deals with converting relative logfile paths to
453  * absolute (root-based) because we reopen logfiles after the current
454  * directory has changed.
455  */
456 int
457 change_logfile(
458 	const char *	fname,
459 	int		leave_crumbs
460 	)
461 {
462 	FILE *		new_file;
463 	const char *	log_fname;
464 	char *		abs_fname;
465 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
466 	char		curdir[512];
467 	size_t		cd_octets;
468 	size_t		octets;
469 #endif	/* POSIX */
470 
471 	REQUIRE(fname != NULL);
472 	log_fname = fname;
473 
474 	/*
475 	 * In a forked child of a parent which is logging to a file
476 	 * instead of syslog, syslog_file will be NULL and both
477 	 * syslog_fname and syslog_abs_fname will be non-NULL.
478 	 * If we are given the same filename previously opened
479 	 * and it's still open, there's nothing to do here.
480 	 */
481 	if (syslog_file != NULL && syslog_fname != NULL &&
482 	    0 == strcmp(syslog_fname, log_fname))
483 		return 0;
484 
485 	if (0 == strcmp(log_fname, "stderr")) {
486 		new_file = stderr;
487 		abs_fname = estrdup(log_fname);
488 	} else if (0 == strcmp(log_fname, "stdout")) {
489 		new_file = stdout;
490 		abs_fname = estrdup(log_fname);
491 	} else {
492 		if (syslog_fname != NULL &&
493 		    0 == strcmp(log_fname, syslog_fname))
494 			log_fname = syslog_abs_fname;
495 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
496 		if (log_fname != syslog_abs_fname &&
497 		    DIR_SEP != log_fname[0] &&
498 		    0 != strcmp(log_fname, "stderr") &&
499 		    0 != strcmp(log_fname, "stdout") &&
500 		    NULL != getcwd(curdir, sizeof(curdir))) {
501 			cd_octets = strlen(curdir);
502 			/* trim any trailing '/' */
503 			if (cd_octets > 1 &&
504 			    DIR_SEP == curdir[cd_octets - 1])
505 				cd_octets--;
506 			octets = cd_octets;
507 			octets += 1;	/* separator '/' */
508 			octets += strlen(log_fname);
509 			octets += 1;	/* NUL terminator */
510 			abs_fname = emalloc(octets);
511 			snprintf(abs_fname, octets, "%.*s%c%s",
512 				 (int)cd_octets, curdir, DIR_SEP,
513 				 log_fname);
514 		} else
515 #endif
516 			abs_fname = estrdup(log_fname);
517 		TRACE(1, ("attempting to open log %s\n", abs_fname));
518 		new_file = fopen(abs_fname, "a");
519 	}
520 
521 	if (NULL == new_file) {
522 		free(abs_fname);
523 		return -1;
524 	}
525 
526 	/* leave a pointer in the old log */
527 	if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
528 		msyslog(LOG_NOTICE, "switching logging to file %s",
529 			abs_fname);
530 
531 	if (syslog_file != NULL &&
532 	    syslog_file != stderr && syslog_file != stdout &&
533 	    fileno(syslog_file) != fileno(new_file))
534 		fclose(syslog_file);
535 	syslog_file = new_file;
536 	if (log_fname == syslog_abs_fname) {
537 		free(abs_fname);
538 	} else {
539 		if (syslog_abs_fname != NULL &&
540 		    syslog_abs_fname != syslog_fname)
541 			free(syslog_abs_fname);
542 		if (syslog_fname != NULL)
543 			free(syslog_fname);
544 		syslog_fname = estrdup(log_fname);
545 		syslog_abs_fname = abs_fname;
546 	}
547 	syslogit = FALSE;
548 
549 	return 0;
550 }
551 
552 
553 /*
554  * setup_logfile()
555  *
556  * Redirect logging to a file if requested with -l/--logfile or via
557  * ntp.conf logfile directive.
558  *
559  * This routine is invoked three different times in the sequence of a
560  * typical daemon ntpd with DNS lookups to do.  First it is invoked in
561  * the original ntpd process, then again in the daemon after closing
562  * all descriptors.  In both of those cases, ntp.conf has not been
563  * processed, so only -l/--logfile will trigger logfile redirection in
564  * those invocations.  Finally, if DNS names are resolved, the worker
565  * child invokes this routine after its fork and close of all
566  * descriptors.  In this case, ntp.conf has been processed and any
567  * "logfile" directive needs to be honored in the child as well.
568  */
569 void
570 setup_logfile(
571 	const char *	name
572 	)
573 {
574 	if (NULL == syslog_fname && NULL != name) {
575 		if (-1 == change_logfile(name, TRUE))
576 			msyslog(LOG_ERR, "Cannot open log file %s, %m",
577 				name);
578 		return ;
579 	}
580 	if (NULL == syslog_fname)
581 		return;
582 
583 	if (-1 == change_logfile(syslog_fname, FALSE))
584 		msyslog(LOG_ERR, "Cannot reopen log file %s, %m",
585 			syslog_fname);
586 }
587