syslog.c revision 1.42 1 /* $NetBSD: syslog.c,v 1.42 2008/10/22 02:17:29 dogcow 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.42 2008/10/22 02:17:29 dogcow 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 int opened;
174
175 pri &= ~LOG_SIGNAL_SAFE;
176
177 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
178 /* Check for invalid bits. */
179 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
180 syslog_r(INTERNALLOG | signal_safe, data,
181 "syslog_r: unknown facility/priority: %x", pri);
182 pri &= LOG_PRIMASK|LOG_FACMASK;
183 }
184
185 /* Check priority against setlogmask values. */
186 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
187 return;
188
189 saved_errno = errno;
190
191 /* Set default facility if none specified. */
192 if ((pri & LOG_FACMASK) == 0)
193 pri |= data->log_fac;
194
195 /* Build the message. */
196
197 /*
198 * Although it's tempting, we can't ignore the possibility of
199 * overflowing the buffer when assembling the "fixed" portion
200 * of the message. Strftime's "%h" directive expands to the
201 * locale's abbreviated month name, but if the user has the
202 * ability to construct to his own locale files, it may be
203 * arbitrarily long.
204 */
205 if (!signal_safe)
206 (void)time(&now);
207
208 p = tbuf;
209 tbuf_left = TBUF_LEN;
210
211 #define DEC() \
212 do { \
213 if (prlen >= tbuf_left) \
214 prlen = tbuf_left - 1; \
215 p += prlen; \
216 tbuf_left -= prlen; \
217 } while (/*CONSTCOND*/0)
218
219 prlen = snprintf_ss(p, tbuf_left, "<%d>", pri);
220 DEC();
221
222 if (!signal_safe) {
223 /* strftime() implies tzset(), localtime_r() doesn't. */
224 tzset();
225 prlen = strftime(p, tbuf_left, "%h %e %T ",
226 localtime_r(&now, &tmnow));
227 DEC();
228 }
229
230 if (data->log_stat & LOG_PERROR)
231 stdp = p;
232 if (data->log_tag == NULL)
233 data->log_tag = getprogname();
234 if (data->log_tag != NULL) {
235 prlen = snprintf_ss(p, tbuf_left, "%s", data->log_tag);
236 DEC();
237 }
238 if (data->log_stat & LOG_PID) {
239 prlen = snprintf_ss(p, tbuf_left, "[%d]", getpid());
240 DEC();
241 }
242 if (data->log_tag != NULL) {
243 if (tbuf_left > 1) {
244 *p++ = ':';
245 tbuf_left--;
246 }
247 if (tbuf_left > 1) {
248 *p++ = ' ';
249 tbuf_left--;
250 }
251 }
252
253 /*
254 * We wouldn't need this mess if printf handled %m, or if
255 * strerror() had been invented before syslog().
256 */
257 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
258 if (ch == '%' && fmt[1] == 'm') {
259 char ebuf[128];
260 ++fmt;
261 if (signal_safe ||
262 strerror_r(saved_errno, ebuf, sizeof(ebuf)))
263 prlen = snprintf_ss(t, fmt_left, "Error %d",
264 saved_errno);
265 else
266 prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
267 if (prlen >= fmt_left)
268 prlen = fmt_left - 1;
269 t += prlen;
270 fmt_left -= prlen;
271 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
272 *t++ = '%';
273 *t++ = '%';
274 fmt++;
275 fmt_left -= 2;
276 } else {
277 if (fmt_left > 1) {
278 *t++ = ch;
279 fmt_left--;
280 }
281 }
282 }
283 *t = '\0';
284
285 if (signal_safe)
286 prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
287 else
288 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
289 DEC();
290 cnt = p - tbuf;
291
292 /* Output to stderr if requested. */
293 if (data->log_stat & LOG_PERROR) {
294 struct iovec iov[2];
295
296 iov[0].iov_base = stdp;
297 iov[0].iov_len = cnt - (stdp - tbuf);
298 iov[1].iov_base = __UNCONST("\n");
299 iov[1].iov_len = 1;
300 (void)writev(STDERR_FILENO, iov, 2);
301 }
302
303 /* Get connected, output the message to the local logger. */
304 if (data == &sdata)
305 mutex_lock(&syslog_mutex);
306 opened = !data->opened;
307 if (opened)
308 openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
309 connectlog_r(data);
310
311 /*
312 * If the send() failed, there are two likely scenarios:
313 * 1) syslogd was restarted
314 * 2) /dev/log is out of socket buffer space
315 * We attempt to reconnect to /dev/log to take care of
316 * case #1 and keep send()ing data to cover case #2
317 * to give syslogd a chance to empty its socket buffer.
318 */
319 for (tries = 0; tries < MAXTRIES; tries++) {
320 if (send(data->log_file, tbuf, cnt, 0) != -1)
321 break;
322 if (errno != ENOBUFS) {
323 disconnectlog_r(data);
324 connectlog_r(data);
325 } else
326 (void)usleep(1);
327 }
328
329 /*
330 * Output the message to the console; try not to block
331 * as a blocking console should not stop other processes.
332 * Make sure the error reported is the one from the syslogd failure.
333 */
334 if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
335 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0 &&
336 (p = strchr(tbuf, '>')) != NULL) {
337 struct iovec iov[2];
338 iov[0].iov_base = ++p;
339 iov[0].iov_len = cnt - (p - tbuf);
340 iov[1].iov_base = __UNCONST("\r\n");
341 iov[1].iov_len = 2;
342 (void)writev(fd, iov, 2);
343 (void)close(fd);
344 }
345
346 if (data == &sdata)
347 mutex_unlock(&syslog_mutex);
348
349 if (data != &sdata && opened) {
350 /* preserve log tag */
351 const char *ident = data->log_tag;
352 closelog_r(data);
353 data->log_tag = ident;
354 }
355 }
356
357 static void
358 disconnectlog_r(struct syslog_data *data)
359 {
360 /*
361 * If the user closed the FD and opened another in the same slot,
362 * that's their problem. They should close it before calling on
363 * system services.
364 */
365 if (data->log_file != -1) {
366 (void)close(data->log_file);
367 data->log_file = -1;
368 }
369 data->connected = 0; /* retry connect */
370 }
371
372 static void
373 connectlog_r(struct syslog_data *data)
374 {
375 /* AF_UNIX address of local logger */
376 static const struct sockaddr_un sun = {
377 .sun_family = AF_LOCAL,
378 .sun_len = sizeof(sun),
379 .sun_path = _PATH_LOG,
380 };
381
382 if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
383 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
384 return;
385 (void)fcntl(data->log_file, F_SETFD, FD_CLOEXEC);
386 data->connected = 0;
387 }
388 if (!data->connected) {
389 if (connect(data->log_file,
390 (const struct sockaddr *)(const void *)&sun,
391 sizeof(sun)) == -1) {
392 (void)close(data->log_file);
393 data->log_file = -1;
394 } else
395 data->connected = 1;
396 }
397 }
398
399 static void
400 openlog_unlocked_r(const char *ident, int logstat, int logfac,
401 struct syslog_data *data)
402 {
403 if (ident != NULL)
404 data->log_tag = ident;
405 data->log_stat = logstat;
406 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
407 data->log_fac = logfac;
408
409 if (data->log_stat & LOG_NDELAY) /* open immediately */
410 connectlog_r(data);
411
412 data->opened = 1;
413 }
414
415 void
416 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
417 {
418 if (data == &sdata)
419 mutex_lock(&syslog_mutex);
420 openlog_unlocked_r(ident, logstat, logfac, data);
421 if (data == &sdata)
422 mutex_unlock(&syslog_mutex);
423 }
424
425 void
426 closelog_r(struct syslog_data *data)
427 {
428 if (data == &sdata)
429 mutex_lock(&syslog_mutex);
430 (void)close(data->log_file);
431 data->log_file = -1;
432 data->connected = 0;
433 data->log_tag = NULL;
434 if (data == &sdata)
435 mutex_unlock(&syslog_mutex);
436 }
437
438 int
439 setlogmask_r(int pmask, struct syslog_data *data)
440 {
441 int omask;
442
443 omask = data->log_mask;
444 if (pmask != 0)
445 data->log_mask = pmask;
446 return omask;
447 }
448