Home | History | Annotate | Line # | Download | only in gen
xsyslog.c revision 1.1
      1  1.1  christos /*	$NetBSD: xsyslog.c,v 1.1 2017/01/12 00:38:01 christos 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.1  christos __RCSID("$NetBSD: xsyslog.c,v 1.1 2017/01/12 00:38:01 christos 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.1  christos #ifdef __weak_alias
     62  1.1  christos __weak_alias(closelog,_closelog)
     63  1.1  christos __weak_alias(openlog,_openlog)
     64  1.1  christos __weak_alias(setlogmask,_setlogmask)
     65  1.1  christos #endif
     66  1.1  christos 
     67  1.1  christos struct syslog_data _syslog_data = SYSLOG_DATA_INIT;
     68  1.1  christos 
     69  1.1  christos static void	openlog_unlocked_r(const char *, int, int,
     70  1.1  christos     struct syslog_data *);
     71  1.1  christos static void	disconnectlog_r(struct syslog_data *);
     72  1.1  christos static void	connectlog_r(struct syslog_data *);
     73  1.1  christos 
     74  1.1  christos #ifdef _REENTRANT
     75  1.1  christos static mutex_t	syslog_mutex = MUTEX_INITIALIZER;
     76  1.1  christos #endif
     77  1.1  christos 
     78  1.1  christos void
     79  1.1  christos openlog(const char *ident, int logstat, int logfac)
     80  1.1  christos {
     81  1.1  christos 	openlog_r(ident, logstat, logfac, &_syslog_data);
     82  1.1  christos }
     83  1.1  christos 
     84  1.1  christos void
     85  1.1  christos closelog(void)
     86  1.1  christos {
     87  1.1  christos 	closelog_r(&_syslog_data);
     88  1.1  christos }
     89  1.1  christos 
     90  1.1  christos /* setlogmask -- set the log mask level */
     91  1.1  christos int
     92  1.1  christos setlogmask(int pmask)
     93  1.1  christos {
     94  1.1  christos 	return setlogmask_r(pmask, &_syslog_data);
     95  1.1  christos }
     96  1.1  christos 
     97  1.1  christos static void
     98  1.1  christos _xsyslogp_r(int pri, struct syslog_fun *fun,
     99  1.1  christos     struct syslog_data *data, const char *msgid,
    100  1.1  christos     const char *sdfmt, const char *msgfmt, ...)
    101  1.1  christos {
    102  1.1  christos 	va_list ap;
    103  1.1  christos 	va_start(ap, msgfmt);
    104  1.1  christos 	_vxsyslogp_r(pri, fun, data, msgid, sdfmt, msgfmt, ap);
    105  1.1  christos 	va_end(ap);
    106  1.1  christos }
    107  1.1  christos 
    108  1.1  christos void
    109  1.1  christos _vxsyslogp_r(int pri, struct syslog_fun *fun,
    110  1.1  christos     struct syslog_data *data, const char *msgid,
    111  1.1  christos     const char *sdfmt, const char *msgfmt, va_list ap)
    112  1.1  christos {
    113  1.1  christos 	static const char BRCOSP[] = "]: ";
    114  1.1  christos 	static const char CRLF[] = "\r\n";
    115  1.1  christos 	size_t cnt, prlen, tries;
    116  1.1  christos 	char ch, *p, *t;
    117  1.1  christos 	int fd, saved_errno;
    118  1.1  christos #define TBUF_LEN	2048
    119  1.1  christos #define FMT_LEN		1024
    120  1.1  christos #define MAXTRIES	10
    121  1.1  christos 	char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
    122  1.1  christos 	size_t tbuf_left, fmt_left, msgsdlen;
    123  1.1  christos 	char *fmt = fmt_cat;
    124  1.1  christos 	struct iovec iov[7];	/* prog + [ + pid + ]: + fmt + crlf */
    125  1.1  christos 	int opened, iovcnt;
    126  1.1  christos 
    127  1.1  christos #define INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
    128  1.1  christos 	/* Check for invalid bits. */
    129  1.1  christos 	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
    130  1.1  christos 		_xsyslogp_r(INTERNALLOG, &_syslog_ss_fun, data, NULL, NULL,
    131  1.1  christos 		    "%s: unknown facility/priority: %x", pri);
    132  1.1  christos 		pri &= LOG_PRIMASK|LOG_FACMASK;
    133  1.1  christos 	}
    134  1.1  christos 
    135  1.1  christos 	/* Check priority against setlogmask values. */
    136  1.1  christos 	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
    137  1.1  christos 		return;
    138  1.1  christos 
    139  1.1  christos 	saved_errno = errno;
    140  1.1  christos 
    141  1.1  christos 	/* Set default facility if none specified. */
    142  1.1  christos 	if ((pri & LOG_FACMASK) == 0)
    143  1.1  christos 		pri |= data->log_fac;
    144  1.1  christos 
    145  1.1  christos 	/* Build the message. */
    146  1.1  christos 	p = tbuf;
    147  1.1  christos 	tbuf_left = TBUF_LEN;
    148  1.1  christos 
    149  1.1  christos 	prlen = snprintf_ss(p, tbuf_left, "<%d>1 ", pri);
    150  1.1  christos 	DEC();
    151  1.1  christos 
    152  1.1  christos 	prlen = (*fun->timefun)(p, tbuf_left);
    153  1.1  christos 
    154  1.1  christos 	if (data == &_syslog_data)
    155  1.1  christos 		mutex_lock(&syslog_mutex);
    156  1.1  christos 
    157  1.1  christos 	if (data->log_hostname[0] == '\0' && gethostname(data->log_hostname,
    158  1.1  christos 	    sizeof(data->log_hostname)) == -1) {
    159  1.1  christos 		/* can this really happen? */
    160  1.1  christos 		data->log_hostname[0] = '-';
    161  1.1  christos 		data->log_hostname[1] = '\0';
    162  1.1  christos 	}
    163  1.1  christos 
    164  1.1  christos 	DEC();
    165  1.1  christos 	prlen = snprintf_ss(p, tbuf_left, " %s ", data->log_hostname);
    166  1.1  christos 
    167  1.1  christos 	if (data->log_tag == NULL)
    168  1.1  christos 		data->log_tag = getprogname();
    169  1.1  christos 
    170  1.1  christos 	DEC();
    171  1.1  christos 	prlen = snprintf_ss(p, tbuf_left, "%s ",
    172  1.1  christos 	    data->log_tag ? data->log_tag : "-");
    173  1.1  christos 
    174  1.1  christos 	if (data == &_syslog_data)
    175  1.1  christos 		mutex_unlock(&syslog_mutex);
    176  1.1  christos 
    177  1.1  christos 	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
    178  1.1  christos 		iovcnt = 0;
    179  1.1  christos 		iov[iovcnt].iov_base = p;
    180  1.1  christos 		iov[iovcnt].iov_len = prlen - 1;
    181  1.1  christos 		iovcnt++;
    182  1.1  christos 	}
    183  1.1  christos 	DEC();
    184  1.1  christos 
    185  1.1  christos 	if (data->log_stat & LOG_PID) {
    186  1.1  christos 		prlen = snprintf_ss(p, tbuf_left, "%d ", getpid());
    187  1.1  christos 		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
    188  1.1  christos 			iov[iovcnt].iov_base = __UNCONST("[");
    189  1.1  christos 			iov[iovcnt].iov_len = 1;
    190  1.1  christos 			iovcnt++;
    191  1.1  christos 			iov[iovcnt].iov_base = p;
    192  1.1  christos 			iov[iovcnt].iov_len = prlen - 1;
    193  1.1  christos 			iovcnt++;
    194  1.1  christos 			iov[iovcnt].iov_base = __UNCONST(BRCOSP);
    195  1.1  christos 			iov[iovcnt].iov_len = 3;
    196  1.1  christos 			iovcnt++;
    197  1.1  christos 		}
    198  1.1  christos 	} else {
    199  1.1  christos 		prlen = snprintf_ss(p, tbuf_left, "- ");
    200  1.1  christos 		if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
    201  1.1  christos 			iov[iovcnt].iov_base = __UNCONST(BRCOSP + 1);
    202  1.1  christos 			iov[iovcnt].iov_len = 2;
    203  1.1  christos 			iovcnt++;
    204  1.1  christos 		}
    205  1.1  christos 	}
    206  1.1  christos 	DEC();
    207  1.1  christos 
    208  1.1  christos 	/*
    209  1.1  christos 	 * concat the format strings, then use one vsnprintf()
    210  1.1  christos 	 */
    211  1.1  christos 	if (msgid != NULL && *msgid != '\0') {
    212  1.1  christos 		strlcat(fmt_cat, msgid, FMT_LEN);
    213  1.1  christos 		strlcat(fmt_cat, " ", FMT_LEN);
    214  1.1  christos 	} else
    215  1.1  christos 		strlcat(fmt_cat, "- ", FMT_LEN);
    216  1.1  christos 
    217  1.1  christos 	if (sdfmt != NULL && *sdfmt != '\0') {
    218  1.1  christos 		strlcat(fmt_cat, sdfmt, FMT_LEN);
    219  1.1  christos 	} else
    220  1.1  christos 		strlcat(fmt_cat, "-", FMT_LEN);
    221  1.1  christos 
    222  1.1  christos 	if (data->log_stat & (LOG_PERROR|LOG_CONS))
    223  1.1  christos 		msgsdlen = strlen(fmt_cat) + 1;
    224  1.1  christos 	else
    225  1.1  christos 		msgsdlen = 0;	/* XXX: GCC */
    226  1.1  christos 
    227  1.1  christos 	if (msgfmt != NULL && *msgfmt != '\0') {
    228  1.1  christos 		strlcat(fmt_cat, " ", FMT_LEN);
    229  1.1  christos 		strlcat(fmt_cat, msgfmt, FMT_LEN);
    230  1.1  christos 	}
    231  1.1  christos 
    232  1.1  christos 	/*
    233  1.1  christos 	 * We wouldn't need this mess if printf handled %m, or if
    234  1.1  christos 	 * strerror() had been invented before syslog().
    235  1.1  christos 	 */
    236  1.1  christos 	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
    237  1.1  christos 		if (ch == '%' && fmt[1] == 'm') {
    238  1.1  christos 			char buf[256];
    239  1.1  christos 
    240  1.1  christos 			if ((*fun->errfun)(saved_errno, buf, sizeof(buf)) != 0)
    241  1.1  christos 				prlen = snprintf_ss(t, fmt_left, "Error %d",
    242  1.1  christos 				    saved_errno);
    243  1.1  christos 			else
    244  1.1  christos 				prlen = strlcpy(t, buf, fmt_left);
    245  1.1  christos 			if (prlen >= fmt_left)
    246  1.1  christos 				prlen = fmt_left - 1;
    247  1.1  christos 			t += prlen;
    248  1.1  christos 			fmt++;
    249  1.1  christos 			fmt_left -= prlen;
    250  1.1  christos 		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
    251  1.1  christos 			*t++ = '%';
    252  1.1  christos 			*t++ = '%';
    253  1.1  christos 			fmt++;
    254  1.1  christos 			fmt_left -= 2;
    255  1.1  christos 		} else {
    256  1.1  christos 			if (fmt_left > 1) {
    257  1.1  christos 				*t++ = ch;
    258  1.1  christos 				fmt_left--;
    259  1.1  christos 			}
    260  1.1  christos 		}
    261  1.1  christos 	}
    262  1.1  christos 	*t = '\0';
    263  1.1  christos 
    264  1.1  christos 	prlen = (*fun->prfun)(p, tbuf_left, fmt_cpy, ap);
    265  1.1  christos 	if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
    266  1.1  christos 		iov[iovcnt].iov_base = p + msgsdlen;
    267  1.1  christos 		iov[iovcnt].iov_len = prlen - msgsdlen;
    268  1.1  christos 		iovcnt++;
    269  1.1  christos 	}
    270  1.1  christos 
    271  1.1  christos 	DEC();
    272  1.1  christos 	cnt = p - tbuf;
    273  1.1  christos 
    274  1.1  christos 	/* Output to stderr if requested. */
    275  1.1  christos 	if (data->log_stat & LOG_PERROR) {
    276  1.1  christos 		iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
    277  1.1  christos 		iov[iovcnt].iov_len = 1;
    278  1.1  christos 		(void)writev(STDERR_FILENO, iov, iovcnt + 1);
    279  1.1  christos 	}
    280  1.1  christos 
    281  1.1  christos 	/* Get connected, output the message to the local logger. */
    282  1.1  christos 	if (data == &_syslog_data)
    283  1.1  christos 		mutex_lock(&syslog_mutex);
    284  1.1  christos 	opened = !data->log_opened;
    285  1.1  christos 	if (opened)
    286  1.1  christos 		openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
    287  1.1  christos 	connectlog_r(data);
    288  1.1  christos 
    289  1.1  christos 	/*
    290  1.1  christos 	 * If the send() failed, there are two likely scenarios:
    291  1.1  christos 	 *  1) syslogd was restarted
    292  1.1  christos 	 *  2) /dev/log is out of socket buffer space
    293  1.1  christos 	 * We attempt to reconnect to /dev/log to take care of
    294  1.1  christos 	 * case #1 and keep send()ing data to cover case #2
    295  1.1  christos 	 * to give syslogd a chance to empty its socket buffer.
    296  1.1  christos 	 */
    297  1.1  christos 	for (tries = 0; tries < MAXTRIES; tries++) {
    298  1.1  christos 		if (send(data->log_file, tbuf, cnt, 0) != -1)
    299  1.1  christos 			break;
    300  1.1  christos 		if (errno != ENOBUFS) {
    301  1.1  christos 			disconnectlog_r(data);
    302  1.1  christos 			connectlog_r(data);
    303  1.1  christos 		} else
    304  1.1  christos 			(void)usleep(1);
    305  1.1  christos 	}
    306  1.1  christos 
    307  1.1  christos 	/*
    308  1.1  christos 	 * Output the message to the console; try not to block
    309  1.1  christos 	 * as a blocking console should not stop other processes.
    310  1.1  christos 	 * Make sure the error reported is the one from the syslogd failure.
    311  1.1  christos 	 */
    312  1.1  christos 	if (tries == MAXTRIES && (data->log_stat & LOG_CONS) &&
    313  1.1  christos 	    (fd = open(_PATH_CONSOLE,
    314  1.1  christos 		O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0)) >= 0) {
    315  1.1  christos 		iov[iovcnt].iov_base = __UNCONST(CRLF);
    316  1.1  christos 		iov[iovcnt].iov_len = 2;
    317  1.1  christos 		(void)writev(fd, iov, iovcnt + 1);
    318  1.1  christos 		(void)close(fd);
    319  1.1  christos 	}
    320  1.1  christos 
    321  1.1  christos 	if (data == &_syslog_data)
    322  1.1  christos 		mutex_unlock(&syslog_mutex);
    323  1.1  christos 
    324  1.1  christos 	if (data != &_syslog_data && opened) {
    325  1.1  christos 		/* preserve log tag */
    326  1.1  christos 		const char *ident = data->log_tag;
    327  1.1  christos 		closelog_r(data);
    328  1.1  christos 		data->log_tag = ident;
    329  1.1  christos 	}
    330  1.1  christos }
    331  1.1  christos 
    332  1.1  christos static void
    333  1.1  christos disconnectlog_r(struct syslog_data *data)
    334  1.1  christos {
    335  1.1  christos 	/*
    336  1.1  christos 	 * If the user closed the FD and opened another in the same slot,
    337  1.1  christos 	 * that's their problem.  They should close it before calling on
    338  1.1  christos 	 * system services.
    339  1.1  christos 	 */
    340  1.1  christos 	if (data->log_file != -1) {
    341  1.1  christos 		(void)close(data->log_file);
    342  1.1  christos 		data->log_file = -1;
    343  1.1  christos 	}
    344  1.1  christos 	data->log_connected = 0;		/* retry connect */
    345  1.1  christos }
    346  1.1  christos 
    347  1.1  christos static void
    348  1.1  christos connectlog_r(struct syslog_data *data)
    349  1.1  christos {
    350  1.1  christos 	/* AF_UNIX address of local logger */
    351  1.1  christos 	static const struct sockaddr_un sun = {
    352  1.1  christos 		.sun_family = AF_LOCAL,
    353  1.1  christos 		.sun_len = sizeof(sun),
    354  1.1  christos 		.sun_path = _PATH_LOG,
    355  1.1  christos 	};
    356  1.1  christos 
    357  1.1  christos 	if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
    358  1.1  christos 		if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC,
    359  1.1  christos 		    0)) == -1)
    360  1.1  christos 			return;
    361  1.1  christos 		data->log_connected = 0;
    362  1.1  christos 	}
    363  1.1  christos 	if (!data->log_connected) {
    364  1.1  christos 		if (connect(data->log_file,
    365  1.1  christos 		    (const struct sockaddr *)(const void *)&sun,
    366  1.1  christos 		    (socklen_t)sizeof(sun)) == -1) {
    367  1.1  christos 			(void)close(data->log_file);
    368  1.1  christos 			data->log_file = -1;
    369  1.1  christos 		} else
    370  1.1  christos 			data->log_connected = 1;
    371  1.1  christos 	}
    372  1.1  christos }
    373  1.1  christos 
    374  1.1  christos static void
    375  1.1  christos openlog_unlocked_r(const char *ident, int logstat, int logfac,
    376  1.1  christos     struct syslog_data *data)
    377  1.1  christos {
    378  1.1  christos 	if (ident != NULL)
    379  1.1  christos 		data->log_tag = ident;
    380  1.1  christos 	data->log_stat = logstat;
    381  1.1  christos 	if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
    382  1.1  christos 		data->log_fac = logfac;
    383  1.1  christos 
    384  1.1  christos 	if (data->log_stat & LOG_NDELAY)	/* open immediately */
    385  1.1  christos 		connectlog_r(data);
    386  1.1  christos 
    387  1.1  christos 	data->log_opened = 1;
    388  1.1  christos }
    389  1.1  christos 
    390  1.1  christos void
    391  1.1  christos openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
    392  1.1  christos {
    393  1.1  christos 	if (data == &_syslog_data)
    394  1.1  christos 		mutex_lock(&syslog_mutex);
    395  1.1  christos 	openlog_unlocked_r(ident, logstat, logfac, data);
    396  1.1  christos 	if (data == &_syslog_data)
    397  1.1  christos 		mutex_unlock(&syslog_mutex);
    398  1.1  christos }
    399  1.1  christos 
    400  1.1  christos void
    401  1.1  christos closelog_r(struct syslog_data *data)
    402  1.1  christos {
    403  1.1  christos 	if (data == &_syslog_data)
    404  1.1  christos 		mutex_lock(&syslog_mutex);
    405  1.1  christos 	(void)close(data->log_file);
    406  1.1  christos 	data->log_file = -1;
    407  1.1  christos 	data->log_connected = 0;
    408  1.1  christos 	data->log_tag = NULL;
    409  1.1  christos 	if (data == &_syslog_data)
    410  1.1  christos 		mutex_unlock(&syslog_mutex);
    411  1.1  christos }
    412  1.1  christos 
    413  1.1  christos int
    414  1.1  christos setlogmask_r(int pmask, struct syslog_data *data)
    415  1.1  christos {
    416  1.1  christos 	int omask;
    417  1.1  christos 
    418  1.1  christos 	omask = data->log_mask;
    419  1.1  christos 	if (pmask != 0)
    420  1.1  christos 		data->log_mask = pmask;
    421  1.1  christos 	return omask;
    422  1.1  christos }
    423