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