xref: /netbsd-src/lib/libc/gen/xsyslog.c (revision 454e43c47daccd804fc101d772393c6df7049c32)
1*454e43c4Schristos /*	$NetBSD: xsyslog.c,v 1.7 2020/03/02 15:30:25 christos Exp $	*/
2820e172cSchristos 
3820e172cSchristos /*
4820e172cSchristos  * Copyright (c) 1983, 1988, 1993
5820e172cSchristos  *	The Regents of the University of California.  All rights reserved.
6820e172cSchristos  *
7820e172cSchristos  * Redistribution and use in source and binary forms, with or without
8820e172cSchristos  * modification, are permitted provided that the following conditions
9820e172cSchristos  * are met:
10820e172cSchristos  * 1. Redistributions of source code must retain the above copyright
11820e172cSchristos  *    notice, this list of conditions and the following disclaimer.
12820e172cSchristos  * 2. Redistributions in binary form must reproduce the above copyright
13820e172cSchristos  *    notice, this list of conditions and the following disclaimer in the
14820e172cSchristos  *    documentation and/or other materials provided with the distribution.
15820e172cSchristos  * 3. Neither the name of the University nor the names of its contributors
16820e172cSchristos  *    may be used to endorse or promote products derived from this software
17820e172cSchristos  *    without specific prior written permission.
18820e172cSchristos  *
19820e172cSchristos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20820e172cSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21820e172cSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22820e172cSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23820e172cSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24820e172cSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25820e172cSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26820e172cSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27820e172cSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28820e172cSchristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29820e172cSchristos  * SUCH DAMAGE.
30820e172cSchristos  */
31820e172cSchristos 
32820e172cSchristos #include <sys/cdefs.h>
33820e172cSchristos #if defined(LIBC_SCCS) && !defined(lint)
34820e172cSchristos #if 0
35820e172cSchristos static char sccsid[] = "@(#)syslog.c	8.5 (Berkeley) 4/29/95";
36820e172cSchristos #else
37*454e43c4Schristos __RCSID("$NetBSD: xsyslog.c,v 1.7 2020/03/02 15:30:25 christos Exp $");
38820e172cSchristos #endif
39820e172cSchristos #endif /* LIBC_SCCS and not lint */
40820e172cSchristos 
41820e172cSchristos #include "namespace.h"
42820e172cSchristos #include <sys/types.h>
43820e172cSchristos #include <sys/param.h>
44820e172cSchristos #include <sys/socket.h>
45820e172cSchristos #include <sys/syslog.h>
46820e172cSchristos #include <sys/uio.h>
47820e172cSchristos #include <sys/un.h>
48820e172cSchristos 
49820e172cSchristos #include <errno.h>
50820e172cSchristos #include <stdio.h>
51820e172cSchristos #include <stdarg.h>
52820e172cSchristos #include <string.h>
53820e172cSchristos #include <fcntl.h>
54820e172cSchristos #include <unistd.h>
55820e172cSchristos #include <stdlib.h>
56820e172cSchristos #include <paths.h>
57820e172cSchristos #include "syslog_private.h"
58820e172cSchristos #include "reentrant.h"
59820e172cSchristos #include "extern.h"
60820e172cSchristos 
619cc0be31Schristos static void
disconnectlog_r(struct syslog_data * data)629cc0be31Schristos disconnectlog_r(struct syslog_data *data)
63820e172cSchristos {
649cc0be31Schristos 	/*
659cc0be31Schristos 	 * If the user closed the FD and opened another in the same slot,
669cc0be31Schristos 	 * that's their problem.  They should close it before calling on
679cc0be31Schristos 	 * system services.
689cc0be31Schristos 	 */
699cc0be31Schristos 	if (data->log_file != -1) {
709cc0be31Schristos 		(void)close(data->log_file);
719cc0be31Schristos 		data->log_file = -1;
729cc0be31Schristos 	}
739cc0be31Schristos 	data->log_connected = 0;		/* retry connect */
749cc0be31Schristos }
759cc0be31Schristos 
769cc0be31Schristos static void
connectlog_r(struct syslog_data * data)779cc0be31Schristos connectlog_r(struct syslog_data *data)
789cc0be31Schristos {
799cc0be31Schristos 	/* AF_UNIX address of local logger */
809cc0be31Schristos 	static const struct sockaddr_un sun = {
819cc0be31Schristos 		.sun_family = AF_LOCAL,
829cc0be31Schristos 		.sun_len = sizeof(sun),
839cc0be31Schristos 		.sun_path = _PATH_LOG,
849cc0be31Schristos 	};
859cc0be31Schristos 
869cc0be31Schristos 	if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
879cc0be31Schristos 		if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC,
889cc0be31Schristos 		    0)) == -1)
899cc0be31Schristos 			return;
909cc0be31Schristos 		data->log_connected = 0;
919cc0be31Schristos 	}
929cc0be31Schristos 	if (!data->log_connected) {
939cc0be31Schristos 		if (connect(data->log_file,
949cc0be31Schristos 		    (const struct sockaddr *)(const void *)&sun,
959cc0be31Schristos 		    (socklen_t)sizeof(sun)) == -1) {
969cc0be31Schristos 			(void)close(data->log_file);
979cc0be31Schristos 			data->log_file = -1;
989cc0be31Schristos 		} else
999cc0be31Schristos 			data->log_connected = 1;
1009cc0be31Schristos 	}
101820e172cSchristos }
102820e172cSchristos 
103820e172cSchristos void
_openlog_unlocked_r(const char * ident,int logstat,int logfac,struct syslog_data * data)1049cc0be31Schristos _openlog_unlocked_r(const char *ident, int logstat, int logfac,
1059cc0be31Schristos     struct syslog_data *data)
106820e172cSchristos {
1079cc0be31Schristos 	if (ident != NULL)
1089cc0be31Schristos 		data->log_tag = ident;
1099cc0be31Schristos 	data->log_stat = logstat;
1109cc0be31Schristos 	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
1119cc0be31Schristos 		data->log_fac = logfac;
1129cc0be31Schristos 
1139cc0be31Schristos 	if (data->log_stat & LOG_NDELAY)	/* open immediately */
1149cc0be31Schristos 		connectlog_r(data);
1159cc0be31Schristos 
1169cc0be31Schristos 	data->log_opened = 1;
117820e172cSchristos }
118820e172cSchristos 
1199cc0be31Schristos void
_closelog_unlocked_r(struct syslog_data * data)1209cc0be31Schristos _closelog_unlocked_r(struct syslog_data *data)
121820e172cSchristos {
1229cc0be31Schristos 	(void)close(data->log_file);
1239cc0be31Schristos 	data->log_file = -1;
1249cc0be31Schristos 	data->log_connected = 0;
125820e172cSchristos }
126820e172cSchristos 
127*454e43c4Schristos static __sysloglike(6, 7) void
_xsyslogp_r(int pri,struct syslog_fun * fun,struct syslog_data * data,const char * msgid,const char * sdfmt,const char * msgfmt,...)128820e172cSchristos _xsyslogp_r(int pri, struct syslog_fun *fun,
129820e172cSchristos     struct syslog_data *data, const char *msgid,
130820e172cSchristos     const char *sdfmt, const char *msgfmt, ...)
131820e172cSchristos {
132820e172cSchristos 	va_list ap;
133820e172cSchristos 	va_start(ap, msgfmt);
134820e172cSchristos 	_vxsyslogp_r(pri, fun, data, msgid, sdfmt, msgfmt, ap);
135820e172cSchristos 	va_end(ap);
136820e172cSchristos }
137820e172cSchristos 
138820e172cSchristos void
_vxsyslogp_r(int pri,struct syslog_fun * fun,struct syslog_data * data,const char * msgid,const char * sdfmt,const char * msgfmt,va_list ap)139820e172cSchristos _vxsyslogp_r(int pri, struct syslog_fun *fun,
140820e172cSchristos     struct syslog_data *data, const char *msgid,
141820e172cSchristos     const char *sdfmt, const char *msgfmt, va_list ap)
142820e172cSchristos {
143820e172cSchristos 	static const char BRCOSP[] = "]: ";
144820e172cSchristos 	static const char CRLF[] = "\r\n";
145820e172cSchristos 	size_t cnt, prlen, tries;
146820e172cSchristos 	char ch, *p, *t;
147820e172cSchristos 	int fd, saved_errno;
148820e172cSchristos #define TBUF_LEN	2048
149820e172cSchristos #define FMT_LEN		1024
150820e172cSchristos #define MAXTRIES	10
151820e172cSchristos 	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
152820e172cSchristos 	size_t tbuf_left, fmt_left, msgsdlen;
153820e172cSchristos 	char *fmt = fmt_cat;
154820e172cSchristos 	struct iovec iov[7];	/* prog + [ + pid + ]: + fmt + crlf */
155820e172cSchristos 	int opened, iovcnt;
156820e172cSchristos 
157b24bc655Smaya 	iovcnt = opened = 0;
1584b48a30aSkre 
159820e172cSchristos #define INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
160820e172cSchristos 	/* Check for invalid bits. */
161820e172cSchristos 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
162820e172cSchristos 		_xsyslogp_r(INTERNALLOG, &_syslog_ss_fun, data, NULL, NULL,
16374707f6aSchristos 		    "Unknown facility/priority: %#x", pri);
164820e172cSchristos 		pri &= LOG_PRIMASK|LOG_FACMASK;
165820e172cSchristos 	}
166820e172cSchristos 
167820e172cSchristos 	/* Check priority against setlogmask values. */
168820e172cSchristos 	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
169820e172cSchristos 		return;
170820e172cSchristos 
171820e172cSchristos 	saved_errno = errno;
172820e172cSchristos 
173820e172cSchristos 	/* Set default facility if none specified. */
174820e172cSchristos 	if ((pri & LOG_FACMASK) == 0)
175820e172cSchristos 		pri |= data->log_fac;
176820e172cSchristos 
177820e172cSchristos 	/* Build the message. */
178820e172cSchristos 	p = tbuf;
179820e172cSchristos 	tbuf_left = TBUF_LEN;
180820e172cSchristos 
181820e172cSchristos 	prlen = snprintf_ss(p, tbuf_left, "<%d>1 ", pri);
182820e172cSchristos 	DEC();
183820e172cSchristos 
184820e172cSchristos 	prlen = (*fun->timefun)(p, tbuf_left);
185820e172cSchristos 
1869cc0be31Schristos 	(*fun->lock)(data);
187820e172cSchristos 
188820e172cSchristos 	if (data->log_hostname[0] == '\0' && gethostname(data->log_hostname,
189820e172cSchristos 	    sizeof(data->log_hostname)) == -1) {
190820e172cSchristos 		/* can this really happen? */
191820e172cSchristos 		data->log_hostname[0] = '-';
192820e172cSchristos 		data->log_hostname[1] = '\0';
193820e172cSchristos 	}
194820e172cSchristos 
195820e172cSchristos 	DEC();
196820e172cSchristos 	prlen = snprintf_ss(p, tbuf_left, " %s ", data->log_hostname);
197820e172cSchristos 
198820e172cSchristos 	if (data->log_tag == NULL)
199820e172cSchristos 		data->log_tag = getprogname();
200820e172cSchristos 
201820e172cSchristos 	DEC();
202820e172cSchristos 	prlen = snprintf_ss(p, tbuf_left, "%s ",
203820e172cSchristos 	    data->log_tag ? data->log_tag : "-");
204820e172cSchristos 
2059cc0be31Schristos 	(*fun->unlock)(data);
206820e172cSchristos 
207820e172cSchristos 	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
208820e172cSchristos 		iov[iovcnt].iov_base = p;
209820e172cSchristos 		iov[iovcnt].iov_len = prlen - 1;
210820e172cSchristos 		iovcnt++;
211820e172cSchristos 	}
212820e172cSchristos 	DEC();
213820e172cSchristos 
214820e172cSchristos 	if (data->log_stat & LOG_PID) {
215820e172cSchristos 		prlen = snprintf_ss(p, tbuf_left, "%d ", getpid());
216820e172cSchristos 		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
217820e172cSchristos 			iov[iovcnt].iov_base = __UNCONST("[");
218820e172cSchristos 			iov[iovcnt].iov_len = 1;
219820e172cSchristos 			iovcnt++;
220820e172cSchristos 			iov[iovcnt].iov_base = p;
221820e172cSchristos 			iov[iovcnt].iov_len = prlen - 1;
222820e172cSchristos 			iovcnt++;
223820e172cSchristos 			iov[iovcnt].iov_base = __UNCONST(BRCOSP);
224820e172cSchristos 			iov[iovcnt].iov_len = 3;
225820e172cSchristos 			iovcnt++;
226820e172cSchristos 		}
227820e172cSchristos 	} else {
228820e172cSchristos 		prlen = snprintf_ss(p, tbuf_left, "- ");
229820e172cSchristos 		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
230820e172cSchristos 			iov[iovcnt].iov_base = __UNCONST(BRCOSP + 1);
231820e172cSchristos 			iov[iovcnt].iov_len = 2;
232820e172cSchristos 			iovcnt++;
233820e172cSchristos 		}
234820e172cSchristos 	}
235820e172cSchristos 	DEC();
236820e172cSchristos 
237820e172cSchristos 	/*
238820e172cSchristos 	 * concat the format strings, then use one vsnprintf()
239820e172cSchristos 	 */
240820e172cSchristos 	if (msgid != NULL && *msgid != '\0') {
241820e172cSchristos 		strlcat(fmt_cat, msgid, FMT_LEN);
242820e172cSchristos 		strlcat(fmt_cat, " ", FMT_LEN);
243820e172cSchristos 	} else
244820e172cSchristos 		strlcat(fmt_cat, "- ", FMT_LEN);
245820e172cSchristos 
246820e172cSchristos 	if (sdfmt != NULL && *sdfmt != '\0') {
247820e172cSchristos 		strlcat(fmt_cat, sdfmt, FMT_LEN);
248820e172cSchristos 	} else
249820e172cSchristos 		strlcat(fmt_cat, "-", FMT_LEN);
250820e172cSchristos 
251820e172cSchristos 	if (data->log_stat & (LOG_PERROR|LOG_CONS))
252820e172cSchristos 		msgsdlen = strlen(fmt_cat) + 1;
253820e172cSchristos 	else
254820e172cSchristos 		msgsdlen = 0;	/* XXX: GCC */
255820e172cSchristos 
256820e172cSchristos 	if (msgfmt != NULL && *msgfmt != '\0') {
257820e172cSchristos 		strlcat(fmt_cat, " ", FMT_LEN);
258820e172cSchristos 		strlcat(fmt_cat, msgfmt, FMT_LEN);
259820e172cSchristos 	}
260820e172cSchristos 
261820e172cSchristos 	/*
262820e172cSchristos 	 * We wouldn't need this mess if printf handled %m, or if
263820e172cSchristos 	 * strerror() had been invented before syslog().
264820e172cSchristos 	 */
265820e172cSchristos 	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
266820e172cSchristos 		if (ch == '%' && fmt[1] == 'm') {
267820e172cSchristos 			char buf[256];
268820e172cSchristos 
269820e172cSchristos 			if ((*fun->errfun)(saved_errno, buf, sizeof(buf)) != 0)
270820e172cSchristos 				prlen = snprintf_ss(t, fmt_left, "Error %d",
271820e172cSchristos 				    saved_errno);
272820e172cSchristos 			else
273820e172cSchristos 				prlen = strlcpy(t, buf, fmt_left);
274820e172cSchristos 			if (prlen >= fmt_left)
275820e172cSchristos 				prlen = fmt_left - 1;
276820e172cSchristos 			t += prlen;
277820e172cSchristos 			fmt++;
278820e172cSchristos 			fmt_left -= prlen;
279820e172cSchristos 		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
280820e172cSchristos 			*t++ = '%';
281820e172cSchristos 			*t++ = '%';
282820e172cSchristos 			fmt++;
283820e172cSchristos 			fmt_left -= 2;
284820e172cSchristos 		} else {
285820e172cSchristos 			if (fmt_left > 1) {
286820e172cSchristos 				*t++ = ch;
287820e172cSchristos 				fmt_left--;
288820e172cSchristos 			}
289820e172cSchristos 		}
290820e172cSchristos 	}
291820e172cSchristos 	*t = '\0';
292820e172cSchristos 
293820e172cSchristos 	prlen = (*fun->prfun)(p, tbuf_left, fmt_cpy, ap);
294820e172cSchristos 	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
295820e172cSchristos 		iov[iovcnt].iov_base = p + msgsdlen;
296820e172cSchristos 		iov[iovcnt].iov_len = prlen - msgsdlen;
297820e172cSchristos 		iovcnt++;
298820e172cSchristos 	}
299820e172cSchristos 
300820e172cSchristos 	DEC();
301820e172cSchristos 	cnt = p - tbuf;
302820e172cSchristos 
303820e172cSchristos 	/* Output to stderr if requested. */
304820e172cSchristos 	if (data->log_stat & LOG_PERROR) {
3051b119018Sroy 		struct iovec *piov;
3061b119018Sroy 		int piovcnt;
3071b119018Sroy 
308820e172cSchristos 		iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
309820e172cSchristos 		iov[iovcnt].iov_len = 1;
3101b119018Sroy 		if (data->log_stat & LOG_PTRIM) {
3111b119018Sroy 			piov = &iov[iovcnt - 1];
3121b119018Sroy 			piovcnt = 2;
3131b119018Sroy 		} else {
3141b119018Sroy 			piov = iov;
3151b119018Sroy 			piovcnt = iovcnt + 1;
316820e172cSchristos 		}
3171b119018Sroy 		(void)writev(STDERR_FILENO, piov, piovcnt);
3181b119018Sroy 	}
3191b119018Sroy 
3201b119018Sroy 	if (data->log_stat & LOG_NLOG)
3211b119018Sroy 		goto out;
322820e172cSchristos 
323820e172cSchristos 	/* Get connected, output the message to the local logger. */
3249cc0be31Schristos 	(*fun->lock)(data);
325820e172cSchristos 	opened = !data->log_opened;
326820e172cSchristos 	if (opened)
3279cc0be31Schristos 		_openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
328820e172cSchristos 	connectlog_r(data);
329820e172cSchristos 
330820e172cSchristos 	/*
331820e172cSchristos 	 * If the send() failed, there are two likely scenarios:
332820e172cSchristos 	 *  1) syslogd was restarted
333820e172cSchristos 	 *  2) /dev/log is out of socket buffer space
334820e172cSchristos 	 * We attempt to reconnect to /dev/log to take care of
335820e172cSchristos 	 * case #1 and keep send()ing data to cover case #2
336820e172cSchristos 	 * to give syslogd a chance to empty its socket buffer.
337820e172cSchristos 	 */
338820e172cSchristos 	for (tries = 0; tries < MAXTRIES; tries++) {
339820e172cSchristos 		if (send(data->log_file, tbuf, cnt, 0) != -1)
340820e172cSchristos 			break;
341820e172cSchristos 		if (errno != ENOBUFS) {
342820e172cSchristos 			disconnectlog_r(data);
343820e172cSchristos 			connectlog_r(data);
344820e172cSchristos 		} else
345820e172cSchristos 			(void)usleep(1);
346820e172cSchristos 	}
347820e172cSchristos 
348820e172cSchristos 	/*
349820e172cSchristos 	 * Output the message to the console; try not to block
350820e172cSchristos 	 * as a blocking console should not stop other processes.
351820e172cSchristos 	 * Make sure the error reported is the one from the syslogd failure.
352820e172cSchristos 	 */
353820e172cSchristos 	if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
354820e172cSchristos 	    (fd = open(_PATH_CONSOLE,
355820e172cSchristos 		O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0)) >= 0) {
356820e172cSchristos 		iov[iovcnt].iov_base = __UNCONST(CRLF);
357820e172cSchristos 		iov[iovcnt].iov_len = 2;
358820e172cSchristos 		(void)writev(fd, iov, iovcnt + 1);
359820e172cSchristos 		(void)close(fd);
360820e172cSchristos 	}
361820e172cSchristos 
3621b119018Sroy out:
3639cc0be31Schristos 	if (!(*fun->unlock)(data) && opened)
3649cc0be31Schristos 		_closelog_unlocked_r(data);
365820e172cSchristos }
366820e172cSchristos 
367820e172cSchristos int
setlogmask_r(int pmask,struct syslog_data * data)368820e172cSchristos setlogmask_r(int pmask, struct syslog_data *data)
369820e172cSchristos {
370820e172cSchristos 	int omask;
371820e172cSchristos 
372820e172cSchristos 	omask = data->log_mask;
373820e172cSchristos 	if (pmask != 0)
374820e172cSchristos 		data->log_mask = pmask;
375820e172cSchristos 	return omask;
376820e172cSchristos }
377