1 /* $OpenBSD: syslog.c,v 1.28 2005/08/08 08:05:34 espie Exp $ */
2 /*
3 * Copyright (c) 1983, 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/uio.h>
34 #include <syslog.h>
35 #include <sys/un.h>
36 #include <netdb.h>
37
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <paths.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include <stdarg.h>
46
47 static struct syslog_data sdata = SYSLOG_DATA_INIT;
48
49 extern char *__progname; /* Program name, from crt0. */
50
51 static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */
52 static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */
53
54 /*
55 * syslog, vsyslog --
56 * print message on log file; output is intended for syslogd(8).
57 */
58 void
syslog(int pri,const char * fmt,...)59 syslog(int pri, const char *fmt, ...)
60 {
61 va_list ap;
62
63 va_start(ap, fmt);
64 vsyslog(pri, fmt, ap);
65 va_end(ap);
66 }
67
68 void
vsyslog(int pri,const char * fmt,va_list ap)69 vsyslog(int pri, const char *fmt, va_list ap)
70 {
71 vsyslog_r(pri, &sdata, fmt, ap);
72 }
73
74 void
openlog(const char * ident,int logstat,int logfac)75 openlog(const char *ident, int logstat, int logfac)
76 {
77 openlog_r(ident, logstat, logfac, &sdata);
78 }
79
80 void
closelog(void)81 closelog(void)
82 {
83 closelog_r(&sdata);
84 }
85
86 /* setlogmask -- set the log mask level */
87 int
setlogmask(int pmask)88 setlogmask(int pmask)
89 {
90 return setlogmask_r(pmask, &sdata);
91 }
92
93 /* Reentrant version of syslog, i.e. syslog_r() */
94
95 void
syslog_r(int pri,struct syslog_data * data,const char * fmt,...)96 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
97 {
98 va_list ap;
99
100 va_start(ap, fmt);
101 vsyslog_r(pri, data, fmt, ap);
102 va_end(ap);
103 }
104
105 void
vsyslog_r(int pri,struct syslog_data * data,const char * fmt,va_list ap)106 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
107 {
108 int cnt;
109 char ch, *p, *t;
110 time_t now;
111 int fd, saved_errno, error;
112 #define TBUF_LEN 2048
113 #define FMT_LEN 1024
114 char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
115 int tbuf_left, fmt_left, prlen;
116
117 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
118 /* Check for invalid bits. */
119 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
120 if (data == &sdata) {
121 syslog(INTERNALLOG,
122 "syslog: unknown facility/priority: %x", pri);
123 } else {
124 syslog_r(INTERNALLOG, data,
125 "syslog_r: unknown facility/priority: %x", pri);
126 }
127 pri &= LOG_PRIMASK|LOG_FACMASK;
128 }
129
130 /* Check priority against setlogmask values. */
131 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
132 return;
133
134 saved_errno = errno;
135
136 /* Set default facility if none specified. */
137 if ((pri & LOG_FACMASK) == 0)
138 pri |= data->log_fac;
139
140 /* If we have been called through syslog(), no need for reentrancy. */
141 if (data == &sdata)
142 (void)time(&now);
143
144 p = tbuf;
145 tbuf_left = TBUF_LEN;
146
147 #define DEC() \
148 do { \
149 if (prlen < 0) \
150 prlen = 0; \
151 if (prlen >= tbuf_left) \
152 prlen = tbuf_left - 1; \
153 p += prlen; \
154 tbuf_left -= prlen; \
155 } while (0)
156
157 prlen = snprintf(p, tbuf_left, "<%d>", pri);
158 DEC();
159
160 /*
161 * syslogd will expand time automagically for reentrant case, and
162 * for normal case, just do like before
163 */
164 if (data == &sdata) {
165 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
166 DEC();
167 }
168
169 if (data->log_stat & LOG_PERROR)
170 stdp = p;
171 if (data->log_tag == NULL)
172 data->log_tag = __progname;
173 if (data->log_tag != NULL) {
174 prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
175 DEC();
176 }
177 if (data->log_stat & LOG_PID) {
178 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
179 DEC();
180 }
181 if (data->log_tag != NULL) {
182 if (tbuf_left > 1) {
183 *p++ = ':';
184 tbuf_left--;
185 }
186 if (tbuf_left > 1) {
187 *p++ = ' ';
188 tbuf_left--;
189 }
190 }
191
192 /* strerror() is not reentrant */
193
194 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
195 if (ch == '%' && fmt[1] == 'm') {
196 ++fmt;
197 if (data == &sdata) {
198 prlen = snprintf(t, fmt_left, "%s",
199 strerror(saved_errno));
200 } else {
201 prlen = snprintf(t, fmt_left, "Error %d",
202 saved_errno);
203 }
204 if (prlen < 0)
205 prlen = 0;
206 if (prlen >= fmt_left)
207 prlen = fmt_left - 1;
208 t += prlen;
209 fmt_left -= prlen;
210 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
211 *t++ = '%';
212 *t++ = '%';
213 fmt++;
214 fmt_left -= 2;
215 } else {
216 if (fmt_left > 1) {
217 *t++ = ch;
218 fmt_left--;
219 }
220 }
221 }
222 *t = '\0';
223
224 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
225 DEC();
226 cnt = p - tbuf;
227
228 /* Output to stderr if requested. */
229 if (data->log_stat & LOG_PERROR) {
230 struct iovec iov[2];
231
232 iov[0].iov_base = stdp;
233 iov[0].iov_len = cnt - (stdp - tbuf);
234 iov[1].iov_base = "\n";
235 iov[1].iov_len = 1;
236 (void)writev(STDERR_FILENO, iov, 2);
237 }
238
239 /* Get connected, output the message to the local logger. */
240 if (!data->opened)
241 openlog_r(data->log_tag, data->log_stat, 0, data);
242 connectlog_r(data);
243
244 /*
245 * If the send() failed, there are two likely scenarios:
246 * 1) syslogd was restarted
247 * 2) /dev/log is out of socket buffer space
248 * We attempt to reconnect to /dev/log to take care of
249 * case #1 and keep send()ing data to cover case #2
250 * to give syslogd a chance to empty its socket buffer.
251 */
252 if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
253 if (errno != ENOBUFS) {
254 disconnectlog_r(data);
255 connectlog_r(data);
256 }
257 do {
258 usleep(1);
259 if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
260 break;
261 } while (errno == ENOBUFS);
262 }
263
264 /*
265 * Output the message to the console; try not to block
266 * as a blocking console should not stop other processes.
267 * Make sure the error reported is the one from the syslogd failure.
268 */
269 if (error == -1 && (data->log_stat & LOG_CONS) &&
270 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
271 struct iovec iov[2];
272
273 p = strchr(tbuf, '>') + 1;
274 iov[0].iov_base = p;
275 iov[0].iov_len = cnt - (p - tbuf);
276 iov[1].iov_base = "\r\n";
277 iov[1].iov_len = 2;
278 (void)writev(fd, iov, 2);
279 (void)close(fd);
280 }
281
282 if (data != &sdata)
283 closelog_r(data);
284 }
285
286 static void
disconnectlog_r(struct syslog_data * data)287 disconnectlog_r(struct syslog_data *data)
288 {
289 /*
290 * If the user closed the FD and opened another in the same slot,
291 * that's their problem. They should close it before calling on
292 * system services.
293 */
294 if (data->log_file != -1) {
295 close(data->log_file);
296 data->log_file = -1;
297 }
298 data->connected = 0; /* retry connect */
299 }
300
301 static void
connectlog_r(struct syslog_data * data)302 connectlog_r(struct syslog_data *data)
303 {
304 union {
305 struct sockaddr syslogAddr;
306 struct sockaddr_un syslogAddrUn;
307 } u;
308
309 #define SyslogAddr u.syslogAddrUn
310
311 if (data->log_file == -1) {
312 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
313 return;
314 (void)fcntl(data->log_file, F_SETFD, 1);
315 }
316 if (data->log_file != -1 && !data->connected) {
317 memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
318 #if 0
319 /* BIONIC: no sun_len field to fill on Linux */
320 SyslogAddr.sun_len = sizeof(SyslogAddr);
321 #endif
322 SyslogAddr.sun_family = AF_UNIX;
323 strlcpy(SyslogAddr.sun_path, _PATH_LOG,
324 sizeof(SyslogAddr.sun_path));
325 if (connect(data->log_file, &u.syslogAddr,
326 sizeof(SyslogAddr)) == -1) {
327 (void)close(data->log_file);
328 data->log_file = -1;
329 } else
330 data->connected = 1;
331 }
332 }
333
334 void
openlog_r(const char * ident,int logstat,int logfac,struct syslog_data * data)335 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
336 {
337 if (ident != NULL)
338 data->log_tag = ident;
339 data->log_stat = logstat;
340 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
341 data->log_fac = logfac;
342
343 if (data->log_stat & LOG_NDELAY) /* open immediately */
344 connectlog_r(data);
345
346 data->opened = 1; /* ident and facility has been set */
347 }
348
349 void
closelog_r(struct syslog_data * data)350 closelog_r(struct syslog_data *data)
351 {
352 (void)close(data->log_file);
353 data->log_file = -1;
354 data->connected = 0;
355 data->log_tag = NULL;
356 }
357
358 /* setlogmask -- set the log mask level */
359 int
setlogmask_r(int pmask,struct syslog_data * data)360 setlogmask_r(int pmask, struct syslog_data *data)
361 {
362 int omask;
363
364 omask = data->log_mask;
365 if (pmask != 0)
366 data->log_mask = pmask;
367 return (omask);
368 }
369