syslog.c revision 158426
1171831Skan/*
2171831Skan * Copyright (c) 1983, 1988, 1993
3171831Skan *	The Regents of the University of California.  All rights reserved.
4171831Skan *
5169695Skan * Redistribution and use in source and binary forms, with or without
6169695Skan * modification, are permitted provided that the following conditions
7169695Skan * are met:
8169695Skan * 1. Redistributions of source code must retain the above copyright
9169695Skan *    notice, this list of conditions and the following disclaimer.
10169695Skan * 2. Redistributions in binary form must reproduce the above copyright
11169695Skan *    notice, this list of conditions and the following disclaimer in the
12169695Skan *    documentation and/or other materials provided with the distribution.
13169695Skan * 3. All advertising materials mentioning features or use of this software
14169695Skan *    must display the following acknowledgement:
15169695Skan *	This product includes software developed by the University of
16169695Skan *	California, Berkeley and its contributors.
17169695Skan * 4. Neither the name of the University nor the names of its contributors
18169695Skan *    may be used to endorse or promote products derived from this software
19169695Skan *    without specific prior written permission.
20169695Skan *
21169695Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22169695Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23169695Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24169695Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25169695Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26169695Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27169695Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28169695Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29169695Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30169695Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31169695Skan * SUCH DAMAGE.
32169695Skan */
33169695Skan
34169695Skan#if defined(LIBC_SCCS) && !defined(lint)
35169695Skanstatic char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
36169695Skan#endif /* LIBC_SCCS and not lint */
37169695Skan#include <sys/cdefs.h>
38169695Skan__FBSDID("$FreeBSD: head/lib/libc/gen/syslog.c 158426 2006-05-11 09:10:33Z davidxu $");
39169695Skan
40169695Skan#include "namespace.h"
41169695Skan#include <sys/types.h>
42169695Skan#include <sys/socket.h>
43169695Skan#include <sys/syslog.h>
44169695Skan#include <sys/uio.h>
45169695Skan#include <sys/un.h>
46169695Skan#include <netdb.h>
47169695Skan
48169695Skan#include <errno.h>
49169695Skan#include <fcntl.h>
50169695Skan#include <paths.h>
51169695Skan#include <pthread.h>
52169695Skan#include <stdio.h>
53169695Skan#include <stdlib.h>
54169695Skan#include <string.h>
55169695Skan#include <time.h>
56169695Skan#include <unistd.h>
57169695Skan
58169695Skan#include <stdarg.h>
59169695Skan#include "un-namespace.h"
60169695Skan
61169695Skan#include "libc_private.h"
62169695Skan
63169695Skanstatic int	LogFile = -1;		/* fd for log */
64169695Skanstatic int	status;			/* connection status */
65169695Skanstatic int	opened;			/* have done openlog() */
66169695Skanstatic int	LogStat = 0;		/* status bits, set by openlog() */
67169695Skanstatic const char *LogTag = NULL;	/* string to tag the entry with */
68169695Skanstatic int	LogFacility = LOG_USER;	/* default facility code */
69169695Skanstatic int	LogMask = 0xff;		/* mask of priorities to be logged */
70169695Skanstatic pthread_mutex_t	syslog_mutex = PTHREAD_MUTEX_INITIALIZER;
71169695Skan
72169695Skan#define	THREAD_LOCK()							\
73169695Skan	do { 								\
74169695Skan		if (__isthreaded) _pthread_mutex_lock(&syslog_mutex);	\
75169695Skan	} while(0)
76169695Skan#define	THREAD_UNLOCK()							\
77169695Skan	do {								\
78169695Skan		if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex);	\
79169695Skan	} while(0)
80169695Skan
81169695Skanstatic void	disconnectlog(void); /* disconnect from syslogd */
82169695Skanstatic void	connectlog(void);	/* (re)connect to syslogd */
83169695Skanstatic void	openlog_unlocked(const char *, int, int);
84169695Skan
85169695Skanenum {
86169695Skan	NOCONN = 0,
87169695Skan	CONNDEF,
88169695Skan	CONNPRIV,
89169695Skan};
90169695Skan
91169695Skan/*
92169695Skan * Format of the magic cookie passed through the stdio hook
93169695Skan */
94169695Skanstruct bufcookie {
95169695Skan	char	*base;	/* start of buffer */
96169695Skan	int	left;
97169695Skan};
98169695Skan
99169695Skan/*
100169695Skan * stdio write hook for writing to a static string buffer
101169695Skan * XXX: Maybe one day, dynamically allocate it so that the line length
102169695Skan *      is `unlimited'.
103169695Skan */
104169695Skanstatic int
105169695Skanwritehook(void *cookie, const char *buf, int len)
106169695Skan{
107169695Skan	struct bufcookie *h;	/* private `handle' */
108169695Skan
109169695Skan	h = (struct bufcookie *)cookie;
110169695Skan	if (len > h->left) {
111169695Skan		/* clip in case of wraparound */
112169695Skan		len = h->left;
113169695Skan	}
114169695Skan	if (len > 0) {
115169695Skan		(void)memcpy(h->base, buf, len); /* `write' it. */
116169695Skan		h->base += len;
117169695Skan		h->left -= len;
118169695Skan	}
119169695Skan	return 0;
120169695Skan}
121169695Skan
122169695Skan/*
123169695Skan * syslog, vsyslog --
124169695Skan *	print message on log file; output is intended for syslogd(8).
125169695Skan */
126169695Skanvoid
127169695Skansyslog(int pri, const char *fmt, ...)
128169695Skan{
129169695Skan	va_list ap;
130169695Skan
131169695Skan	va_start(ap, fmt);
132169695Skan	vsyslog(pri, fmt, ap);
133169695Skan	va_end(ap);
134169695Skan}
135169695Skan
136169695Skanvoid
137169695Skanvsyslog(int pri, const char *fmt, va_list ap)
138169695Skan{
139169695Skan	int cnt;
140169695Skan	char ch, *p;
141169695Skan	time_t now;
142169695Skan	int fd, saved_errno;
143169695Skan	char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64];
144169695Skan	FILE *fp, *fmt_fp;
145169695Skan	struct bufcookie tbuf_cookie;
146169695Skan	struct bufcookie fmt_cookie;
147169695Skan
148169695Skan#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
149169695Skan	/* Check for invalid bits. */
150169695Skan	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
151169695Skan		syslog(INTERNALLOG,
152169695Skan		    "syslog: unknown facility/priority: %x", pri);
153169695Skan		pri &= LOG_PRIMASK|LOG_FACMASK;
154169695Skan	}
155169695Skan
156169695Skan	saved_errno = errno;
157169695Skan
158169695Skan	THREAD_LOCK();
159169695Skan
160169695Skan	/* Check priority against setlogmask values. */
161169695Skan	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) {
162169695Skan		THREAD_UNLOCK();
163169695Skan		return;
164169695Skan	}
165169695Skan
166169695Skan	/* Set default facility if none specified. */
167169695Skan	if ((pri & LOG_FACMASK) == 0)
168169695Skan		pri |= LogFacility;
169169695Skan
170169695Skan	/* Create the primary stdio hook */
171169695Skan	tbuf_cookie.base = tbuf;
172169695Skan	tbuf_cookie.left = sizeof(tbuf);
173169695Skan	fp = fwopen(&tbuf_cookie, writehook);
174169695Skan	if (fp == NULL) {
175169695Skan		THREAD_UNLOCK();
176169695Skan		return;
177169695Skan	}
178169695Skan
179169695Skan	/* Build the message. */
180169695Skan	(void)time(&now);
181169695Skan	(void)fprintf(fp, "<%d>", pri);
182169695Skan	(void)fprintf(fp, "%.15s ", ctime_r(&now, timbuf) + 4);
183169695Skan	if (LogStat & LOG_PERROR) {
184169695Skan		/* Transfer to string buffer */
185169695Skan		(void)fflush(fp);
186169695Skan		stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left);
187169695Skan	}
188169695Skan	if (LogTag == NULL)
189169695Skan		LogTag = _getprogname();
190169695Skan	if (LogTag != NULL)
191169695Skan		(void)fprintf(fp, "%s", LogTag);
192169695Skan	if (LogStat & LOG_PID)
193169695Skan		(void)fprintf(fp, "[%d]", getpid());
194169695Skan	if (LogTag != NULL) {
195169695Skan		(void)fprintf(fp, ": ");
196169695Skan	}
197169695Skan
198169695Skan	/* Check to see if we can skip expanding the %m */
199169695Skan	if (strstr(fmt, "%m")) {
200169695Skan
201169695Skan		/* Create the second stdio hook */
202169695Skan		fmt_cookie.base = fmt_cpy;
203169695Skan		fmt_cookie.left = sizeof(fmt_cpy) - 1;
204169695Skan		fmt_fp = fwopen(&fmt_cookie, writehook);
205169695Skan		if (fmt_fp == NULL) {
206169695Skan			fclose(fp);
207169695Skan			THREAD_UNLOCK();
208169695Skan			return;
209169695Skan		}
210169695Skan
211169695Skan		/*
212169695Skan		 * Substitute error message for %m.  Be careful not to
213169695Skan		 * molest an escaped percent "%%m".  We want to pass it
214169695Skan		 * on untouched as the format is later parsed by vfprintf.
215169695Skan		 */
216169695Skan		for ( ; (ch = *fmt); ++fmt) {
217169695Skan			if (ch == '%' && fmt[1] == 'm') {
218169695Skan				++fmt;
219169695Skan				strerror_r(saved_errno, errstr, sizeof(errstr));
220169695Skan				fputs(errstr, fmt_fp);
221169695Skan			} else if (ch == '%' && fmt[1] == '%') {
222169695Skan				++fmt;
223169695Skan				fputc(ch, fmt_fp);
224169695Skan				fputc(ch, fmt_fp);
225169695Skan			} else {
226169695Skan				fputc(ch, fmt_fp);
227169695Skan			}
228169695Skan		}
229169695Skan
230169695Skan		/* Null terminate if room */
231169695Skan		fputc(0, fmt_fp);
232169695Skan		fclose(fmt_fp);
233169695Skan
234169695Skan		/* Guarantee null termination */
235169695Skan		fmt_cpy[sizeof(fmt_cpy) - 1] = '\0';
236169695Skan
237169695Skan		fmt = fmt_cpy;
238169695Skan	}
239169695Skan
240169695Skan	(void)vfprintf(fp, fmt, ap);
241169695Skan	(void)fclose(fp);
242169695Skan
243169695Skan	cnt = sizeof(tbuf) - tbuf_cookie.left;
244169695Skan
245169695Skan	/* Remove a trailing newline */
246169695Skan	if (tbuf[cnt - 1] == '\n')
247169695Skan		cnt--;
248169695Skan
249169695Skan	/* Output to stderr if requested. */
250169695Skan	if (LogStat & LOG_PERROR) {
251169695Skan		struct iovec iov[2];
252169695Skan		struct iovec *v = iov;
253169695Skan
254169695Skan		v->iov_base = stdp;
255169695Skan		v->iov_len = cnt - (stdp - tbuf);
256169695Skan		++v;
257169695Skan		v->iov_base = "\n";
258169695Skan		v->iov_len = 1;
259169695Skan		(void)_writev(STDERR_FILENO, iov, 2);
260169695Skan	}
261169695Skan
262169695Skan	/* Get connected, output the message to the local logger. */
263169695Skan	if (!opened)
264169695Skan		openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0);
265169695Skan	connectlog();
266169695Skan
267169695Skan	/*
268169695Skan	 * If the send() failed, there are two likely scenarios:
269169695Skan	 *  1) syslogd was restarted
270169695Skan	 *  2) /var/run/log is out of socket buffer space, which
271169695Skan	 *     in most cases means local DoS.
272169695Skan	 * We attempt to reconnect to /var/run/log to take care of
273169695Skan	 * case #1 and keep send()ing data to cover case #2
274169695Skan	 * to give syslogd a chance to empty its socket buffer.
275169695Skan	 *
276169695Skan	 * If we are working with a priveleged socket, then take
277169695Skan	 * only one attempt, because we don't want to freeze a
278169695Skan	 * critical application like su(1) or sshd(8).
279169695Skan	 *
280169695Skan	 */
281169695Skan
282169695Skan	if (send(LogFile, tbuf, cnt, 0) < 0) {
283169695Skan		if (errno != ENOBUFS) {
284169695Skan			disconnectlog();
285169695Skan			connectlog();
286169695Skan		}
287169695Skan		do {
288169695Skan			_usleep(1);
289169695Skan			if (send(LogFile, tbuf, cnt, 0) >= 0) {
290169695Skan				THREAD_UNLOCK();
291169695Skan				return;
292169695Skan			}
293169695Skan			if (status == CONNPRIV)
294169695Skan				break;
295169695Skan		} while (errno == ENOBUFS);
296169695Skan	} else {
297169695Skan		THREAD_UNLOCK();
298169695Skan		return;
299169695Skan	}
300169695Skan
301169695Skan	/*
302169695Skan	 * Output the message to the console; try not to block
303169695Skan	 * as a blocking console should not stop other processes.
304169695Skan	 * Make sure the error reported is the one from the syslogd failure.
305169695Skan	 */
306169695Skan	if (LogStat & LOG_CONS &&
307169695Skan	    (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
308169695Skan		struct iovec iov[2];
309169695Skan		struct iovec *v = iov;
310169695Skan
311169695Skan		p = strchr(tbuf, '>') + 1;
312169695Skan		v->iov_base = p;
313169695Skan		v->iov_len = cnt - (p - tbuf);
314169695Skan		++v;
315169695Skan		v->iov_base = "\r\n";
316169695Skan		v->iov_len = 2;
317169695Skan		(void)_writev(fd, iov, 2);
318169695Skan		(void)_close(fd);
319169695Skan	}
320169695Skan
321169695Skan	THREAD_UNLOCK();
322169695Skan}
323169695Skan
324169695Skan/* Should be called with mutex acquired */
325169695Skanstatic void
326169695Skandisconnectlog(void)
327169695Skan{
328169695Skan	/*
329169695Skan	 * If the user closed the FD and opened another in the same slot,
330169695Skan	 * that's their problem.  They should close it before calling on
331169695Skan	 * system services.
332169695Skan	 */
333169695Skan	if (LogFile != -1) {
334169695Skan		_close(LogFile);
335169695Skan		LogFile = -1;
336169695Skan	}
337169695Skan	status = NOCONN;			/* retry connect */
338169695Skan}
339169695Skan
340169695Skan/* Should be called with mutex acquired */
341169695Skanstatic void
342169695Skanconnectlog(void)
343169695Skan{
344169695Skan	struct sockaddr_un SyslogAddr;	/* AF_UNIX address of local logger */
345169695Skan
346169695Skan	if (LogFile == -1) {
347169695Skan		if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
348169695Skan			return;
349169695Skan		(void)_fcntl(LogFile, F_SETFD, 1);
350169695Skan	}
351169695Skan	if (LogFile != -1 && status == NOCONN) {
352169695Skan		SyslogAddr.sun_len = sizeof(SyslogAddr);
353169695Skan		SyslogAddr.sun_family = AF_UNIX;
354169695Skan
355169695Skan		/*
356169695Skan		 * First try priveleged socket. If no success,
357169695Skan		 * then try default socket.
358169695Skan		 */
359169695Skan		(void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV,
360169695Skan		    sizeof SyslogAddr.sun_path);
361169695Skan		if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
362169695Skan		    sizeof(SyslogAddr)) != -1)
363169695Skan			status = CONNPRIV;
364169695Skan
365169695Skan		if (status == NOCONN) {
366169695Skan			(void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
367169695Skan			    sizeof SyslogAddr.sun_path);
368169695Skan			if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
369169695Skan			    sizeof(SyslogAddr)) != -1)
370169695Skan				status = CONNDEF;
371169695Skan		}
372169695Skan
373169695Skan		if (status == NOCONN) {
374169695Skan			/*
375169695Skan			 * Try the old "/dev/log" path, for backward
376169695Skan			 * compatibility.
377169695Skan			 */
378169695Skan			(void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG,
379169695Skan			    sizeof SyslogAddr.sun_path);
380169695Skan			if (_connect(LogFile, (struct sockaddr *)&SyslogAddr,
381169695Skan			    sizeof(SyslogAddr)) != -1)
382169695Skan				status = CONNDEF;
383169695Skan		}
384169695Skan
385169695Skan		if (status == NOCONN) {
386169695Skan			(void)_close(LogFile);
387169695Skan			LogFile = -1;
388169695Skan		}
389169695Skan	}
390169695Skan}
391169695Skan
392169695Skanstatic void
393169695Skanopenlog_unlocked(const char *ident, int logstat, int logfac)
394169695Skan{
395169695Skan	if (ident != NULL)
396169695Skan		LogTag = ident;
397169695Skan	LogStat = logstat;
398169695Skan	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
399169695Skan		LogFacility = logfac;
400169695Skan
401169695Skan	if (LogStat & LOG_NDELAY)	/* open immediately */
402169695Skan		connectlog();
403169695Skan
404169695Skan	opened = 1;	/* ident and facility has been set */
405169695Skan}
406169695Skan
407169695Skanvoid
408169695Skanopenlog(const char *ident, int logstat, int logfac)
409169695Skan{
410169695Skan	THREAD_LOCK();
411169695Skan	openlog_unlocked(ident, logstat, logfac);
412169695Skan	THREAD_UNLOCK();
413169695Skan}
414169695Skan
415169695Skan
416169695Skanvoid
417169695Skancloselog(void)
418169695Skan{
419169695Skan	THREAD_LOCK();
420169695Skan	(void)_close(LogFile);
421169695Skan	LogFile = -1;
422169695Skan	LogTag = NULL;
423169695Skan	status = NOCONN;
424169695Skan	THREAD_UNLOCK();
425169695Skan}
426169695Skan
427169695Skan/* setlogmask -- set the log mask level */
428169695Skanint
429169695Skansetlogmask(int pmask)
430169695Skan{
431169695Skan	int omask;
432169695Skan
433169695Skan	THREAD_LOCK();
434169695Skan	omask = LogMask;
435169695Skan	if (pmask != 0)
436169695Skan		LogMask = pmask;
437169695Skan	THREAD_UNLOCK();
438169695Skan	return (omask);
439169695Skan}
440169695Skan