xref: /freebsd/lib/libc/gen/syslog.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 /*
36 static char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
37 */
38 static const char rcsid[] =
39   "$Id: syslog.c,v 1.16 1998/03/06 03:10:49 brian Exp $";
40 #endif /* LIBC_SCCS and not lint */
41 
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 <stdio.h>
53 #include <string.h>
54 #include <time.h>
55 #include <unistd.h>
56 
57 #if __STDC__
58 #include <stdarg.h>
59 #else
60 #include <varargs.h>
61 #endif
62 
63 static int	LogFile = -1;		/* fd for log */
64 static int	connected;		/* have done connect */
65 static int	opened;			/* have done openlog() */
66 static int	LogStat = 0;		/* status bits, set by openlog() */
67 static const char *LogTag = NULL;	/* string to tag the entry with */
68 static int	LogFacility = LOG_USER;	/* default facility code */
69 static int	LogMask = 0xff;		/* mask of priorities to be logged */
70 extern char	*__progname;		/* Program name, from crt0. */
71 
72 static void	disconnectlog __P((void)); /* disconnect from syslogd */
73 static void	connectlog __P((void));	/* (re)connect to syslogd */
74 
75 /*
76  * Format of the magic cookie passed through the stdio hook
77  */
78 struct bufcookie {
79 	char	*base;	/* start of buffer */
80 	int	left;
81 };
82 
83 /*
84  * stdio write hook for writing to a static string buffer
85  * XXX: Maybe one day, dynamically allocate it so that the line length
86  *      is `unlimited'.
87  */
88 static
89 int writehook(cookie, buf, len)
90 	void	*cookie;	/* really [struct bufcookie *] */
91 	char	*buf;		/* characters to copy */
92 	int	len;		/* length to copy */
93 {
94 	struct bufcookie *h;	/* private `handle' */
95 
96 	h = (struct bufcookie *)cookie;
97 	if (len > h->left) {
98 		/* clip in case of wraparound */
99 		len = h->left;
100 	}
101 	if (len > 0) {
102 		(void)memcpy(h->base, buf, len); /* `write' it. */
103 		h->base += len;
104 		h->left -= len;
105 	}
106 	return 0;
107 }
108 
109 /*
110  * syslog, vsyslog --
111  *	print message on log file; output is intended for syslogd(8).
112  */
113 void
114 #if __STDC__
115 syslog(int pri, const char *fmt, ...)
116 #else
117 syslog(pri, fmt, va_alist)
118 	int pri;
119 	char *fmt;
120 	va_dcl
121 #endif
122 {
123 	va_list ap;
124 
125 #if __STDC__
126 	va_start(ap, fmt);
127 #else
128 	va_start(ap);
129 #endif
130 	vsyslog(pri, fmt, ap);
131 	va_end(ap);
132 }
133 
134 void
135 vsyslog(pri, fmt, ap)
136 	int pri;
137 	register const char *fmt;
138 	va_list ap;
139 {
140 	register int cnt;
141 	register char ch, *p;
142 	time_t now;
143 	int fd, saved_errno;
144 	char *stdp, tbuf[2048], fmt_cpy[1024];
145 	FILE *fp, *fmt_fp;
146 	struct bufcookie tbuf_cookie;
147 	struct bufcookie fmt_cookie;
148 
149 #define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
150 	/* Check for invalid bits. */
151 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
152 		syslog(INTERNALLOG,
153 		    "syslog: unknown facility/priority: %x", pri);
154 		pri &= LOG_PRIMASK|LOG_FACMASK;
155 	}
156 
157 	/* Check priority against setlogmask values. */
158 	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask))
159 		return;
160 
161 	saved_errno = errno;
162 
163 	/* Set default facility if none specified. */
164 	if ((pri & LOG_FACMASK) == 0)
165 		pri |= LogFacility;
166 
167 	/* Create the primary stdio hook */
168 	tbuf_cookie.base = tbuf;
169 	tbuf_cookie.left = sizeof(tbuf);
170 	fp = fwopen(&tbuf_cookie, writehook);
171 	if (fp == NULL)
172 		return;
173 
174 	/* Build the message. */
175 	(void)time(&now);
176 	(void)fprintf(fp, "<%d>", pri);
177 	(void)fprintf(fp, "%.15s ", ctime(&now) + 4);
178 	if (LogStat & LOG_PERROR) {
179 		/* Transfer to string buffer */
180 		(void)fflush(fp);
181 		stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left);
182 	}
183 	if (LogTag == NULL)
184 		LogTag = __progname;
185 	if (LogTag != NULL)
186 		(void)fprintf(fp, "%s", LogTag);
187 	if (LogStat & LOG_PID)
188 		(void)fprintf(fp, "[%d]", getpid());
189 	if (LogTag != NULL) {
190 		(void)fprintf(fp, ": ");
191 	}
192 
193 	/* Check to see if we can skip expanding the %m */
194 	if (strstr(fmt, "%m")) {
195 
196 		/* Create the second stdio hook */
197 		fmt_cookie.base = fmt_cpy;
198 		fmt_cookie.left = sizeof(fmt_cpy) - 1;
199 		fmt_fp = fwopen(&fmt_cookie, writehook);
200 		if (fmt_fp == NULL) {
201 			fclose(fp);
202 			return;
203 		}
204 
205 		/* Substitute error message for %m. */
206 		for ( ; (ch = *fmt); ++fmt)
207 			if (ch == '%' && fmt[1] == 'm') {
208 				++fmt;
209 				fputs(strerror(saved_errno), fmt_fp);
210 			} else
211 				fputc(ch, fmt_fp);
212 
213 		/* Null terminate if room */
214 		fputc(0, fmt_fp);
215 		fclose(fmt_fp);
216 
217 		/* Guarantee null termination */
218 		fmt_cpy[sizeof(fmt_cpy) - 1] = '\0';
219 
220 		fmt = fmt_cpy;
221 	}
222 
223 	(void)vfprintf(fp, fmt, ap);
224 	(void)fclose(fp);
225 
226 	cnt = sizeof(tbuf) - tbuf_cookie.left;
227 
228 	/* Output to stderr if requested. */
229 	if (LogStat & LOG_PERROR) {
230 		struct iovec iov[2];
231 		register struct iovec *v = iov;
232 
233 		v->iov_base = stdp;
234 		v->iov_len = cnt - (stdp - tbuf);
235 		++v;
236 		v->iov_base = "\n";
237 		v->iov_len = 1;
238 		(void)writev(STDERR_FILENO, iov, 2);
239 	}
240 
241 	/* Get connected, output the message to the local logger. */
242 	if (!opened)
243 		openlog(LogTag, LogStat | LOG_NDELAY, 0);
244 	connectlog();
245 	if (send(LogFile, tbuf, cnt, 0) >= 0)
246 		return;
247 
248 	/*
249 	 * If the send() failed, the odds are syslogd was restarted.
250 	 * Make one (only) attempt to reconnect to /dev/log.
251 	 */
252 	disconnectlog();
253 	connectlog();
254 	if (send(LogFile, tbuf, cnt, 0) >= 0)
255 		return;
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 		register struct iovec *v = iov;
266 
267 		p = strchr(tbuf, '>') + 1;
268 		v->iov_base = p;
269 		v->iov_len = cnt - (p - tbuf);
270 		++v;
271 		v->iov_base = "\r\n";
272 		v->iov_len = 2;
273 		(void)writev(fd, iov, 2);
274 		(void)close(fd);
275 	}
276 }
277 static void
278 disconnectlog()
279 {
280 	/*
281 	 * If the user closed the FD and opened another in the same slot,
282 	 * that's their problem.  They should close it before calling on
283 	 * system services.
284 	 */
285 	if (LogFile != -1) {
286 		close(LogFile);
287 		LogFile = -1;
288 	}
289 	connected = 0;			/* retry connect */
290 }
291 
292 static void
293 connectlog()
294 {
295 	struct sockaddr_un SyslogAddr;	/* AF_UNIX address of local logger */
296 
297 	if (LogFile == -1) {
298 		if ((LogFile = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
299 			return;
300 		(void)fcntl(LogFile, F_SETFD, 1);
301 	}
302 	if (LogFile != -1 && !connected) {
303 		SyslogAddr.sun_len = sizeof(SyslogAddr);
304 		SyslogAddr.sun_family = AF_UNIX;
305 		(void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
306 		    sizeof SyslogAddr.sun_path);
307 		connected = connect(LogFile, (struct sockaddr *)&SyslogAddr,
308 			sizeof(SyslogAddr)) != -1;
309 
310 		if (!connected) {
311 			/*
312 			 * Try the old "/dev/log" path, for backward
313 			 * compatibility.
314 			 */
315 			(void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG,
316 			    sizeof SyslogAddr.sun_path);
317 			connected = connect(LogFile,
318 				(struct sockaddr *)&SyslogAddr,
319 				sizeof(SyslogAddr)) != -1;
320 		}
321 
322 		if (!connected) {
323 			(void)close(LogFile);
324 			LogFile = -1;
325 		}
326 	}
327 }
328 
329 void
330 openlog(ident, logstat, logfac)
331 	const char *ident;
332 	int logstat, logfac;
333 {
334 	if (ident != NULL)
335 		LogTag = ident;
336 	LogStat = logstat;
337 	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
338 		LogFacility = logfac;
339 
340 	if (LogStat & LOG_NDELAY)	/* open immediately */
341 		connectlog();
342 
343 	opened = 1;	/* ident and facility has been set */
344 }
345 
346 void
347 closelog()
348 {
349 	(void)close(LogFile);
350 	LogFile = -1;
351 	connected = 0;
352 }
353 
354 /* setlogmask -- set the log mask level */
355 int
356 setlogmask(pmask)
357 	int pmask;
358 {
359 	int omask;
360 
361 	omask = LogMask;
362 	if (pmask != 0)
363 		LogMask = pmask;
364 	return (omask);
365 }
366