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 #include "config.h"
9 
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include "ntp.h"
16 #include "ntp_debug.h"
17 #include "ntp_stdlib.h"
18 #include "ntp_syslog.h"
19 #include "lib_strbuf.h"
20 
21 /* start out with syslog and stderr, otherwise startup errors lost */
22 bool    syslogit = true;        /* log messages to syslog */
23 bool    termlogit = true;       /* duplicate to stdout/err */
24 bool	termlogit_pid = true;
25 bool	msyslog_include_timestamp = true;
26 
27 static FILE *	syslog_file;
28 static char *	syslog_fname;
29 static char *	syslog_abs_fname;
30 
31 int		debug;
32 
33 /* libntp default ntp_syslogmask is all bits lit */
34 #define INIT_NTP_SYSLOGMASK	~(uint32_t)0
35 uint32_t ntp_syslogmask = INIT_NTP_SYSLOGMASK;
36 
37 extern	char *	progname;
38 
39 /* Declare the local functions */
40 #define TIMESTAMP_LEN  128
41 static void	humanlogtime(char buf[TIMESTAMP_LEN]);
42 static void	addto_syslog	(int, const char *);
43 
44 
45 /* We don't want to clutter up the log with the year and day of the week,
46    etc.; just the minimal date and time.  */
47 static void
humanlogtime(char buf[TIMESTAMP_LEN])48 humanlogtime(char buf[TIMESTAMP_LEN])
49 {
50 	time_t		cursec;
51 	struct tm	tmbuf, *tm;
52 
53 	cursec = time(NULL);
54 	tm = localtime_r(&cursec, &tmbuf);
55 	if (!tm) {
56 		strlcpy(buf, "-- --- --:--:--", TIMESTAMP_LEN);
57 		return;
58 	}
59 
60 #ifdef ENABLE_CLASSIC_MODE
61 	const char * const months[12] = {
62 	    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63 	    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
64 	};
65 
66 	snprintf(buf, TIMESTAMP_LEN, "%2d %s %02d:%02d:%02d",
67 		 tm->tm_mday, months[tm->tm_mon],
68 		 tm->tm_hour, tm->tm_min, tm->tm_sec);
69 #else
70 	/* ISO 8601 is a better format, sort order equals time order */
71 	snprintf(buf, TIMESTAMP_LEN, "%04d-%02d-%02dT%02d:%02d:%02d",
72 		 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
73 		 tm->tm_hour, tm->tm_min, tm->tm_sec);
74 #endif /* ENABLE_CLASSIC_MODE */
75 }
76 
77 
78 /*
79  * addto_syslog()
80  * This routine adds the contents of a buffer to the syslog or an
81  * application-specific logfile.
82  */
83 static void
addto_syslog(int level,const char * msg)84 addto_syslog(
85 	int		level,
86 	const char *	msg
87 	)
88 {
89 	static char *	prevcall_progname;
90 	static char *	prog;
91 	const char	nl[] = "\n";
92 	const char	empty[] = "";
93 	FILE *		term_file;
94 	bool		log_to_term;
95 	bool		log_to_file;
96 	int		pid;
97 	const char *	nl_or_empty;
98 	const char *	human_time;
99 	char            tbuf[TIMESTAMP_LEN];
100 
101 	/* setup program basename static var prog if needed */
102 	if (progname != prevcall_progname) {
103 		prevcall_progname = progname;
104 		prog = strrchr(progname, DIR_SEP);
105 		if (prog != NULL) {
106 			prog++;
107 		} else {
108 			prog = progname;
109 		}
110 	}
111 
112 	log_to_term = termlogit;
113 	log_to_file = false;
114 	if (syslogit)
115 		syslog(level, "%s", msg);
116 	else
117 		if (syslog_file != NULL)
118 			log_to_file = true;
119 #if defined(DEBUG) && DEBUG
120 	if (debug > 0) /* SPECIAL DEBUG */
121 		log_to_term = true;
122 #endif
123 	if (!(log_to_file || log_to_term))
124 		return;
125 
126 	/* syslog() adds the timestamp, name, and pid */
127 	if (msyslog_include_timestamp) {
128 		humanlogtime(tbuf);
129 		human_time = tbuf;
130 	} else	/* suppress gcc pot. uninit. warning */
131 		human_time = NULL;
132 	if (termlogit_pid || log_to_file)
133 		pid = getpid();
134 	else	/* suppress gcc pot. uninit. warning */
135 		pid = -1;
136 
137 	/* syslog() adds trailing \n if not present */
138 	if ('\n' != msg[strlen(msg) - 1]) {
139 		nl_or_empty = nl;
140 	} else {
141 		nl_or_empty = empty;
142 }
143 
144 	if (log_to_term) {
145 		term_file = (level <= LOG_ERR)
146 				? stderr
147 				: stdout;
148 		if (msyslog_include_timestamp)
149 			fprintf(term_file, "%s ", human_time);
150 		if (termlogit_pid)
151 			fprintf(term_file, "%s[%d]: ", prog, pid);
152 		fprintf(term_file, "%s%s", msg, nl_or_empty);
153 		fflush(term_file);
154 	}
155 
156 	if (log_to_file) {
157 	 	/*
158 		 * Thread-safe write, the de-facto way.  It's not
159 		 * actually guaranteed by standards that a write of
160 		 * PIPE_BUF chars or less is atomic anywhere but on a
161 		 * pipe.  In ancient times this was 512 and happened to
162 		 * be equal to the usual size of a hardware disk sector,
163 		 * which was what really bounded atomicity. The actual
164 		 * answer under POSIX is SSIZE_MAX, which is far larger
165 		 * than we want or need to allocate here.
166 		 */
167 		char buf[PIPE_BUF];
168 		buf[0] = '\0';
169 		if (msyslog_include_timestamp)
170 			snprintf(buf, sizeof(buf), "%s ", human_time);
171 		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
172 			 "%s[%d]: %s%s", prog, pid, msg, nl_or_empty);
173 		IGNORE(write(fileno(syslog_file), buf, strlen(buf)));
174 	}
175 }
176 
177 
178 void
msyslog(int level,const char * fmt,...)179 msyslog(
180 	int		level,
181 	const char *	fmt,
182 	...
183 	)
184 {
185 	char	buf[1024];
186 	va_list	ap;
187 
188 	va_start(ap, fmt);
189 	vsnprintf(buf, sizeof(buf), fmt, ap);
190 	va_end(ap);
191 	addto_syslog(level, buf);
192 }
193 
194 
195 /*
196  * Initialize the logging
197  *
198  * Called once per process, including forked children.
199  */
200 void
init_logging(const char * name,uint32_t def_syslogmask,int is_daemon)201 init_logging(
202 	const char *	name,
203 	uint32_t		def_syslogmask,
204 	int		is_daemon
205 	)
206 {
207 	static bool	was_daemon;
208 	const char *	cp;
209 	const char *	pname;
210 
211 	/*
212 	 * ntpd defaults to only logging sync-category events, when
213 	 * NLOG() is used to conditionalize.  Other libntp clients
214 	 * leave it alone so that all NLOG() conditionals will fire.
215 	 * This presumes all bits lit in ntp_syslogmask can't be
216 	 * configured via logconfig and all lit is thereby a sentinel
217 	 * that ntp_syslogmask is still at its default from libntp,
218 	 * keeping in mind this function is called in forked children
219 	 * where it has already been called in the parent earlier.
220 	 * Forked children pass 0 for def_syslogmask.
221 	 */
222 	if (INIT_NTP_SYSLOGMASK == ntp_syslogmask &&
223 	    0 != def_syslogmask)
224 		ntp_syslogmask = def_syslogmask; /* set more via logconfig */
225 
226 	/*
227 	 * Logging.  This may actually work on the gizmo board.  Find a name
228 	 * to log with by using the basename
229 	 */
230 	cp = strrchr(name, DIR_SEP);
231 	if (NULL == cp) {
232 		pname = name;
233 	} else {
234 		pname = 1 + cp;	/* skip DIR_SEP */
235 	}
236 	progname = estrdup(pname);
237 
238 	if (is_daemon)
239 		was_daemon = true;
240 # ifndef LOG_DAEMON
241 	openlog(progname, LOG_PID);
242 # else /* LOG_DAEMON */
243 
244 #  ifndef LOG_NTP
245 #	define	LOG_NTP LOG_DAEMON
246 #  endif
247 	openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon)
248 						    ? LOG_NTP
249 						    : 0);
250 #  ifdef DEBUG
251 	if (debug) /* SPECIAL DEBUG */
252 		setlogmask(LOG_UPTO(LOG_DEBUG));
253 	else
254 #  endif /* DEBUG */
255 		setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
256 # endif /* LOG_DAEMON */
257 }
258 
259 
260 /*
261  * change_logfile()
262  *
263  * Used to change from syslog to a logfile, or from one logfile to
264  * another, and to reopen logfiles after forking.  On systems where
265  * ntpd forks, deals with converting relative logfile paths to
266  * absolute (root-based) because we reopen logfiles after the current
267  * directory has changed.
268  */
269 int
change_logfile(const char * fname,bool leave_crumbs)270 change_logfile(
271 	const char *	fname,
272 	bool		leave_crumbs
273 	)
274 {
275 	FILE *		new_file;
276 	const char *	log_fname;
277 	char *		abs_fname;
278 	char		curdir[512];
279 	size_t		cd_octets;
280 	size_t		octets;
281 
282 	//REQUIRE(fname != NULL);
283 	log_fname = fname;
284 
285 	/*
286 	 * In a forked child of a parent which is logging to a file
287 	 * instead of syslog, syslog_file will be NULL and both
288 	 * syslog_fname and syslog_abs_fname will be non-NULL.
289 	 * If we are given the same filename previously opened
290 	 * and it's still open, there's nothing to do here.
291 	 */
292 	if (syslog_file != NULL && syslog_fname != NULL &&
293 	    0 == strcmp(syslog_fname, log_fname)) {
294 		return 0;
295 	}
296 
297 	if (0 == strcmp(log_fname, "stderr")) {
298 		new_file = stderr;
299 		abs_fname = estrdup(log_fname);
300 	} else if (0 == strcmp(log_fname, "stdout")) {
301 		new_file = stdout;
302 		abs_fname = estrdup(log_fname);
303 	} else {
304 		if (syslog_fname != NULL &&
305 		    0 == strcmp(log_fname, syslog_fname)) {
306 			log_fname = syslog_abs_fname;
307 		}
308 		if (log_fname != syslog_abs_fname &&
309 		    DIR_SEP != log_fname[0] &&
310 		    0 != strcmp(log_fname, "stderr") &&
311 		    0 != strcmp(log_fname, "stdout") &&
312 		    NULL != getcwd(curdir, sizeof(curdir))) {
313 			cd_octets = strlen(curdir);
314 			/* trim any trailing '/' */
315 			if (cd_octets > 1 &&
316 			    DIR_SEP == curdir[cd_octets - 1])
317 				cd_octets--;
318 			octets = cd_octets;
319 			octets += 1;	/* separator '/' */
320 			octets += strlen(log_fname);
321 			octets += 1;	/* NUL terminator */
322 			abs_fname = emalloc(octets);
323 			snprintf(abs_fname, octets, "%.*s%c%s",
324 				 (int)cd_octets, curdir, DIR_SEP,
325 				 log_fname);
326 		} else
327 			abs_fname = estrdup(log_fname);
328 		DPRINT(1, ("attempting to open log %s\n", abs_fname));
329 		new_file = fopen(abs_fname, "a");
330 	}
331 
332 	if (NULL == new_file) {
333 		free(abs_fname);
334 		return -1;
335 	}
336 
337 	/* leave a pointer in the old log */
338 	if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname))
339 		msyslog(LOG_NOTICE, "LOG: switching logging to file %s",
340 			abs_fname);
341 
342 	if (syslog_file != NULL &&
343 	    syslog_file != stderr && syslog_file != stdout &&
344 	    fileno(syslog_file) != fileno(new_file)) {
345 		fclose(syslog_file);
346 	}
347 	syslog_file = new_file;
348 	if (log_fname == syslog_abs_fname) {
349 		free(abs_fname);
350 	} else {
351 		if (syslog_abs_fname != NULL &&
352 		    syslog_abs_fname != syslog_fname) {
353 			free(syslog_abs_fname);
354 		}
355 		if (syslog_fname != NULL) {
356 			free(syslog_fname);
357 		}
358 		syslog_fname = estrdup(log_fname);
359 		syslog_abs_fname = abs_fname;
360 	}
361 	syslogit = false;
362 
363 	return 0;
364 }
365 
366 
367 /*
368  * setup_logfile()
369  *
370  * Redirect logging to a file if requested with -l/--logfile or via
371  * ntp.conf logfile directive.
372  *
373  * This routine is invoked three different times in the sequence of a
374  * typical daemon ntpd with DNS lookups to do.  First it is invoked in
375  * the original ntpd process, then again in the daemon after closing
376  * all descriptors.  In both of those cases, ntp.conf has not been
377  * processed, so only -l/--logfile will trigger logfile redirection in
378  * those invocations.  Finally, if DNS names are resolved, the worker
379  * child invokes this routine after its fork and close of all
380  * descriptors.  In this case, ntp.conf has been processed and any
381  * "logfile" directive needs to be honored in the child as well.
382  */
383 void
setup_logfile(const char * name)384 setup_logfile(
385 	const char *	name
386 	)
387 {
388 	if (NULL == syslog_fname && NULL != name) {
389 		if (-1 == change_logfile(name, true))
390 			msyslog(LOG_ERR, "LOG: Cannot open log file %s, %s",
391 				name, strerror(errno));
392 		return ;
393 	}
394 	if (NULL == syslog_fname) {
395 		return;
396 	}
397 
398 	if (-1 == change_logfile(syslog_fname, false))
399 		msyslog(LOG_ERR, "LOG: Cannot reopen log file %s, %s",
400 			syslog_fname, strerror(errno));
401 }
402 
403 /*
404  * check_logfile()
405  *
406  * reopen current logfile in case the old file has been renamed by logrotate
407  * called on SIGHUP and hourly
408  */
409 
410 void
check_logfile(void)411 check_logfile(void)
412 {
413 	FILE *  new_file;
414 
415 	if (NULL == syslog_file) {
416 		return;  /* no log file, no clutter */
417 	}
418 
419 	new_file = fopen(syslog_fname, "a");
420 	if (NULL == new_file) {
421 		msyslog(LOG_ERR, "LOG: check_logfile: couldn't open %s %s",
422                         syslog_fname, strerror(errno));
423 		return;
424 	}
425 
426 	/* This is a hack to avoid cluttering the log if we would reuse
427 	 * the same file all over again.
428 	 * change_logfile compares filenos.  That doesn't work.
429 	 * Can't check for a new file using a length of 0 since
430 	 * newsyslog on FreeBSD puts a "logfile turned over" message there.
431 	 * This seems to work.
432 	 */
433 	if (ftell(syslog_file) == ftell(new_file)) {
434 		fclose(new_file);
435 		return;
436 	}
437 
438 	msyslog(LOG_INFO, "LOG: check_logfile: closing old file");
439 	fclose(syslog_file);
440 	syslog_file = new_file;
441 	msyslog(LOG_INFO, "LOG: check_logfile: using %s", syslog_fname);
442 }
443 
444 /* Hack because there are 2 APIs to strerror_r()  */
ntp_strerror_r(int errnum,char * buf,size_t buflen)445 void ntp_strerror_r(int errnum, char *buf, size_t buflen) {
446 #ifdef STRERROR_CHAR
447 	char *answer = strerror_r(errnum, buf, buflen);
448 	if (answer != buf) {
449 		strlcpy(buf, answer, buflen);
450 	}
451 #else
452 	int answer = strerror_r(errnum, buf, buflen);
453 	UNUSED_LOCAL(answer);
454 #endif
455 }
456 
457