syslog.c revision 1.51 1 /* $NetBSD: syslog.c,v 1.51 2012/10/10 22:50:51 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.51 2012/10/10 22:50:51 christos Exp $");
38 #endif
39 #endif /* LIBC_SCCS and not lint */
40
41 #include "namespace.h"
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/socket.h>
45 #include <sys/syslog.h>
46 #include <sys/uio.h>
47 #include <sys/un.h>
48 #include <netdb.h>
49
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <paths.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <time.h>
58 #include <unistd.h>
59 #include "reentrant.h"
60 #include "extern.h"
61
62 #ifdef __weak_alias
63 __weak_alias(closelog,_closelog)
64 __weak_alias(openlog,_openlog)
65 __weak_alias(setlogmask,_setlogmask)
66 __weak_alias(syslog,_syslog)
67 __weak_alias(vsyslog,_vsyslog)
68 __weak_alias(syslogp,_syslogp)
69 __weak_alias(vsyslogp,_vsyslogp)
70
71 __weak_alias(syslog_ss,_syslog_ss)
72 __weak_alias(vsyslog_ss,_vsyslog_ss)
73 __weak_alias(syslogp_ss,_syslogp_ss)
74 __weak_alias(vsyslogp_ss,_vsyslogp_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 /*
112 * syslogp, vsyslogp --
113 * like syslog but take additional arguments for MSGID and SD
114 */
115 void
116 syslogp(int pri, const char *msgid, const char *sdfmt, const char *msgfmt, ...)
117 {
118 va_list ap;
119
120 va_start(ap, msgfmt);
121 vsyslogp(pri, msgid, sdfmt, msgfmt, ap);
122 va_end(ap);
123 }
124
125 void
126 vsyslogp(int pri, const char *msgid, const char *sdfmt, const char *msgfmt, va_list ap)
127 {
128 vsyslogp_r(pri, &sdata, msgid, sdfmt, msgfmt, ap);
129 }
130
131 void
132 openlog(const char *ident, int logstat, int logfac)
133 {
134 openlog_r(ident, logstat, logfac, &sdata);
135 }
136
137 void
138 closelog(void)
139 {
140 closelog_r(&sdata);
141 }
142
143 /* setlogmask -- set the log mask level */
144 int
145 setlogmask(int pmask)
146 {
147 return setlogmask_r(pmask, &sdata);
148 }
149
150 /* Reentrant version of syslog, i.e. syslog_r() */
151
152 void
153 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
154 {
155 va_list ap;
156
157 va_start(ap, fmt);
158 vsyslog_r(pri, data, fmt, ap);
159 va_end(ap);
160 }
161
162 void
163 syslogp_r(int pri, struct syslog_data *data, const char *msgid,
164 const char *sdfmt, const char *msgfmt, ...)
165 {
166 va_list ap;
167
168 va_start(ap, msgfmt);
169 vsyslogp_r(pri, data, msgid, sdfmt, msgfmt, ap);
170 va_end(ap);
171 }
172
173 void
174 syslog_ss(int pri, struct syslog_data *data, const char *fmt, ...)
175 {
176 va_list ap;
177
178 va_start(ap, fmt);
179 vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
180 va_end(ap);
181 }
182
183 void
184 syslogp_ss(int pri, struct syslog_data *data, const char *msgid,
185 const char *sdfmt, const char *msgfmt, ...)
186 {
187 va_list ap;
188
189 va_start(ap, msgfmt);
190 vsyslogp_r(pri | LOG_SIGNAL_SAFE, data, msgid, sdfmt, msgfmt, ap);
191 va_end(ap);
192 }
193
194 void
195 vsyslog_ss(int pri, struct syslog_data *data, const char *fmt, va_list ap)
196 {
197 vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap);
198 }
199
200 void
201 vsyslogp_ss(int pri, struct syslog_data *data, const char *msgid,
202 const char *sdfmt, const char *msgfmt, va_list ap)
203 {
204 vsyslogp_r(pri | LOG_SIGNAL_SAFE, data, msgid, sdfmt, msgfmt, ap);
205 }
206
207
208 void
209 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
210 {
211 vsyslogp_r(pri, data, NULL, NULL, fmt, ap);
212 }
213
214 void
215 vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
216 const char *sdfmt, const char *msgfmt, va_list ap)
217 {
218 static const char BRCOSP[] = "]: ";
219 static const char CRLF[] = "\r\n";
220 size_t cnt, prlen, tries;
221 char ch, *p, *t;
222 struct timeval tv;
223 struct tm tmnow;
224 time_t now;
225 int fd, saved_errno;
226 #define TBUF_LEN 2048
227 #define FMT_LEN 1024
228 #define MAXTRIES 10
229 char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
230 size_t tbuf_left, fmt_left, msgsdlen;
231 char *fmt = fmt_cat;
232 int signal_safe = pri & LOG_SIGNAL_SAFE;
233 struct iovec iov[7]; /* prog + [ + pid + ]: + fmt + crlf */
234 int opened, iovcnt;
235
236 pri &= ~LOG_SIGNAL_SAFE;
237
238 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
239 /* Check for invalid bits. */
240 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
241 syslog_r(INTERNALLOG | signal_safe, data,
242 "syslog_r: unknown facility/priority: %x", pri);
243 pri &= LOG_PRIMASK|LOG_FACMASK;
244 }
245
246 /* Check priority against setlogmask values. */
247 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
248 return;
249
250 saved_errno = errno;
251
252 /* Set default facility if none specified. */
253 if ((pri & LOG_FACMASK) == 0)
254 pri |= data->log_fac;
255
256 /* Build the message. */
257 p = tbuf;
258 tbuf_left = TBUF_LEN;
259
260 #define DEC() \
261 do { \
262 if (prlen >= tbuf_left) \
263 prlen = tbuf_left - 1; \
264 p += prlen; \
265 tbuf_left -= prlen; \
266 } while (/*CONSTCOND*/0)
267
268 prlen = snprintf_ss(p, tbuf_left, "<%d>1 ", pri);
269 DEC();
270
271 if (!signal_safe && (gettimeofday(&tv, NULL) != -1)) {
272 /* strftime() implies tzset(), localtime_r() doesn't. */
273 tzset();
274 now = (time_t) tv.tv_sec;
275 localtime_r(&now, &tmnow);
276
277 prlen = strftime(p, tbuf_left, "%FT%T", &tmnow);
278 DEC();
279 prlen = snprintf(p, tbuf_left, ".%06ld", (long)tv.tv_usec);
280 DEC();
281 prlen = strftime(p, tbuf_left-1, "%z", &tmnow);
282 /* strftime gives eg. "+0200", but we need "+02:00" */
283 if (prlen == 5) {
284 p[prlen+1] = p[prlen];
285 p[prlen] = p[prlen-1];
286 p[prlen-1] = p[prlen-2];
287 p[prlen-2] = ':';
288 prlen += 1;
289 }
290 } else {
291 prlen = snprintf_ss(p, tbuf_left, "-");
292
293 /* if gmtime_r() was signal-safe we could output the UTC-time:
294 gmtime_r(&now, &tmnow);
295 prlen = strftime(p, tbuf_left, "%FT%TZ", &tmnow);
296 */
297 }
298
299 if (data->log_hostname[0] == '\0' && gethostname(data->log_hostname,
300 sizeof(data->log_hostname)) == -1) {
301 /* can this really happen? */
302 data->log_hostname[0] = '-';
303 data->log_hostname[1] = '\0';
304 }
305
306 DEC();
307 prlen = snprintf_ss(p, tbuf_left, " %s ", data->log_hostname);
308
309 if (data->log_tag == NULL)
310 data->log_tag = getprogname();
311
312 DEC();
313 prlen = snprintf_ss(p, tbuf_left, "%s ",
314 data->log_tag ? data->log_tag : "-");
315 if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
316 iovcnt = 0;
317 iov[iovcnt].iov_base = p;
318 iov[iovcnt].iov_len = prlen - 1;
319 iovcnt++;
320 }
321 DEC();
322
323 if (data->log_stat & LOG_PID) {
324 prlen = snprintf_ss(p, tbuf_left, "%d ", getpid());
325 if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
326 iov[iovcnt].iov_base = __UNCONST("[");
327 iov[iovcnt].iov_len = 1;
328 iovcnt++;
329 iov[iovcnt].iov_base = p;
330 iov[iovcnt].iov_len = prlen - 1;
331 iovcnt++;
332 iov[iovcnt].iov_base = __UNCONST(BRCOSP);
333 iov[iovcnt].iov_len = 3;
334 iovcnt++;
335 }
336 } else {
337 prlen = snprintf_ss(p, tbuf_left, "- ");
338 if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
339 iov[iovcnt].iov_base = __UNCONST(BRCOSP + 1);
340 iov[iovcnt].iov_len = 2;
341 iovcnt++;
342 }
343 }
344 DEC();
345
346 /*
347 * concat the format strings, then use one vsnprintf()
348 */
349 if (msgid != NULL && *msgid != '\0') {
350 strlcat(fmt_cat, msgid, FMT_LEN);
351 strlcat(fmt_cat, " ", FMT_LEN);
352 } else
353 strlcat(fmt_cat, "- ", FMT_LEN);
354
355 if (sdfmt != NULL && *sdfmt != '\0') {
356 strlcat(fmt_cat, sdfmt, FMT_LEN);
357 } else
358 strlcat(fmt_cat, "-", FMT_LEN);
359
360 if (data->log_stat & (LOG_PERROR|LOG_CONS))
361 msgsdlen = strlen(fmt_cat) + 1;
362 else
363 msgsdlen = 0; /* XXX: GCC */
364
365 if (msgfmt != NULL && *msgfmt != '\0') {
366 strlcat(fmt_cat, " ", FMT_LEN);
367 strlcat(fmt_cat, msgfmt, FMT_LEN);
368 }
369
370 /*
371 * We wouldn't need this mess if printf handled %m, or if
372 * strerror() had been invented before syslog().
373 */
374 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
375 if (ch == '%' && fmt[1] == 'm') {
376 char ebuf[128];
377 ++fmt;
378 if (signal_safe ||
379 strerror_r(saved_errno, ebuf, sizeof(ebuf)))
380 prlen = snprintf_ss(t, fmt_left, "Error %d",
381 saved_errno);
382 else
383 prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
384 if (prlen >= fmt_left)
385 prlen = fmt_left - 1;
386 t += prlen;
387 fmt_left -= prlen;
388 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
389 *t++ = '%';
390 *t++ = '%';
391 fmt++;
392 fmt_left -= 2;
393 } else {
394 if (fmt_left > 1) {
395 *t++ = ch;
396 fmt_left--;
397 }
398 }
399 }
400 *t = '\0';
401
402 if (signal_safe)
403 prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
404 else
405 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
406
407 if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
408 iov[iovcnt].iov_base = p + msgsdlen;
409 iov[iovcnt].iov_len = prlen - msgsdlen;
410 iovcnt++;
411 }
412
413 DEC();
414 cnt = p - tbuf;
415
416 /* Output to stderr if requested. */
417 if (data->log_stat & LOG_PERROR) {
418 iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
419 iov[iovcnt].iov_len = 1;
420 (void)writev(STDERR_FILENO, iov, iovcnt + 1);
421 }
422
423 /* Get connected, output the message to the local logger. */
424 if (data == &sdata)
425 mutex_lock(&syslog_mutex);
426 opened = !data->log_opened;
427 if (opened)
428 openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
429 connectlog_r(data);
430
431 /*
432 * If the send() failed, there are two likely scenarios:
433 * 1) syslogd was restarted
434 * 2) /dev/log is out of socket buffer space
435 * We attempt to reconnect to /dev/log to take care of
436 * case #1 and keep send()ing data to cover case #2
437 * to give syslogd a chance to empty its socket buffer.
438 */
439 for (tries = 0; tries < MAXTRIES; tries++) {
440 if (send(data->log_file, tbuf, cnt, 0) != -1)
441 break;
442 if (errno != ENOBUFS) {
443 disconnectlog_r(data);
444 connectlog_r(data);
445 } else
446 (void)usleep(1);
447 }
448
449 /*
450 * Output the message to the console; try not to block
451 * as a blocking console should not stop other processes.
452 * Make sure the error reported is the one from the syslogd failure.
453 */
454 if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
455 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
456 iov[iovcnt].iov_base = __UNCONST(CRLF);
457 iov[iovcnt].iov_len = 2;
458 (void)writev(fd, iov, iovcnt + 1);
459 (void)close(fd);
460 }
461
462 if (data == &sdata)
463 mutex_unlock(&syslog_mutex);
464
465 if (data != &sdata && opened) {
466 /* preserve log tag */
467 const char *ident = data->log_tag;
468 closelog_r(data);
469 data->log_tag = ident;
470 }
471 }
472
473 static void
474 disconnectlog_r(struct syslog_data *data)
475 {
476 /*
477 * If the user closed the FD and opened another in the same slot,
478 * that's their problem. They should close it before calling on
479 * system services.
480 */
481 if (data->log_file != -1) {
482 (void)close(data->log_file);
483 data->log_file = -1;
484 }
485 data->log_connected = 0; /* retry connect */
486 }
487
488 static void
489 connectlog_r(struct syslog_data *data)
490 {
491 /* AF_UNIX address of local logger */
492 static const struct sockaddr_un sun = {
493 .sun_family = AF_LOCAL,
494 .sun_len = sizeof(sun),
495 .sun_path = _PATH_LOG,
496 };
497
498 if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
499 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC,
500 0)) == -1)
501 return;
502 data->log_connected = 0;
503 }
504 if (!data->log_connected) {
505 if (connect(data->log_file,
506 (const struct sockaddr *)(const void *)&sun,
507 (socklen_t)sizeof(sun)) == -1) {
508 (void)close(data->log_file);
509 data->log_file = -1;
510 } else
511 data->log_connected = 1;
512 }
513 }
514
515 static void
516 openlog_unlocked_r(const char *ident, int logstat, int logfac,
517 struct syslog_data *data)
518 {
519 if (ident != NULL)
520 data->log_tag = ident;
521 data->log_stat = logstat;
522 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
523 data->log_fac = logfac;
524
525 if (data->log_stat & LOG_NDELAY) /* open immediately */
526 connectlog_r(data);
527
528 data->log_opened = 1;
529 }
530
531 void
532 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
533 {
534 if (data == &sdata)
535 mutex_lock(&syslog_mutex);
536 openlog_unlocked_r(ident, logstat, logfac, data);
537 if (data == &sdata)
538 mutex_unlock(&syslog_mutex);
539 }
540
541 void
542 closelog_r(struct syslog_data *data)
543 {
544 if (data == &sdata)
545 mutex_lock(&syslog_mutex);
546 (void)close(data->log_file);
547 data->log_file = -1;
548 data->log_connected = 0;
549 data->log_tag = NULL;
550 if (data == &sdata)
551 mutex_unlock(&syslog_mutex);
552 }
553
554 int
555 setlogmask_r(int pmask, struct syslog_data *data)
556 {
557 int omask;
558
559 omask = data->log_mask;
560 if (pmask != 0)
561 data->log_mask = pmask;
562 return omask;
563 }
564