1/*	$NetBSD: syslog.c,v 1.48 2010/05/13 22:40:14 christos Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1988, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if defined(LIBC_SCCS) && !defined(lint)
34#if 0
35static char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
36#else
37__RCSID("$NetBSD: syslog.c,v 1.48 2010/05/13 22:40:14 christos Exp $");
38#endif
39#endif /* LIBC_SCCS and not lint */
40
41#include "namespace.h"
42#include <sys/types.h>
43#include <sys/param.h>
44#include <sys/socket.h>
45#include <sys/syslog.h>
46#include <sys/uio.h>
47#include <sys/un.h>
48#include <netdb.h>
49
50#include <errno.h>
51#include <fcntl.h>
52#include <paths.h>
53#include <stdarg.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <time.h>
58#include <unistd.h>
59#include "reentrant.h"
60#include "extern.h"
61
62#ifdef __weak_alias
63__weak_alias(closelog,_closelog)
64__weak_alias(openlog,_openlog)
65__weak_alias(setlogmask,_setlogmask)
66__weak_alias(syslog,_syslog)
67__weak_alias(vsyslog,_vsyslog)
68__weak_alias(syslogp,_syslogp)
69__weak_alias(vsyslogp,_vsyslogp)
70
71__weak_alias(closelog_r,_closelog_r)
72__weak_alias(openlog_r,_openlog_r)
73__weak_alias(setlogmask_r,_setlogmask_r)
74__weak_alias(syslog_r,_syslog_r)
75__weak_alias(vsyslog_r,_vsyslog_r)
76__weak_alias(syslog_ss,_syslog_ss)
77__weak_alias(vsyslog_ss,_vsyslog_ss)
78__weak_alias(syslogp_r,_syslogp_r)
79__weak_alias(vsyslogp_r,_vsyslogp_r)
80__weak_alias(syslogp_ss,_syslogp_ss)
81__weak_alias(vsyslogp_ss,_vsyslogp_ss)
82#endif
83
84static struct syslog_data sdata = SYSLOG_DATA_INIT;
85
86static void	openlog_unlocked_r(const char *, int, int,
87    struct syslog_data *);
88static void	disconnectlog_r(struct syslog_data *);
89static void	connectlog_r(struct syslog_data *);
90
91#define LOG_SIGNAL_SAFE	(int)0x80000000
92
93
94#ifdef _REENTRANT
95static mutex_t	syslog_mutex = MUTEX_INITIALIZER;
96#endif
97
98static char hostname[MAXHOSTNAMELEN];
99
100/*
101 * syslog, vsyslog --
102 *	print message on log file; output is intended for syslogd(8).
103 */
104void
105syslog(int pri, const char *fmt, ...)
106{
107	va_list ap;
108
109	va_start(ap, fmt);
110	vsyslog(pri, fmt, ap);
111	va_end(ap);
112}
113
114void
115vsyslog(int pri, const char *fmt, va_list ap)
116{
117	vsyslog_r(pri, &sdata, fmt, ap);
118}
119
120/*
121 * syslogp, vsyslogp --
122 *	like syslog but take additional arguments for MSGID and SD
123 */
124void
125syslogp(int pri, const char *msgid, const char *sdfmt, const char *msgfmt, ...)
126{
127	va_list ap;
128
129	va_start(ap, msgfmt);
130	vsyslogp(pri, msgid, sdfmt, msgfmt, ap);
131	va_end(ap);
132}
133
134void
135vsyslogp(int pri, const char *msgid, const char *sdfmt, const char *msgfmt, va_list ap)
136{
137	vsyslogp_r(pri, &sdata, msgid, sdfmt, msgfmt, ap);
138}
139
140void
141openlog(const char *ident, int logstat, int logfac)
142{
143	openlog_r(ident, logstat, logfac, &sdata);
144}
145
146void
147closelog(void)
148{
149	closelog_r(&sdata);
150}
151
152/* setlogmask -- set the log mask level */
153int
154setlogmask(int pmask)
155{
156	return setlogmask_r(pmask, &sdata);
157}
158
159/* Reentrant version of syslog, i.e. syslog_r() */
160
161void
162syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
163{
164	va_list ap;
165
166	va_start(ap, fmt);
167	vsyslog_r(pri, data, fmt, ap);
168	va_end(ap);
169}
170
171void
172syslogp_r(int pri, struct syslog_data *data, const char *msgid,
173	const char *sdfmt, const char *msgfmt, ...)
174{
175	va_list ap;
176
177	va_start(ap, msgfmt);
178	vsyslogp_r(pri, data, msgid, sdfmt, msgfmt, ap);
179	va_end(ap);
180}
181
182void
183syslog_ss(int pri, struct syslog_data *data, const char *fmt, ...)
184{
185	va_list ap;
186
187	va_start(ap, fmt);
188	vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
189	va_end(ap);
190}
191
192void
193syslogp_ss(int pri, struct syslog_data *data, const char *msgid,
194	const char *sdfmt, const char *msgfmt, ...)
195{
196	va_list ap;
197
198	va_start(ap, msgfmt);
199	vsyslogp_r(pri | LOG_SIGNAL_SAFE, data, msgid, sdfmt, msgfmt, ap);
200	va_end(ap);
201}
202
203void
204vsyslog_ss(int pri, struct syslog_data *data, const char *fmt, va_list ap)
205{
206	vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
207}
208
209void
210vsyslogp_ss(int pri, struct syslog_data *data, const char *msgid,
211	const char *sdfmt, const char *msgfmt, va_list ap)
212{
213	vsyslogp_r(pri | LOG_SIGNAL_SAFE, data, msgid, sdfmt, msgfmt, ap);
214}
215
216
217void
218vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
219{
220	vsyslogp_r(pri, data, NULL, NULL, fmt, ap);
221}
222
223void
224vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
225	const char *sdfmt, const char *msgfmt, va_list ap)
226{
227	static const char BRCOSP[] = "]: ";
228	static const char CRLF[] = "\r\n";
229	size_t cnt, prlen, tries;
230	char ch, *p, *t;
231	struct timeval tv;
232	struct tm tmnow;
233	time_t now;
234	int fd, saved_errno;
235#define TBUF_LEN	2048
236#define FMT_LEN		1024
237#define MAXTRIES	10
238	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
239	size_t tbuf_left, fmt_left, msgsdlen;
240	char *fmt = fmt_cat;
241	int signal_safe = pri & LOG_SIGNAL_SAFE;
242	struct iovec iov[7];	/* prog + [ + pid + ]: + fmt + crlf */
243	int opened, iovcnt;
244
245	pri &= ~LOG_SIGNAL_SAFE;
246
247#define INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
248	/* Check for invalid bits. */
249	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
250		syslog_r(INTERNALLOG | signal_safe, data,
251		    "syslog_r: unknown facility/priority: %x", pri);
252		pri &= LOG_PRIMASK|LOG_FACMASK;
253	}
254
255	/* Check priority against setlogmask values. */
256	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
257		return;
258
259	saved_errno = errno;
260
261	/* Set default facility if none specified. */
262	if ((pri & LOG_FACMASK) == 0)
263		pri |= data->log_fac;
264
265	/* Build the message. */
266	p = tbuf;
267	tbuf_left = TBUF_LEN;
268
269#define DEC()							\
270	do {							\
271		if (prlen >= tbuf_left)				\
272			prlen = tbuf_left - 1;			\
273		p += prlen;					\
274		tbuf_left -= prlen;				\
275	} while (/*CONSTCOND*/0)
276
277	prlen = snprintf_ss(p, tbuf_left, "<%d>1 ", pri);
278	DEC();
279
280	if (!signal_safe && (gettimeofday(&tv, NULL) != -1)) {
281		/* strftime() implies tzset(), localtime_r() doesn't. */
282		tzset();
283		now = (time_t) tv.tv_sec;
284		localtime_r(&now, &tmnow);
285
286		prlen = strftime(p, tbuf_left, "%FT%T", &tmnow);
287		DEC();
288		prlen = snprintf(p, tbuf_left, ".%06ld", (long)tv.tv_usec);
289		DEC();
290		prlen = strftime(p, tbuf_left-1, "%z", &tmnow);
291		/* strftime gives eg. "+0200", but we need "+02:00" */
292		if (prlen == 5) {
293			p[prlen+1] = p[prlen];
294			p[prlen]   = p[prlen-1];
295			p[prlen-1] = p[prlen-2];
296			p[prlen-2] = ':';
297			prlen += 1;
298		}
299	} else {
300		prlen = snprintf_ss(p, tbuf_left, "-");
301
302		/* if gmtime_r() was signal-safe we could output the UTC-time:
303		gmtime_r(&now, &tmnow);
304		prlen = strftime(p, tbuf_left, "%FT%TZ", &tmnow);
305		*/
306	}
307	DEC();
308	prlen = snprintf_ss(p, tbuf_left, " %s ", hostname);
309	DEC();
310
311	if (data->log_tag == NULL)
312		data->log_tag = getprogname();
313
314	prlen = snprintf_ss(p, tbuf_left, "%s ",
315	    data->log_tag ? data->log_tag : "-");
316	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
317		iovcnt = 0;
318		iov[iovcnt].iov_base = p;
319		iov[iovcnt].iov_len = prlen - 1;
320		iovcnt++;
321	}
322	DEC();
323
324	if (data->log_stat & LOG_PID) {
325		prlen = snprintf_ss(p, tbuf_left, "%d ", getpid());
326		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
327			iov[iovcnt].iov_base = __UNCONST("[");
328			iov[iovcnt].iov_len = 1;
329			iovcnt++;
330			iov[iovcnt].iov_base = p;
331			iov[iovcnt].iov_len = prlen - 1;
332			iovcnt++;
333			iov[iovcnt].iov_base = __UNCONST(BRCOSP);
334			iov[iovcnt].iov_len = 3;
335			iovcnt++;
336		}
337	} else {
338		prlen = snprintf_ss(p, tbuf_left, "- ");
339		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
340			iov[iovcnt].iov_base = __UNCONST(BRCOSP + 1);
341			iov[iovcnt].iov_len = 2;
342			iovcnt++;
343		}
344	}
345	DEC();
346
347	/*
348	 * concat the format strings, then use one vsnprintf()
349	 */
350	if (msgid != NULL && *msgid != '\0') {
351		strlcat(fmt_cat, msgid, FMT_LEN);
352		strlcat(fmt_cat, " ", FMT_LEN);
353	} else
354		strlcat(fmt_cat, "- ", FMT_LEN);
355
356	if (sdfmt != NULL && *sdfmt != '\0') {
357		strlcat(fmt_cat, sdfmt, FMT_LEN);
358	} else
359		strlcat(fmt_cat, "-", FMT_LEN);
360
361	if (data->log_stat & (LOG_PERROR|LOG_CONS))
362		msgsdlen = strlen(fmt_cat) + 1;
363	else
364		msgsdlen = 0;	/* XXX: GCC */
365
366	if (msgfmt != NULL && *msgfmt != '\0') {
367		strlcat(fmt_cat, " ", FMT_LEN);
368		strlcat(fmt_cat, msgfmt, FMT_LEN);
369	}
370
371	/*
372	 * We wouldn't need this mess if printf handled %m, or if
373	 * strerror() had been invented before syslog().
374	 */
375	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
376		if (ch == '%' && fmt[1] == 'm') {
377			char ebuf[128];
378			++fmt;
379			if (signal_safe ||
380			    strerror_r(saved_errno, ebuf, sizeof(ebuf)))
381				prlen = snprintf_ss(t, fmt_left, "Error %d",
382				    saved_errno);
383			else
384				prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
385			if (prlen >= fmt_left)
386				prlen = fmt_left - 1;
387			t += prlen;
388			fmt_left -= prlen;
389		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
390			*t++ = '%';
391			*t++ = '%';
392			fmt++;
393			fmt_left -= 2;
394		} else {
395			if (fmt_left > 1) {
396				*t++ = ch;
397				fmt_left--;
398			}
399		}
400	}
401	*t = '\0';
402
403	if (signal_safe)
404		prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
405	else
406		prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
407
408	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
409		iov[iovcnt].iov_base = p + msgsdlen;
410		iov[iovcnt].iov_len = prlen - msgsdlen;
411		iovcnt++;
412	}
413
414	DEC();
415	cnt = p - tbuf;
416
417	/* Output to stderr if requested. */
418	if (data->log_stat & LOG_PERROR) {
419		iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
420		iov[iovcnt].iov_len = 1;
421		(void)writev(STDERR_FILENO, iov, iovcnt + 1);
422	}
423
424	/* Get connected, output the message to the local logger. */
425	if (data == &sdata)
426		mutex_lock(&syslog_mutex);
427	opened = !data->opened;
428	if (opened)
429		openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
430	connectlog_r(data);
431
432	/*
433	 * If the send() failed, there are two likely scenarios:
434	 *  1) syslogd was restarted
435	 *  2) /dev/log is out of socket buffer space
436	 * We attempt to reconnect to /dev/log to take care of
437	 * case #1 and keep send()ing data to cover case #2
438	 * to give syslogd a chance to empty its socket buffer.
439	 */
440	for (tries = 0; tries < MAXTRIES; tries++) {
441		if (send(data->log_file, tbuf, cnt, 0) != -1)
442			break;
443		if (errno != ENOBUFS) {
444			disconnectlog_r(data);
445			connectlog_r(data);
446		} else
447			(void)usleep(1);
448	}
449
450	/*
451	 * Output the message to the console; try not to block
452	 * as a blocking console should not stop other processes.
453	 * Make sure the error reported is the one from the syslogd failure.
454	 */
455	if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
456	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
457		iov[iovcnt].iov_base = __UNCONST(CRLF);
458		iov[iovcnt].iov_len = 2;
459		(void)writev(fd, iov, iovcnt + 1);
460		(void)close(fd);
461	}
462
463	if (data == &sdata)
464		mutex_unlock(&syslog_mutex);
465
466	if (data != &sdata && opened) {
467		/* preserve log tag */
468		const char *ident = data->log_tag;
469		closelog_r(data);
470		data->log_tag = ident;
471	}
472}
473
474static void
475disconnectlog_r(struct syslog_data *data)
476{
477	/*
478	 * If the user closed the FD and opened another in the same slot,
479	 * that's their problem.  They should close it before calling on
480	 * system services.
481	 */
482	if (data->log_file != -1) {
483		(void)close(data->log_file);
484		data->log_file = -1;
485	}
486	data->connected = 0;		/* retry connect */
487}
488
489static void
490connectlog_r(struct syslog_data *data)
491{
492	/* AF_UNIX address of local logger */
493	static const struct sockaddr_un sun = {
494		.sun_family = AF_LOCAL,
495		.sun_len = sizeof(sun),
496		.sun_path = _PATH_LOG,
497	};
498
499	if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
500		if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC,
501		    0)) == -1)
502			return;
503		data->connected = 0;
504	}
505	if (!data->connected) {
506		if (connect(data->log_file,
507		    (const struct sockaddr *)(const void *)&sun,
508		    sizeof(sun)) == -1) {
509			(void)close(data->log_file);
510			data->log_file = -1;
511		} else
512			data->connected = 1;
513	}
514}
515
516static void
517openlog_unlocked_r(const char *ident, int logstat, int logfac,
518    struct syslog_data *data)
519{
520	if (ident != NULL)
521		data->log_tag = ident;
522	data->log_stat = logstat;
523	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
524		data->log_fac = logfac;
525
526	if (data->log_stat & LOG_NDELAY)	/* open immediately */
527		connectlog_r(data);
528
529	/* We could cache this, but then it might change */
530	if (gethostname(hostname, sizeof(hostname)) == -1
531	    || hostname[0] == '\0') {
532		/* can this really happen? */
533		hostname[0] = '-';
534		hostname[1] = '\0';
535	}
536	data->opened = 1;
537}
538
539void
540openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
541{
542	if (data == &sdata)
543		mutex_lock(&syslog_mutex);
544	openlog_unlocked_r(ident, logstat, logfac, data);
545	if (data == &sdata)
546		mutex_unlock(&syslog_mutex);
547}
548
549void
550closelog_r(struct syslog_data *data)
551{
552	if (data == &sdata)
553		mutex_lock(&syslog_mutex);
554	(void)close(data->log_file);
555	data->log_file = -1;
556	data->connected = 0;
557	data->log_tag = NULL;
558	if (data == &sdata)
559		mutex_unlock(&syslog_mutex);
560}
561
562int
563setlogmask_r(int pmask, struct syslog_data *data)
564{
565	int omask;
566
567	omask = data->log_mask;
568	if (pmask != 0)
569		data->log_mask = pmask;
570	return omask;
571}
572