xref: /netbsd-src/lib/libc/gen/syslog.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: syslog.c,v 1.39 2006/11/22 17:23:25 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.39 2006/11/22 17:23:25 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 #include "extern.h"
60 
61 #ifdef __weak_alias
62 __weak_alias(closelog,_closelog)
63 __weak_alias(openlog,_openlog)
64 __weak_alias(setlogmask,_setlogmask)
65 __weak_alias(syslog,_syslog)
66 __weak_alias(vsyslog,_vsyslog)
67 
68 __weak_alias(closelog_r,_closelog_r)
69 __weak_alias(openlog_r,_openlog_r)
70 __weak_alias(setlogmask_r,_setlogmask_r)
71 __weak_alias(syslog_r,_syslog_r)
72 __weak_alias(vsyslog_r,_vsyslog_r)
73 __weak_alias(syslog_ss,_syslog_ss)
74 __weak_alias(vsyslog_ss,_vsyslog_ss)
75 #endif
76 
77 static struct syslog_data sdata = SYSLOG_DATA_INIT;
78 
79 static void	openlog_unlocked_r(const char *, int, int,
80     struct syslog_data *);
81 static void	disconnectlog_r(struct syslog_data *);
82 static void	connectlog_r(struct syslog_data *);
83 
84 #define LOG_SIGNAL_SAFE	(int)0x80000000
85 
86 
87 #ifdef _REENTRANT
88 static mutex_t	syslog_mutex = MUTEX_INITIALIZER;
89 #endif
90 
91 /*
92  * syslog, vsyslog --
93  *	print message on log file; output is intended for syslogd(8).
94  */
95 void
96 syslog(int pri, const char *fmt, ...)
97 {
98 	va_list ap;
99 
100 	va_start(ap, fmt);
101 	vsyslog(pri, fmt, ap);
102 	va_end(ap);
103 }
104 
105 void
106 vsyslog(int pri, const char *fmt, va_list ap)
107 {
108 	vsyslog_r(pri, &sdata, fmt, ap);
109 }
110 
111 void
112 openlog(const char *ident, int logstat, int logfac)
113 {
114 	openlog_r(ident, logstat, logfac, &sdata);
115 }
116 
117 void
118 closelog(void)
119 {
120 	closelog_r(&sdata);
121 }
122 
123 /* setlogmask -- set the log mask level */
124 int
125 setlogmask(int pmask)
126 {
127 	return setlogmask_r(pmask, &sdata);
128 }
129 
130 /* Reentrant version of syslog, i.e. syslog_r() */
131 
132 void
133 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
134 {
135 	va_list ap;
136 
137 	va_start(ap, fmt);
138 	vsyslog_r(pri, data, fmt, ap);
139 	va_end(ap);
140 }
141 
142 void
143 syslog_ss(int pri, struct syslog_data *data, const char *fmt, ...)
144 {
145 	va_list ap;
146 
147 	va_start(ap, fmt);
148 	vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
149 	va_end(ap);
150 }
151 
152 void
153 vsyslog_ss(int pri, struct syslog_data *data, const char *fmt, va_list ap)
154 {
155 	vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
156 }
157 
158 void
159 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
160 {
161 	size_t cnt, prlen;
162 	char ch, *p, *t;
163 	time_t now;
164 	struct tm tmnow;
165 	int fd, saved_errno;
166 #define	TBUF_LEN	2048
167 #define	FMT_LEN		1024
168 	char *stdp = NULL;	/* pacify gcc */
169 	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
170 	size_t tbuf_left, fmt_left;
171 	int signal_safe = pri & LOG_SIGNAL_SAFE;
172 
173 	pri &= ~LOG_SIGNAL_SAFE;
174 
175 #define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
176 	/* Check for invalid bits. */
177 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
178 		syslog_r(INTERNALLOG | signal_safe, data,
179 		    "syslog_r: unknown facility/priority: %x", pri);
180 		pri &= LOG_PRIMASK|LOG_FACMASK;
181 	}
182 
183 	/* Check priority against setlogmask values. */
184 	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
185 		return;
186 
187 	saved_errno = errno;
188 
189 	/* Set default facility if none specified. */
190 	if ((pri & LOG_FACMASK) == 0)
191 		pri |= data->log_fac;
192 
193 	/* Build the message. */
194 
195 	/*
196  	 * Although it's tempting, we can't ignore the possibility of
197 	 * overflowing the buffer when assembling the "fixed" portion
198 	 * of the message.  Strftime's "%h" directive expands to the
199 	 * locale's abbreviated month name, but if the user has the
200 	 * ability to construct to his own locale files, it may be
201 	 * arbitrarily long.
202 	 */
203 	 if (!signal_safe)
204 		(void)time(&now);
205 
206 	p = tbuf;
207 	tbuf_left = TBUF_LEN;
208 
209 #define	DEC()							\
210 	do {							\
211 		if (prlen >= tbuf_left)				\
212 			prlen = tbuf_left - 1;			\
213 		p += prlen;					\
214 		tbuf_left -= prlen;				\
215 	} while (/*CONSTCOND*/0)
216 
217 	prlen = snprintf_ss(p, tbuf_left, "<%d>", pri);
218 	DEC();
219 
220 	if (!signal_safe) {
221 		/* strftime() implies tzset(), localtime_r() doesn't. */
222 		tzset();
223 		prlen = strftime(p, tbuf_left, "%h %e %T ",
224 		    localtime_r(&now, &tmnow));
225 		DEC();
226 	}
227 
228 	if (data->log_stat & LOG_PERROR)
229 		stdp = p;
230 	if (data->log_tag == NULL)
231 		data->log_tag = getprogname();
232 	if (data->log_tag != NULL) {
233 		prlen = snprintf_ss(p, tbuf_left, "%s", data->log_tag);
234 		DEC();
235 	}
236 	if (data->log_stat & LOG_PID) {
237 		prlen = snprintf_ss(p, tbuf_left, "[%d]", getpid());
238 		DEC();
239 	}
240 	if (data->log_tag != NULL) {
241 		if (tbuf_left > 1) {
242 			*p++ = ':';
243 			tbuf_left--;
244 		}
245 		if (tbuf_left > 1) {
246 			*p++ = ' ';
247 			tbuf_left--;
248 		}
249 	}
250 
251 	/*
252 	 * We wouldn't need this mess if printf handled %m, or if
253 	 * strerror() had been invented before syslog().
254 	 */
255 	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
256 		if (ch == '%' && fmt[1] == 'm') {
257 			char ebuf[128];
258 			++fmt;
259 			if (signal_safe ||
260 			    strerror_r(saved_errno, ebuf, sizeof(ebuf)))
261 				prlen = snprintf_ss(t, fmt_left, "Error %d",
262 				    saved_errno);
263 			else
264 				prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
265 			if (prlen >= fmt_left)
266 				prlen = fmt_left - 1;
267 			t += prlen;
268 			fmt_left -= prlen;
269 		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
270 			*t++ = '%';
271 			*t++ = '%';
272 			fmt++;
273 			fmt_left -= 2;
274 		} else {
275 			if (fmt_left > 1) {
276 				*t++ = ch;
277 				fmt_left--;
278 			}
279 		}
280 	}
281 	*t = '\0';
282 
283 	if (signal_safe)
284 		prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
285 	else
286 		prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
287 	DEC();
288 	cnt = p - tbuf;
289 
290 	/* Output to stderr if requested. */
291 	if (data->log_stat & LOG_PERROR) {
292 		struct iovec iov[2];
293 
294 		iov[0].iov_base = stdp;
295 		iov[0].iov_len = cnt - (stdp - tbuf);
296 		iov[1].iov_base = __UNCONST("\n");
297 		iov[1].iov_len = 1;
298 		(void)writev(STDERR_FILENO, iov, 2);
299 	}
300 
301 	/* Get connected, output the message to the local logger. */
302 	if (data == &sdata)
303 		mutex_lock(&syslog_mutex);
304 	if (!data->opened)
305 		openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
306 	connectlog_r(data);
307 
308 	/*
309 	 * If the send() failed, there are two likely scenarios:
310 	 *  1) syslogd was restarted
311 	 *  2) /dev/log is out of socket buffer space
312 	 * We attempt to reconnect to /dev/log to take care of
313 	 * case #1 and keep send()ing data to cover case #2
314 	 * to give syslogd a chance to empty its socket buffer.
315 	 */
316 	if (send(data->log_file, tbuf, cnt, 0) == -1) {
317 		if (errno != ENOBUFS) {
318 			disconnectlog_r(data);
319 			connectlog_r(data);
320 		}
321 		do {
322 			usleep(1);
323 			if (send(data->log_file, tbuf, cnt, 0) != -1)
324 				break;
325 		} while (errno == ENOBUFS);
326 	}
327 	if (data == &sdata)
328 		mutex_unlock(&syslog_mutex);
329 
330 	/*
331 	 * Output the message to the console; try not to block
332 	 * as a blocking console should not stop other processes.
333 	 * Make sure the error reported is the one from the syslogd failure.
334 	 */
335 	if ((data->log_stat & LOG_CONS) &&
336 	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
337 		struct iovec iov[2];
338 
339 		p = strchr(tbuf, '>') + 1;
340 		iov[0].iov_base = p;
341 		iov[0].iov_len = cnt - (p - tbuf);
342 		iov[1].iov_base = __UNCONST("\r\n");
343 		iov[1].iov_len = 2;
344 		(void)writev(fd, iov, 2);
345 		(void)close(fd);
346 	}
347 	if (data != &sdata)
348 		closelog_r(data);
349 }
350 
351 static void
352 disconnectlog_r(struct syslog_data *data)
353 {
354 	/*
355 	 * If the user closed the FD and opened another in the same slot,
356 	 * that's their problem.  They should close it before calling on
357 	 * system services.
358 	 */
359 	if (data->log_file != -1) {
360 		(void)close(data->log_file);
361 		data->log_file = -1;
362 	}
363 	data->connected = 0;		/* retry connect */
364 }
365 
366 static void
367 connectlog_r(struct syslog_data *data)
368 {
369 	/* AF_UNIX address of local logger */
370 	static const struct sockaddr_un sun = {
371 		.sun_family = AF_LOCAL,
372 		.sun_len = sizeof(sun),
373 		.sun_path = _PATH_LOG,
374 	};
375 
376 	if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
377 		if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
378 			return;
379 		(void)fcntl(data->log_file, F_SETFD, FD_CLOEXEC);
380 		data->connected = 0;
381 	}
382 	if (!data->connected) {
383 		if (connect(data->log_file,
384 		    (const struct sockaddr *)(const void *)&sun,
385 		    sizeof(sun)) == -1) {
386 			(void)close(data->log_file);
387 			data->log_file = -1;
388  		} else
389 			data->connected = 1;
390 	}
391 }
392 
393 static void
394 openlog_unlocked_r(const char *ident, int logstat, int logfac,
395     struct syslog_data *data)
396 {
397 	if (ident != NULL)
398 		data->log_tag = ident;
399 	data->log_stat = logstat;
400 	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
401 		data->log_fac = logfac;
402 
403 	if (data->log_stat & LOG_NDELAY)	/* open immediately */
404 		connectlog_r(data);
405 }
406 
407 void
408 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
409 {
410 	if (data == &sdata)
411 		mutex_lock(&syslog_mutex);
412 	openlog_unlocked_r(ident, logstat, logfac, data);
413 	if (data == &sdata)
414 		mutex_unlock(&syslog_mutex);
415 }
416 
417 void
418 closelog_r(struct syslog_data *data)
419 {
420 	if (data == &sdata)
421 		mutex_lock(&syslog_mutex);
422 	(void)close(data->log_file);
423 	data->log_file = -1;
424 	data->connected = 0;
425 	data->log_tag = NULL;
426 	if (data == &sdata)
427 		mutex_unlock(&syslog_mutex);
428 }
429 
430 int
431 setlogmask_r(int pmask, struct syslog_data *data)
432 {
433 	int omask;
434 
435 	omask = data->log_mask;
436 	if (pmask != 0)
437 		data->log_mask = pmask;
438 	return omask;
439 }
440