syslog.c revision 1.39.18.1 1 /* $NetBSD: syslog.c,v 1.39.18.1 2008/06/23 04:29:31 wrstuden 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.18.1 2008/06/23 04:29:31 wrstuden 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