syslog.c revision 1.26 1 /* $NetBSD: syslog.c,v 1.26 2001/02/19 22:22:16 cgd 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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #if defined(LIBC_SCCS) && !defined(lint)
38 #if 0
39 static char sccsid[] = "@(#)syslog.c 8.5 (Berkeley) 4/29/95";
40 #else
41 __RCSID("$NetBSD: syslog.c,v 1.26 2001/02/19 22:22:16 cgd Exp $");
42 #endif
43 #endif /* LIBC_SCCS and not lint */
44
45 #include "namespace.h"
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <sys/syslog.h>
49 #include <sys/uio.h>
50 #include <sys/un.h>
51 #include <netdb.h>
52
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <paths.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include "reentrant.h"
62
63 #if __STDC__
64 #include <stdarg.h>
65 #else
66 #include <varargs.h>
67 #endif
68
69 #ifdef __weak_alias
70 __weak_alias(closelog,_closelog)
71 __weak_alias(openlog,_openlog)
72 __weak_alias(setlogmask,_setlogmask)
73 __weak_alias(syslog,_syslog)
74 __weak_alias(vsyslog,_vsyslog)
75 #endif
76
77 static int LogFile = -1; /* fd for log */
78 static int connected; /* have done connect */
79 static int LogStat = 0; /* status bits, set by openlog() */
80 static const char *LogTag = NULL; /* string to tag the entry with */
81 static int LogFacility = LOG_USER; /* default facility code */
82 static int LogMask = 0xff; /* mask of priorities to be logged */
83
84 static void openlog_unlocked __P((const char *, int, int));
85 static void closelog_unlocked __P((void));
86
87 #ifdef _REENT
88 static mutex_t syslog_mutex = MUTEX_INITIALIZER;
89 #endif
90
91 #ifdef lint
92 static const int ZERO = 0;
93 #else
94 #define ZERO 0
95 #endif
96
97 /*
98 * syslog, vsyslog --
99 * print message on log file; output is intended for syslogd(8).
100 */
101 void
102 #if __STDC__
103 syslog(int pri, const char *fmt, ...)
104 #else
105 syslog(pri, fmt, va_alist)
106 int pri;
107 char *fmt;
108 va_dcl
109 #endif
110 {
111 va_list ap;
112
113 #if __STDC__
114 va_start(ap, fmt);
115 #else
116 va_start(ap);
117 #endif
118 vsyslog(pri, fmt, ap);
119 va_end(ap);
120 }
121
122 void
123 vsyslog(pri, fmt, ap)
124 int pri;
125 const char *fmt;
126 _BSD_VA_LIST_ ap;
127 {
128 size_t cnt;
129 char ch, *p, *t;
130 time_t now;
131 struct tm tmnow;
132 int fd, saved_errno;
133 #define TBUF_LEN 2048
134 #define FMT_LEN 1024
135 char *stdp = NULL; /* pacify gcc */
136 char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
137 size_t tbuf_left, fmt_left, prlen;
138
139 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
140 /* Check for invalid bits. */
141 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
142 syslog(INTERNALLOG,
143 "syslog: unknown facility/priority: %x", pri);
144 pri &= LOG_PRIMASK|LOG_FACMASK;
145 }
146
147 /* Check priority against setlogmask values. */
148 if (!(LOG_MASK(LOG_PRI(pri)) & LogMask))
149 return;
150
151 saved_errno = errno;
152
153 /* Set default facility if none specified. */
154 if ((pri & LOG_FACMASK) == 0)
155 pri |= LogFacility;
156
157 /* Build the message. */
158
159 /*
160 * Although it's tempting, we can't ignore the possibility of
161 * overflowing the buffer when assembling the "fixed" portion
162 * of the message. Strftime's "%h" directive expands to the
163 * locale's abbreviated month name, but if the user has the
164 * ability to construct to his own locale files, it may be
165 * arbitrarily long.
166 */
167 (void)time(&now);
168
169 p = tbuf;
170 tbuf_left = TBUF_LEN;
171
172 #define DEC() \
173 do { \
174 if (prlen >= tbuf_left) \
175 prlen = tbuf_left - 1; \
176 p += prlen; \
177 tbuf_left -= prlen; \
178 } while (ZERO)
179
180 prlen = snprintf(p, tbuf_left, "<%d>", pri);
181 DEC();
182
183 tzset(); /* strftime() implies tzset(), localtime_r() doesn't. */
184 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime_r(&now, &tmnow));
185 DEC();
186
187 if (LogStat & LOG_PERROR)
188 stdp = p;
189 if (LogTag == NULL)
190 LogTag = getprogname();
191 if (LogTag != NULL) {
192 prlen = snprintf(p, tbuf_left, "%s", LogTag);
193 DEC();
194 }
195 if (LogStat & LOG_PID) {
196 prlen = snprintf(p, tbuf_left, "[%d]", getpid());
197 DEC();
198 }
199 if (LogTag != NULL) {
200 if (tbuf_left > 1) {
201 *p++ = ':';
202 tbuf_left--;
203 }
204 if (tbuf_left > 1) {
205 *p++ = ' ';
206 tbuf_left--;
207 }
208 }
209
210 /*
211 * We wouldn't need this mess if printf handled %m, or if
212 * strerror() had been invented before syslog().
213 */
214 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
215 if (ch == '%' && fmt[1] == 'm') {
216 ++fmt;
217 prlen = snprintf(t, fmt_left, "%s",
218 strerror(saved_errno));
219 if (prlen >= fmt_left)
220 prlen = fmt_left - 1;
221 t += prlen;
222 fmt_left -= prlen;
223 } else {
224 if (fmt_left > 1) {
225 *t++ = ch;
226 fmt_left--;
227 }
228 }
229 }
230 *t = '\0';
231
232 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
233 DEC();
234 cnt = p - tbuf;
235
236 /* Output to stderr if requested. */
237 if (LogStat & LOG_PERROR) {
238 struct iovec iov[2];
239
240 iov[0].iov_base = stdp;
241 iov[0].iov_len = cnt - (stdp - tbuf);
242 iov[1].iov_base = "\n";
243 iov[1].iov_len = 1;
244 (void)writev(STDERR_FILENO, iov, 2);
245 }
246
247 /* Get connected, output the message to the local logger. */
248 mutex_lock(&syslog_mutex);
249 if (!connected)
250 openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0);
251 if (send(LogFile, tbuf, cnt, 0) >= 0) {
252 mutex_unlock(&syslog_mutex);
253 return;
254 }
255 mutex_unlock(&syslog_mutex);
256
257 /*
258 * Output the message to the console; don't worry about blocking,
259 * if console blocks everything will. Make sure the error reported
260 * is the one from the syslogd failure.
261 */
262 if (LogStat & LOG_CONS &&
263 (fd = open(_PATH_CONSOLE, O_WRONLY, 0)) >= 0) {
264 struct iovec iov[2];
265
266 p = strchr(tbuf, '>') + 1;
267 iov[0].iov_base = p;
268 iov[0].iov_len = cnt - (p - tbuf);
269 iov[1].iov_base = "\r\n";
270 iov[1].iov_len = 2;
271 (void)writev(fd, iov, 2);
272 (void)close(fd);
273 }
274 }
275
276 static struct sockaddr_un SyslogAddr; /* AF_LOCAL address of local logger */
277
278 static void
279 openlog_unlocked(ident, logstat, logfac)
280 const char *ident;
281 int logstat, logfac;
282 {
283
284 if (ident != NULL)
285 LogTag = ident;
286 LogStat = logstat;
287 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
288 LogFacility = logfac;
289
290 if (LogFile == -1) {
291 SyslogAddr.sun_family = AF_LOCAL;
292 (void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
293 sizeof(SyslogAddr.sun_path));
294 if (LogStat & LOG_NDELAY) {
295 if ((LogFile = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1)
296 return;
297 (void)fcntl(LogFile, F_SETFD, 1);
298 }
299 }
300 if (LogFile != -1 && !connected) {
301 if (connect(LogFile, (struct sockaddr *)(void *)&SyslogAddr,
302 SUN_LEN(&SyslogAddr)) == -1) {
303 (void)close(LogFile);
304 LogFile = -1;
305 } else
306 connected = 1;
307 }
308 }
309
310 void
311 openlog(ident, logstat, logfac)
312 const char *ident;
313 int logstat, logfac;
314 {
315
316 mutex_lock(&syslog_mutex);
317 openlog_unlocked(ident, logstat, logfac);
318 mutex_unlock(&syslog_mutex);
319 }
320
321 static void
322 closelog_unlocked()
323 {
324 (void)close(LogFile);
325 LogFile = -1;
326 connected = 0;
327 }
328
329 void
330 closelog()
331 {
332
333 mutex_lock(&syslog_mutex);
334 closelog_unlocked();
335 mutex_unlock(&syslog_mutex);
336 }
337
338 /* setlogmask -- set the log mask level */
339 int
340 setlogmask(pmask)
341 int pmask;
342 {
343 int omask;
344
345 omask = LogMask;
346 if (pmask != 0)
347 LogMask = pmask;
348 return (omask);
349 }
350