xref: /freebsd/contrib/ntp/libntp/msyslog.c (revision f95c930e6701aa675377bc5871ea490dd565eeba)
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 
29 int	syslogit = TRUE;
30 int	msyslog_term = FALSE;	/* duplicate to stdout/err */
31 int	msyslog_term_pid = TRUE;
32 int	msyslog_include_timestamp = TRUE;
33 FILE *	syslog_file;
34 char *	syslog_fname;
35 char *	syslog_abs_fname;
36 
37 /* libntp default ntp_syslogmask is all bits lit */
38 #define INIT_NTP_SYSLOGMASK	~(u_int32)0
39 u_int32 ntp_syslogmask = INIT_NTP_SYSLOGMASK;
40 
41 extern	char *	progname;
42 
43 /* Declare the local functions */
44 void	addto_syslog	(int, const char *);
45 #ifndef VSNPRINTF_PERCENT_M
46 void	format_errmsg	(char *, size_t, const char *, int);
47 
48 /* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */
49 void
50 format_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
98 void
99 errno_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  */
142 void
143 addto_syslog(
144 	int		level,
145 	const char *	msg
146 	)
147 {
148 	static char *	prevcall_progname;
149 	static char *	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 
225 int
226 mvsnprintf(
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 
258 int
259 mvfprintf(
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 
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 	size_t	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 	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 
360 
361 /*
362  * Initialize the logging
363  *
364  * Called once per process, including forked children.
365  */
366 void
367 init_logging(
368 	const char *	name,
369 	u_int32		def_syslogmask,
370 	int		is_daemon
371 	)
372 {
373 	static int	was_daemon;
374 	const char *	cp;
375 	const char *	pname;
376 
377 	/*
378 	 * ntpd defaults to only logging sync-category events, when
379 	 * NLOG() is used to conditionalize.  Other libntp clients
380 	 * leave it alone so that all NLOG() conditionals will fire.
381 	 * This presumes all bits lit in ntp_syslogmask can't be
382 	 * configured via logconfig and all lit is thereby a sentinel
383 	 * that ntp_syslogmask is still at its default from libntp,
384 	 * keeping in mind this function is called in forked children
385 	 * where it has already been called in the parent earlier.
386 	 * Forked children pass 0 for def_syslogmask.
387 	 */
388 	if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
389 	    0 != def_syslogmask)
390 		ntp_syslogmask = def_syslogmask; /* set more via logconfig */
391 
392 	/*
393 	 * Logging.  This may actually work on the gizmo board.  Find a name
394 	 * to log with by using the basename
395 	 */
396 	cp = strrchr(name, DIR_SEP);
397 	if (NULL == cp)
398 		pname = name;
399 	else
400 		pname = 1 + cp;	/* skip DIR_SEP */
401 	progname = estrdup(pname);
402 #ifdef SYS_WINNT			/* strip ".exe" */
403 	cp = strrchr(progname, '.');
404 	if (NULL != cp && !strcasecmp(cp, ".exe"))
405 		progname[cp - progname] = '\0';
406 #endif
407 
408 #if !defined(VMS)
409 
410 	if (is_daemon)
411 		was_daemon = TRUE;
412 # ifndef LOG_DAEMON
413 	openlog(progname, LOG_PID);
414 # else /* LOG_DAEMON */
415 
416 #  ifndef LOG_NTP
417 #	define	LOG_NTP LOG_DAEMON
418 #  endif
419 	openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
420 						    ? LOG_NTP
421 						    : 0);
422 #  ifdef DEBUG
423 	if (debug)
424 		setlogmask(LOG_UPTO(LOG_DEBUG));
425 	else
426 #  endif /* DEBUG */
427 		setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
428 # endif /* LOG_DAEMON */
429 #endif	/* !VMS */
430 }
431 
432 
433 /*
434  * change_logfile()
435  *
436  * Used to change from syslog to a logfile, or from one logfile to
437  * another, and to reopen logfiles after forking.  On systems where
438  * ntpd forks, deals with converting relative logfile paths to
439  * absolute (root-based) because we reopen logfiles after the current
440  * directory has changed.
441  */
442 int
443 change_logfile(
444 	const char *	fname,
445 	int		leave_crumbs
446 	)
447 {
448 	FILE *		new_file;
449 	const char *	log_fname;
450 	char *		abs_fname;
451 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
452 	char		curdir[512];
453 	size_t		cd_octets;
454 	size_t		octets;
455 #endif	/* POSIX */
456 
457 	NTP_REQUIRE(fname != NULL);
458 	log_fname = fname;
459 
460 	/*
461 	 * In a forked child of a parent which is logging to a file
462 	 * instead of syslog, syslog_file will be NULL and both
463 	 * syslog_fname and syslog_abs_fname will be non-NULL.
464 	 * If we are given the same filename previously opened
465 	 * and it's still open, there's nothing to do here.
466 	 */
467 	if (syslog_file != NULL && syslog_fname != NULL &&
468 	    0 == strcmp(syslog_fname, log_fname))
469 		return 0;
470 
471 	if (0 == strcmp(log_fname, "stderr")) {
472 		new_file = stderr;
473 		abs_fname = estrdup(log_fname);
474 	} else if (0 == strcmp(log_fname, "stdout")) {
475 		new_file = stdout;
476 		abs_fname = estrdup(log_fname);
477 	} else {
478 		if (syslog_fname != NULL &&
479 		    0 == strcmp(log_fname, syslog_fname))
480 			log_fname = syslog_abs_fname;
481 #if !defined(SYS_WINNT) && !defined(SYS_VXWORKS) && !defined(VMS)
482 		if (log_fname != syslog_abs_fname &&
483 		    DIR_SEP != log_fname[0] &&
484 		    0 != strcmp(log_fname, "stderr") &&
485 		    0 != strcmp(log_fname, "stdout") &&
486 		    NULL != getcwd(curdir, sizeof(curdir))) {
487 			cd_octets = strlen(curdir);
488 			/* trim any trailing '/' */
489 			if (cd_octets > 1 &&
490 			    DIR_SEP == curdir[cd_octets - 1])
491 				cd_octets--;
492 			octets = cd_octets;
493 			octets += 1;	/* separator '/' */
494 			octets += strlen(log_fname);
495 			octets += 1;	/* NUL terminator */
496 			abs_fname = emalloc(octets);
497 			snprintf(abs_fname, octets, "%.*s%c%s",
498 				 (int)cd_octets, curdir, DIR_SEP,
499 				 log_fname);
500 		} else
501 #endif
502 			abs_fname = estrdup(log_fname);
503 		TRACE(1, ("attempting to open log %s\n", abs_fname));
504 		new_file = fopen(abs_fname, "a");
505 	}
506 
507 	if (NULL == new_file) {
508 		free(abs_fname);
509 		return -1;
510 	}
511 
512 	/* leave a pointer in the old log */
513 	if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
514 		msyslog(LOG_NOTICE, "switching logging to file %s",
515 			abs_fname);
516 
517 	if (syslog_file != NULL &&
518 	    syslog_file != stderr && syslog_file != stdout &&
519 	    fileno(syslog_file) != fileno(new_file))
520 		fclose(syslog_file);
521 	syslog_file = new_file;
522 	if (log_fname == syslog_abs_fname) {
523 		free(abs_fname);
524 	} else {
525 		if (syslog_abs_fname != NULL &&
526 		    syslog_abs_fname != syslog_fname)
527 			free(syslog_abs_fname);
528 		if (syslog_fname != NULL)
529 			free(syslog_fname);
530 		syslog_fname = estrdup(log_fname);
531 		syslog_abs_fname = abs_fname;
532 	}
533 	syslogit = FALSE;
534 
535 	return 0;
536 }
537 
538 
539 /*
540  * setup_logfile()
541  *
542  * Redirect logging to a file if requested with -l/--logfile or via
543  * ntp.conf logfile directive.
544  *
545  * This routine is invoked three different times in the sequence of a
546  * typical daemon ntpd with DNS lookups to do.  First it is invoked in
547  * the original ntpd process, then again in the daemon after closing
548  * all descriptors.  In both of those cases, ntp.conf has not been
549  * processed, so only -l/--logfile will trigger logfile redirection in
550  * those invocations.  Finally, if DNS names are resolved, the worker
551  * child invokes this routine after its fork and close of all
552  * descriptors.  In this case, ntp.conf has been processed and any
553  * "logfile" directive needs to be honored in the child as well.
554  */
555 void
556 setup_logfile(
557 	const char *	name
558 	)
559 {
560 	if (NULL == syslog_fname && NULL != name) {
561 		if (-1 == change_logfile(name, TRUE))
562 			msyslog(LOG_ERR, "Cannot open log file %s, %m",
563 				name);
564 		return ;
565 	}
566 	if (NULL == syslog_fname)
567 		return;
568 
569 	if (-1 == change_logfile(syslog_fname, FALSE))
570 		msyslog(LOG_ERR, "Cannot reopen log file %s, %m",
571 			syslog_fname);
572 }
573