Home | History | Annotate | Line # | Download | only in gen
xsyslog.c revision 1.5
      1  1.5      maya /*	$NetBSD: xsyslog.c,v 1.5 2017/04/18 12:34:07 maya 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      maya __RCSID("$NetBSD: xsyslog.c,v 1.5 2017/04/18 12:34:07 maya 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.1  christos static 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.1  christos 		    "%s: 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