syslog.c revision 1.39 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