Home | History | Annotate | Line # | Download | only in gen
xsyslog.c revision 1.2.2.1
      1  1.2.2.1    bouyer /*	$NetBSD: xsyslog.c,v 1.2.2.1 2017/04/21 16:53:09 bouyer 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.2.2.1    bouyer __RCSID("$NetBSD: xsyslog.c,v 1.2.2.1 2017/04/21 16:53:09 bouyer 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.2.2.1    bouyer 	iovcnt = opened = 0;
    158  1.2.2.1    bouyer 
    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.2.2.1    bouyer 		struct iovec *piov;
    306  1.2.2.1    bouyer 		int piovcnt;
    307  1.2.2.1    bouyer 
    308      1.1  christos 		iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
    309      1.1  christos 		iov[iovcnt].iov_len = 1;
    310  1.2.2.1    bouyer 		if (data->log_stat & LOG_PTRIM) {
    311  1.2.2.1    bouyer 			piov = &iov[iovcnt - 1];
    312  1.2.2.1    bouyer 			piovcnt = 2;
    313  1.2.2.1    bouyer 		} else {
    314  1.2.2.1    bouyer 			piov = iov;
    315  1.2.2.1    bouyer 			piovcnt = iovcnt + 1;
    316  1.2.2.1    bouyer 		}
    317  1.2.2.1    bouyer 		(void)writev(STDERR_FILENO, piov, piovcnt);
    318      1.1  christos 	}
    319      1.1  christos 
    320  1.2.2.1    bouyer 	if (data->log_stat & LOG_NLOG)
    321  1.2.2.1    bouyer 		goto out;
    322  1.2.2.1    bouyer 
    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.2.2.1    bouyer 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