xref: /netbsd-src/lib/libc/gen/syslog.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: syslog.c,v 1.31 2005/11/29 03:11:59 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
35 static char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
36 #else
37 __RCSID("$NetBSD: syslog.c,v 1.31 2005/11/29 03:11:59 christos Exp $");
38 #endif
39 #endif /* LIBC_SCCS and not lint */
40 
41 #include "namespace.h"
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/syslog.h>
45 #include <sys/uio.h>
46 #include <sys/un.h>
47 #include <netdb.h>
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <paths.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 #include "reentrant.h"
59 
60 #ifdef __weak_alias
61 __weak_alias(closelog,_closelog)
62 __weak_alias(openlog,_openlog)
63 __weak_alias(setlogmask,_setlogmask)
64 __weak_alias(syslog,_syslog)
65 __weak_alias(vsyslog,_vsyslog)
66 #endif
67 
68 static int	LogFile = -1;		/* fd for log */
69 static int	connected;		/* have done connect */
70 static int	LogStat = 0;		/* status bits, set by openlog() */
71 static const char *LogTag = NULL;	/* string to tag the entry with */
72 static int	LogFacility = LOG_USER;	/* default facility code */
73 static int	LogMask = 0xff;		/* mask of priorities to be logged */
74 
75 static void	openlog_unlocked __P((const char *, int, int));
76 static void	closelog_unlocked __P((void));
77 
78 #ifdef _REENTRANT
79 static mutex_t	syslog_mutex = MUTEX_INITIALIZER;
80 #endif
81 
82 #ifdef lint
83 static const int ZERO = 0;
84 #else
85 #define ZERO	0
86 #endif
87 
88 /*
89  * syslog, vsyslog --
90  *	print message on log file; output is intended for syslogd(8).
91  */
92 void
93 syslog(int pri, const char *fmt, ...)
94 {
95 	va_list ap;
96 
97 	va_start(ap, fmt);
98 	vsyslog(pri, fmt, ap);
99 	va_end(ap);
100 }
101 
102 void
103 vsyslog(pri, fmt, ap)
104 	int pri;
105 	const char *fmt;
106 	_BSD_VA_LIST_ ap;
107 {
108 	size_t cnt;
109 	char ch, *p, *t;
110 	time_t now;
111 	struct tm tmnow;
112 	int fd, saved_errno, tries;
113 #define	TBUF_LEN	2048
114 #define	FMT_LEN		1024
115 	char *stdp = NULL;	/* pacify gcc */
116 	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
117 	size_t tbuf_left, fmt_left, prlen;
118 
119 #define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
120 	/* Check for invalid bits. */
121 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
122 		syslog(INTERNALLOG,
123 		    "syslog: unknown facility/priority: %x", pri);
124 		pri &= LOG_PRIMASK|LOG_FACMASK;
125 	}
126 
127 	/* Check priority against setlogmask values. */
128 	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask))
129 		return;
130 
131 	saved_errno = errno;
132 
133 	/* Set default facility if none specified. */
134 	if ((pri & LOG_FACMASK) == 0)
135 		pri |= LogFacility;
136 
137 	/* Build the message. */
138 
139 	/*
140  	 * Although it's tempting, we can't ignore the possibility of
141 	 * overflowing the buffer when assembling the "fixed" portion
142 	 * of the message.  Strftime's "%h" directive expands to the
143 	 * locale's abbreviated month name, but if the user has the
144 	 * ability to construct to his own locale files, it may be
145 	 * arbitrarily long.
146 	 */
147 	(void)time(&now);
148 
149 	p = tbuf;
150 	tbuf_left = TBUF_LEN;
151 
152 #define	DEC()							\
153 	do {							\
154 		if (prlen >= tbuf_left)				\
155 			prlen = tbuf_left - 1;			\
156 		p += prlen;					\
157 		tbuf_left -= prlen;				\
158 	} while (ZERO)
159 
160 	prlen = snprintf(p, tbuf_left, "<%d>", pri);
161 	DEC();
162 
163 	tzset(); /* strftime() implies tzset(), localtime_r() doesn't. */
164 	prlen = strftime(p, tbuf_left, "%h %e %T ", localtime_r(&now, &tmnow));
165 	DEC();
166 
167 	if (LogStat & LOG_PERROR)
168 		stdp = p;
169 	if (LogTag == NULL)
170 		LogTag = getprogname();
171 	if (LogTag != NULL) {
172 		prlen = snprintf(p, tbuf_left, "%s", LogTag);
173 		DEC();
174 	}
175 	if (LogStat & LOG_PID) {
176 		prlen = snprintf(p, tbuf_left, "[%d]", getpid());
177 		DEC();
178 	}
179 	if (LogTag != NULL) {
180 		if (tbuf_left > 1) {
181 			*p++ = ':';
182 			tbuf_left--;
183 		}
184 		if (tbuf_left > 1) {
185 			*p++ = ' ';
186 			tbuf_left--;
187 		}
188 	}
189 
190 	/*
191 	 * We wouldn't need this mess if printf handled %m, or if
192 	 * strerror() had been invented before syslog().
193 	 */
194 	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
195 		if (ch == '%' && fmt[1] == 'm') {
196 			++fmt;
197 			prlen = snprintf(t, fmt_left, "%s",
198 			    strerror(saved_errno));
199 			if (prlen >= fmt_left)
200 				prlen = fmt_left - 1;
201 			t += prlen;
202 			fmt_left -= prlen;
203 		} else {
204 			if (fmt_left > 1) {
205 				*t++ = ch;
206 				fmt_left--;
207 			}
208 		}
209 	}
210 	*t = '\0';
211 
212 	prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
213 	DEC();
214 	cnt = p - tbuf;
215 
216 	/* Output to stderr if requested. */
217 	if (LogStat & LOG_PERROR) {
218 		struct iovec iov[2];
219 
220 		iov[0].iov_base = stdp;
221 		iov[0].iov_len = cnt - (stdp - tbuf);
222 		iov[1].iov_base = __UNCONST("\n");
223 		iov[1].iov_len = 1;
224 		(void)writev(STDERR_FILENO, iov, 2);
225 	}
226 
227 	/* Get connected, output the message to the local logger. */
228 	mutex_lock(&syslog_mutex);
229 	/*
230 	 * Try to send it twice.  If the first try fails, we might
231 	 * be able to simply reconnect and send the message (which
232 	 * means syslogd was restarted since we connected the first
233 	 * time).  If the second attempt doesn't work, then something
234 	 * else is wrong and there's probably not much we can do
235 	 * here.
236 	 */
237 	for (tries = 1; tries <= 2; tries++) {
238 		if (!connected)
239 			openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0);
240 		if (send(LogFile, tbuf, cnt, 0) >= 0) {
241 			mutex_unlock(&syslog_mutex);
242 			return;
243 		}
244 		else
245 			closelog_unlocked();
246 	}
247 	mutex_unlock(&syslog_mutex);
248 
249 	/*
250 	 * Output the message to the console; don't worry about blocking,
251 	 * if console blocks everything will.  Make sure the error reported
252 	 * is the one from the syslogd failure.
253 	 */
254 	if (LogStat & LOG_CONS &&
255 	    (fd = open(_PATH_CONSOLE, O_WRONLY, 0)) >= 0) {
256 		struct iovec iov[2];
257 
258 		p = strchr(tbuf, '>') + 1;
259 		iov[0].iov_base = p;
260 		iov[0].iov_len = cnt - (p - tbuf);
261 		iov[1].iov_base = __UNCONST("\r\n");
262 		iov[1].iov_len = 2;
263 		(void)writev(fd, iov, 2);
264 		(void)close(fd);
265 	}
266 }
267 
268 static struct sockaddr_un SyslogAddr;	/* AF_LOCAL address of local logger */
269 
270 static void
271 openlog_unlocked(ident, logstat, logfac)
272 	const char *ident;
273 	int logstat, logfac;
274 {
275 
276 	if (ident != NULL)
277 		LogTag = ident;
278 	LogStat = logstat;
279 	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
280 		LogFacility = logfac;
281 
282 	if (LogFile == -1) {
283 		SyslogAddr.sun_family = AF_LOCAL;
284 		(void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
285 		    sizeof(SyslogAddr.sun_path));
286 		if (LogStat & LOG_NDELAY) {
287 			if ((LogFile = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1)
288 				return;
289 			(void)fcntl(LogFile, F_SETFD, 1);
290 		}
291 	}
292 	if (LogFile != -1 && !connected) {
293 		if (connect(LogFile, (struct sockaddr *)(void *)&SyslogAddr,
294 		    SUN_LEN(&SyslogAddr)) == -1) {
295 			(void)close(LogFile);
296 			LogFile = -1;
297 		} else
298 			connected = 1;
299 	}
300 }
301 
302 void
303 openlog(ident, logstat, logfac)
304 	const char *ident;
305 	int logstat, logfac;
306 {
307 
308 	mutex_lock(&syslog_mutex);
309 	openlog_unlocked(ident, logstat, logfac);
310 	mutex_unlock(&syslog_mutex);
311 }
312 
313 static void
314 closelog_unlocked()
315 {
316 	(void)close(LogFile);
317 	LogFile = -1;
318 	connected = 0;
319 }
320 
321 void
322 closelog()
323 {
324 
325 	mutex_lock(&syslog_mutex);
326 	closelog_unlocked();
327 	mutex_unlock(&syslog_mutex);
328 }
329 
330 /* setlogmask -- set the log mask level */
331 int
332 setlogmask(pmask)
333 	int pmask;
334 {
335 	int omask;
336 
337 	omask = LogMask;
338 	if (pmask != 0)
339 		LogMask = pmask;
340 	return (omask);
341 }
342