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/syslog.h>
34 #include <sys/uio.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 __RCSID("$MirOS: src/lib/libc/gen/syslog.c,v 1.4 2006/06/02 02:29:50 tg Exp $");
48
49 static struct syslog_data sdata = SYSLOG_DATA_INIT;
50
51 extern char *__progname; /* Program name, from crt0. */
52
53 static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */
54 static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */
55
56 /*
57 * syslog, vsyslog --
58 * print message on log file; output is intended for syslogd(8).
59 */
60 void
syslog(int pri,const char * fmt,...)61 syslog(int pri, const char *fmt, ...)
62 {
63 va_list ap;
64
65 va_start(ap, fmt);
66 vsyslog(pri, fmt, ap);
67 va_end(ap);
68 }
69
70 void
vsyslog(int pri,const char * fmt,va_list ap)71 vsyslog(int pri, const char *fmt, va_list ap)
72 {
73 vsyslog_r(pri, &sdata, fmt, ap);
74 }
75
76 void
openlog(const char * ident,int logstat,int logfac)77 openlog(const char *ident, int logstat, int logfac)
78 {
79 openlog_r(ident, logstat, logfac, &sdata);
80 }
81
82 void
closelog(void)83 closelog(void)
84 {
85 closelog_r(&sdata);
86 }
87
88 /* setlogmask -- set the log mask level */
89 int
setlogmask(int pmask)90 setlogmask(int pmask)
91 {
92 return setlogmask_r(pmask, &sdata);
93 }
94
95 /* Reentrant version of syslog, i.e. syslog_r() */
96
97 void
syslog_r(int pri,struct syslog_data * data,const char * fmt,...)98 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
99 {
100 va_list ap;
101
102 va_start(ap, fmt);
103 vsyslog_r(pri, data, fmt, ap);
104 va_end(ap);
105 }
106
107 void
vsyslog_r(int pri,struct syslog_data * data,const char * fmt,va_list ap)108 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
109 {
110 int cnt;
111 char ch, *p, *t;
112 time_t now;
113 int fd, saved_errno, error;
114 #define TBUF_LEN 2048
115 #define FMT_LEN 1024
116 char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
117 int tbuf_left, fmt_left, prlen;
118
119 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
120 /* Check for invalid bits. */
121 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
122 if (data == &sdata) {
123 syslog(INTERNALLOG,
124 "syslog: unknown facility/priority: %x", pri);
125 } else {
126 syslog_r(INTERNALLOG, data,
127 "syslog_r: unknown facility/priority: %x", pri);
128 }
129 pri &= LOG_PRIMASK|LOG_FACMASK;
130 }
131
132 /* Check priority against setlogmask values. */
133 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
134 return;
135
136 saved_errno = errno;
137
138 /* Set default facility if none specified. */
139 if ((pri & LOG_FACMASK) == 0)
140 pri |= data->log_fac;
141
142 /* If we have been called through syslog(), no need for reentrancy. */
143 if (data == &sdata)
144 (void)time(&now);
145
146 p = tbuf;
147 tbuf_left = TBUF_LEN;
148
149 #define DEC() \
150 do { \
151 if (prlen < 0) \
152 prlen = 0; \
153 if (prlen >= tbuf_left) \
154 prlen = tbuf_left - 1; \
155 p += prlen; \
156 tbuf_left -= prlen; \
157 } while (0)
158
159 prlen = snprintf(p, tbuf_left, "<%d>", pri);
160 DEC();
161
162 /*
163 * syslogd will expand time automagically for reentrant case, and
164 * for normal case, just do like before
165 */
166 if (data == &sdata) {
167 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
168 DEC();
169 }
170
171 if (data->log_stat & LOG_PERROR)
172 stdp = p;
173 if (data->log_tag == NULL)
174 data->log_tag = __progname;
175 if (data->log_tag != NULL) {
176 prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
177 DEC();
178 }
179 if (data->log_stat & LOG_PID) {
180 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
181 DEC();
182 }
183 if (data->log_tag != NULL) {
184 if (tbuf_left > 1) {
185 *p++ = ':';
186 tbuf_left--;
187 }
188 if (tbuf_left > 1) {
189 *p++ = ' ';
190 tbuf_left--;
191 }
192 }
193
194 /* strerror() is not reentrant */
195
196 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
197 if (ch == '%' && fmt[1] == 'm') {
198 ++fmt;
199 if (data == &sdata) {
200 prlen = snprintf(t, fmt_left, "%s",
201 strerror(saved_errno));
202 } else {
203 prlen = snprintf(t, fmt_left, "Error %d",
204 saved_errno);
205 }
206 if (prlen < 0)
207 prlen = 0;
208 if (prlen >= fmt_left)
209 prlen = fmt_left - 1;
210 t += prlen;
211 fmt_left -= prlen;
212 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
213 *t++ = '%';
214 *t++ = '%';
215 fmt++;
216 fmt_left -= 2;
217 } else {
218 if (fmt_left > 1) {
219 *t++ = ch;
220 fmt_left--;
221 }
222 }
223 }
224 *t = '\0';
225
226 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
227 DEC();
228 cnt = p - tbuf;
229
230 /* Output to stderr if requested. */
231 if (data->log_stat & LOG_PERROR) {
232 struct iovec iov[2];
233
234 iov[0].iov_base = stdp;
235 iov[0].iov_len = cnt - (stdp - tbuf);
236 iov[1].iov_base = (void *)"\n";
237 iov[1].iov_len = 1;
238 (void)writev(STDERR_FILENO, iov, 2);
239 }
240
241 /* Get connected, output the message to the local logger. */
242 if (!data->opened)
243 openlog_r(data->log_tag, data->log_stat, 0, data);
244 connectlog_r(data);
245
246 /*
247 * If the send() failed, there are two likely scenarios:
248 * 1) syslogd was restarted
249 * 2) /dev/log is out of socket buffer space
250 * We attempt to reconnect to /dev/log to take care of
251 * case #1 and keep send()ing data to cover case #2
252 * to give syslogd a chance to empty its socket buffer.
253 */
254 if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
255 if (errno != ENOBUFS) {
256 disconnectlog_r(data);
257 connectlog_r(data);
258 }
259 do {
260 usleep(1);
261 if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
262 break;
263 } while (errno == ENOBUFS);
264 }
265
266 /*
267 * Output the message to the console; try not to block
268 * as a blocking console should not stop other processes.
269 * Make sure the error reported is the one from the syslogd failure.
270 */
271 if (error == -1 && (data->log_stat & LOG_CONS) &&
272 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
273 struct iovec iov[2];
274
275 p = strchr(tbuf, '>') + 1;
276 iov[0].iov_base = p;
277 iov[0].iov_len = cnt - (p - tbuf);
278 iov[1].iov_base = (void *)"\r\n";
279 iov[1].iov_len = 2;
280 (void)writev(fd, iov, 2);
281 (void)close(fd);
282 }
283
284 if (data != &sdata)
285 closelog_r(data);
286 }
287
288 static void
disconnectlog_r(struct syslog_data * data)289 disconnectlog_r(struct syslog_data *data)
290 {
291 /*
292 * If the user closed the FD and opened another in the same slot,
293 * that's their problem. They should close it before calling on
294 * system services.
295 */
296 if (data->log_file != -1) {
297 close(data->log_file);
298 data->log_file = -1;
299 }
300 data->connected = 0; /* retry connect */
301 }
302
303 static void
connectlog_r(struct syslog_data * data)304 connectlog_r(struct syslog_data *data)
305 {
306 struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */
307
308 if (data->log_file == -1) {
309 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
310 return;
311 (void)fcntl(data->log_file, F_SETFD, 1);
312 }
313 if (data->log_file != -1 && !data->connected) {
314 memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
315 SyslogAddr.sun_len = sizeof(SyslogAddr);
316 SyslogAddr.sun_family = AF_UNIX;
317 strlcpy(SyslogAddr.sun_path, _PATH_LOG,
318 sizeof(SyslogAddr.sun_path));
319 if (connect(data->log_file, (struct sockaddr *)&SyslogAddr,
320 sizeof(SyslogAddr)) == -1) {
321 (void)close(data->log_file);
322 data->log_file = -1;
323 } else
324 data->connected = 1;
325 }
326 }
327
328 void
openlog_r(const char * ident,int logstat,int logfac,struct syslog_data * data)329 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
330 {
331 if (ident != NULL)
332 data->log_tag = ident;
333 data->log_stat = logstat;
334 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
335 data->log_fac = logfac;
336
337 if (data->log_stat & LOG_NDELAY) /* open immediately */
338 connectlog_r(data);
339
340 data->opened = 1; /* ident and facility has been set */
341 }
342
343 void
closelog_r(struct syslog_data * data)344 closelog_r(struct syslog_data *data)
345 {
346 (void)close(data->log_file);
347 data->log_file = -1;
348 data->connected = 0;
349 data->log_tag = NULL;
350 }
351
352 /* setlogmask -- set the log mask level */
353 int
setlogmask_r(int pmask,struct syslog_data * data)354 setlogmask_r(int pmask, struct syslog_data *data)
355 {
356 int omask;
357
358 omask = data->log_mask;
359 if (pmask != 0)
360 data->log_mask = pmask;
361 return (omask);
362 }
363