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