xref: /netbsd-src/lib/libc/gen/syslog.c (revision fff57c5525bbe431aee7bdb3983954f0627a42cb)
1 /*	$NetBSD: syslog.c,v 1.40 2008/06/12 20:43: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
35 static char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
36 #else
37 __RCSID("$NetBSD: syslog.c,v 1.40 2008/06/12 20:43: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/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, tries;
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 #define MAXTRIES	10
169 	char *stdp = NULL;	/* pacify gcc */
170 	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
171 	size_t tbuf_left, fmt_left;
172 	int signal_safe = pri & LOG_SIGNAL_SAFE;
173 
174 	pri &= ~LOG_SIGNAL_SAFE;
175 
176 #define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
177 	/* Check for invalid bits. */
178 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
179 		syslog_r(INTERNALLOG | signal_safe, data,
180 		    "syslog_r: unknown facility/priority: %x", pri);
181 		pri &= LOG_PRIMASK|LOG_FACMASK;
182 	}
183 
184 	/* Check priority against setlogmask values. */
185 	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
186 		return;
187 
188 	saved_errno = errno;
189 
190 	/* Set default facility if none specified. */
191 	if ((pri & LOG_FACMASK) == 0)
192 		pri |= data->log_fac;
193 
194 	/* Build the message. */
195 
196 	/*
197  	 * Although it's tempting, we can't ignore the possibility of
198 	 * overflowing the buffer when assembling the "fixed" portion
199 	 * of the message.  Strftime's "%h" directive expands to the
200 	 * locale's abbreviated month name, but if the user has the
201 	 * ability to construct to his own locale files, it may be
202 	 * arbitrarily long.
203 	 */
204 	 if (!signal_safe)
205 		(void)time(&now);
206 
207 	p = tbuf;
208 	tbuf_left = TBUF_LEN;
209 
210 #define	DEC()							\
211 	do {							\
212 		if (prlen >= tbuf_left)				\
213 			prlen = tbuf_left - 1;			\
214 		p += prlen;					\
215 		tbuf_left -= prlen;				\
216 	} while (/*CONSTCOND*/0)
217 
218 	prlen = snprintf_ss(p, tbuf_left, "<%d>", pri);
219 	DEC();
220 
221 	if (!signal_safe) {
222 		/* strftime() implies tzset(), localtime_r() doesn't. */
223 		tzset();
224 		prlen = strftime(p, tbuf_left, "%h %e %T ",
225 		    localtime_r(&now, &tmnow));
226 		DEC();
227 	}
228 
229 	if (data->log_stat & LOG_PERROR)
230 		stdp = p;
231 	if (data->log_tag == NULL)
232 		data->log_tag = getprogname();
233 	if (data->log_tag != NULL) {
234 		prlen = snprintf_ss(p, tbuf_left, "%s", data->log_tag);
235 		DEC();
236 	}
237 	if (data->log_stat & LOG_PID) {
238 		prlen = snprintf_ss(p, tbuf_left, "[%d]", getpid());
239 		DEC();
240 	}
241 	if (data->log_tag != NULL) {
242 		if (tbuf_left > 1) {
243 			*p++ = ':';
244 			tbuf_left--;
245 		}
246 		if (tbuf_left > 1) {
247 			*p++ = ' ';
248 			tbuf_left--;
249 		}
250 	}
251 
252 	/*
253 	 * We wouldn't need this mess if printf handled %m, or if
254 	 * strerror() had been invented before syslog().
255 	 */
256 	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
257 		if (ch == '%' && fmt[1] == 'm') {
258 			char ebuf[128];
259 			++fmt;
260 			if (signal_safe ||
261 			    strerror_r(saved_errno, ebuf, sizeof(ebuf)))
262 				prlen = snprintf_ss(t, fmt_left, "Error %d",
263 				    saved_errno);
264 			else
265 				prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
266 			if (prlen >= fmt_left)
267 				prlen = fmt_left - 1;
268 			t += prlen;
269 			fmt_left -= prlen;
270 		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
271 			*t++ = '%';
272 			*t++ = '%';
273 			fmt++;
274 			fmt_left -= 2;
275 		} else {
276 			if (fmt_left > 1) {
277 				*t++ = ch;
278 				fmt_left--;
279 			}
280 		}
281 	}
282 	*t = '\0';
283 
284 	if (signal_safe)
285 		prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
286 	else
287 		prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
288 	DEC();
289 	cnt = p - tbuf;
290 
291 	/* Output to stderr if requested. */
292 	if (data->log_stat & LOG_PERROR) {
293 		struct iovec iov[2];
294 
295 		iov[0].iov_base = stdp;
296 		iov[0].iov_len = cnt - (stdp - tbuf);
297 		iov[1].iov_base = __UNCONST("\n");
298 		iov[1].iov_len = 1;
299 		(void)writev(STDERR_FILENO, iov, 2);
300 	}
301 
302 	/* Get connected, output the message to the local logger. */
303 	if (data == &sdata)
304 		mutex_lock(&syslog_mutex);
305 	if (!data->opened)
306 		openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
307 	connectlog_r(data);
308 
309 	/*
310 	 * If the send() failed, there are two likely scenarios:
311 	 *  1) syslogd was restarted
312 	 *  2) /dev/log is out of socket buffer space
313 	 * We attempt to reconnect to /dev/log to take care of
314 	 * case #1 and keep send()ing data to cover case #2
315 	 * to give syslogd a chance to empty its socket buffer.
316 	 */
317 	for (tries = 0; tries < MAXTRIES; tries++) {
318 		if (send(data->log_file, tbuf, cnt, 0) != -1)
319 			break;
320 		if (errno != ENOBUFS) {
321 			disconnectlog_r(data);
322 			connectlog_r(data);
323 		} else
324 			(void)usleep(1);
325 	}
326 
327 	/*
328 	 * Output the message to the console; try not to block
329 	 * as a blocking console should not stop other processes.
330 	 * Make sure the error reported is the one from the syslogd failure.
331 	 */
332 	if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
333 	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0 &&
334 	    (p = strchr(tbuf, '>')) != NULL) {
335 		struct iovec iov[2];
336 		iov[0].iov_base = ++p;
337 		iov[0].iov_len = cnt - (p - tbuf);
338 		iov[1].iov_base = __UNCONST("\r\n");
339 		iov[1].iov_len = 2;
340 		(void)writev(fd, iov, 2);
341 		(void)close(fd);
342 	}
343 
344 	if (data == &sdata)
345 		mutex_unlock(&syslog_mutex);
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