├── .gitignore ├── src ├── ttymsg.h ├── syslog.tcl ├── Makefile ├── pathnames.h ├── README ├── ttymsg.c ├── syslogd.8 ├── syslog.conf.5 └── syslogd.c └── scripts ├── README └── tclsyslogd /.gitignore: -------------------------------------------------------------------------------- 1 | src/syslog.conf.5.gz 2 | src/tclsyslogd 3 | src/syslogd.8.gz 4 | src/syslogd.o 5 | src/ttymsg.o 6 | syslogd.8.gz 7 | syslog.conf.5.gz 8 | tclsyslogd 9 | -------------------------------------------------------------------------------- /src/ttymsg.h: -------------------------------------------------------------------------------- 1 | /* $FreeBSD: src/usr.bin/wall/ttymsg.h,v 1.1.38.1 2009/08/03 08:13:06 kensmith Exp $ */ 2 | 3 | const char *ttymsg(struct iovec *, int, const char *, int); 4 | -------------------------------------------------------------------------------- /src/syslog.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # an extremely simple syslog.tcl 3 | # 4 | 5 | proc syslog {messageArray} { 6 | upvar $messageArray message 7 | 8 | parray message 9 | puts "" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /scripts/README: -------------------------------------------------------------------------------- 1 | 2 | Build tclsyslogd from the src directory. 3 | 4 | Make install doesn't currently work from there as this stuff isn't in 5 | the FreeBSD source tree. 6 | 7 | Copy tclsyslogd script to /usr/local/etc/rc.d 8 | 9 | Copy tclsyslogd binary from ../src to /usr/local/sbin 10 | 11 | Edit /etc/rc.conf to enable tclsyslogd and disable syslogd... 12 | 13 | tclsyslogd_enable="YES" 14 | syslogd_enable="NO" 15 | 16 | Copy syslogd_flags as tclsyslogd_flags, for instance for a log host: 17 | 18 | tclsyslogd_flags="" 19 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # @(#)Makefile 8.1 (Berkeley) 6/6/93 2 | 3 | # $FreeBSD: src/usr.sbin/syslogd/Makefile,v 1.14.2.1 2009/08/03 08:13:06 kensmith Exp $ 4 | 5 | # uncomment for debugging - probably a better way 6 | #CFLAGS+= -g -O0 7 | #LDFLAGS+= -g 8 | 9 | .include 10 | 11 | .PATH: /usr/src/usr.bin/wall 12 | 13 | PROG= tclsyslogd 14 | MAN= syslog.conf.5 syslogd.8 15 | SRCS= syslogd.c ttymsg.c 16 | 17 | DPADD= ${LIBUTIL} 18 | LDADD= -lutil -ltcl85 19 | 20 | WARNS?= 3 21 | 22 | .if ${MK_INET6_SUPPORT} != "no" 23 | CFLAGS+= -DINET6 24 | .endif 25 | 26 | CFLAGS+= -I/usr/src/usr.bin/wall -I/usr/local/include/tcl8.5 27 | 28 | LDFLAGS+= -L/usr/local/lib 29 | 30 | .include 31 | -------------------------------------------------------------------------------- /scripts/tclsyslogd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # $FreeBSD: src/etc/rc.d/syslogd,v 1.12.2.1 2008/01/28 07:55:44 dougb Exp $ 4 | # 5 | 6 | # PROVIDE: syslogd 7 | # REQUIRE: LOGIN 8 | 9 | ## REQUIRE: mountcritremote cleanvar newsyslog 10 | ## BEFORE: SERVERS 11 | 12 | . /etc/rc.subr 13 | 14 | name="tclsyslogd" 15 | rcvar=`set_rcvar` 16 | pidfile="/var/run/syslog.pid" 17 | command="/usr/local/sbin/${name}" 18 | required_files="/etc/syslog.conf" 19 | start_precmd="syslogd_precmd" 20 | extra_commands="reload" 21 | 22 | sockfile="/var/run/syslogd.sockets" 23 | evalargs="rc_flags=\"\`set_socketlist\` \$rc_flags\"" 24 | altlog_proglist="named" 25 | 26 | syslogd_precmd() 27 | { 28 | local _l _ldir 29 | 30 | # Transitional symlink for old binaries 31 | # 32 | if [ ! -L /dev/log ]; then 33 | ln -sf /var/run/log /dev/log 34 | fi 35 | rm -f /var/run/log 36 | 37 | # Create default list of syslog sockets to watch 38 | # 39 | ( umask 022 ; > $sockfile ) 40 | 41 | # If running named(8) or ntpd(8) chrooted, added appropriate 42 | # syslog socket to list of sockets to watch. 43 | # 44 | for _l in $altlog_proglist; do 45 | eval _ldir=\$${_l}_chrootdir 46 | if checkyesno `set_rcvar $_l` && [ -n "$_ldir" ]; then 47 | echo "${_ldir}/var/run/log" >> $sockfile 48 | fi 49 | done 50 | 51 | # If other sockets have been provided, change run_rc_command()'s 52 | # internal copy of $syslogd_flags to force use of specific 53 | # syslogd sockets. 54 | # 55 | if [ -s $sockfile ]; then 56 | echo "/var/run/log" >> $sockfile 57 | eval $evalargs 58 | fi 59 | 60 | return 0 61 | } 62 | 63 | set_socketlist() 64 | { 65 | local _s _socketargs 66 | 67 | _socketargs= 68 | for _s in `cat $sockfile | tr '\n' ' '` ; do 69 | _socketargs="-l $_s $_socketargs" 70 | done 71 | echo $_socketargs 72 | } 73 | load_rc_config $name 74 | run_rc_command "$1" 75 | -------------------------------------------------------------------------------- /src/pathnames.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1989, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 4. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 30 | * $FreeBSD: src/usr.sbin/syslogd/pathnames.h,v 1.2.30.1 2009/08/03 08:13:06 kensmith Exp $ 31 | */ 32 | 33 | #include 34 | 35 | #define _PATH_KLOG "/dev/klog" 36 | #define _PATH_LOGCONF "/etc/syslog.conf" 37 | #define _PATH_TCLCONF "/usr/local/etc/syslog.tcl" 38 | #define _PATH_LOGPID "/var/run/syslog.pid" 39 | -------------------------------------------------------------------------------- /src/README: -------------------------------------------------------------------------------- 1 | This is tclsyslogd, a modified verison of FreeBSD syslogd that has a Tcl 2 | interpreter built-in. 3 | 4 | The reason we do this, instead of just scanning /var/log/messages, is that 5 | the translation of the syslog message to /var/log/messages causes fidelity to 6 | be lost -- only within syslogd are all the fields known in their entirety. 7 | 8 | Tclsyslogd is fully upward-compatible from regular syslogd -- all syslogd 9 | command line options and configuration files are fully supported. 10 | 11 | When running tclsyslogd, a Tcl interpreter is created and the file 12 | /usr/local/etc/tclsyslogd/syslog.tcl is sourced in. If the file isn't 13 | found, tclsyslogd doesn't do any tcl stuff. If normal debugging is enabled, 14 | it dprintf's messages about any problems it has starting up. 15 | 16 | Whenever tclsyslogd receives a syslog message, whether initiated locally or 17 | remotely (if so configured), your syslog proc is invoked, which can then 18 | look at the message and decide what to do about it, if anything. 19 | 20 | The named array argument should be aliased locally in the syslog proc as an 21 | name you want to refer to the array as within your proc, using upvar. 22 | 23 | Here is a very simple syslog proc: 24 | 25 | proc syslog {messageArray} { 26 | upvar $messageArray message 27 | 28 | parray message 29 | puts "" 30 | } 31 | 32 | This proc will be called for every syslog message received by tclsyslogd. 33 | This will print the array comprising each syslog message as it is received. 34 | 35 | The upvar says I want to refer to the array as "message". 36 | 37 | Your proc can then examine the array for key-value pairs. Some keys will 38 | always be present and some may or may not be present. 39 | 40 | The available key-value pairs are as follows: 41 | 42 | clock - the current time as an epoch clock (not from the message 43 | by rather the epoch clock on the server tclsyslogd is 44 | running on) 45 | 46 | timestamp - the timestamp string from the message. Note that the 47 | string is locale dependent and may be in the timezone 48 | of the process that generated the log message, which 49 | may be a different timezone from the default or even 50 | other instances of the same calling program. 51 | 52 | host - the host on which the syslog message was generated (system 53 | the message is from) 54 | 55 | facility - the facility specified in the syslog message 56 | 57 | facility can be one of... 58 | 59 | auth, authpriv, console, cron, daemon, ftp, kern, lpr, 60 | mail, news, ntp, security, syslog, user, uucp, 61 | local0, local1, local2, local3, local4, local5, local6, 62 | local7 63 | 64 | priority - the priority specified in the syslog message 65 | 66 | priority can be one of... 67 | 68 | alert, crit, debug, emerg, err, info, notice, warning 69 | 70 | program - the program that generated the syslog message 71 | 72 | program will be present but empty if not specified 73 | 74 | kernel - OPTIONAL - the kernel prefix if the message was generated 75 | by the operating system kernel 76 | 77 | msg - the text of the syslog message 78 | 79 | example messages: 80 | 81 | message(clock) = 1291160416 82 | message(facility) = daemon 83 | message(host) = cfood 84 | message(msg) = nrpe[47754]: Command completed with return code 0 and output: mem_used:8032756 mem_total:16777216 85 | message(priority) = debug 86 | message(program) = nrpe 87 | message(timestamp) = Nov 30 17:40:16 88 | 89 | message(clock) = 1291160409 90 | message(facility) = local5 91 | message(host) = cfood 92 | message(msg) = balancer[44238]: agent_heartbeat: cfood.hou.flightaware.com (sock7) 0.66 93 | message(priority) = debug 94 | message(program) = balancer 95 | message(timestamp) = Nov 30 17:40:09 96 | 97 | message(clock) = 1291160402 98 | message(facility) = syslog 99 | message(host) = cfood 100 | message(msg) = syslogd: restart 101 | message(priority) = info 102 | message(program) = syslogd 103 | message(timestamp) = Nov 30 17:40:02 104 | 105 | message(clock) = 1291160580 106 | message(facility) = cron 107 | message(host) = cfood 108 | message(msg) = /usr/sbin/cron[47841]: ... 109 | message(priority) = info 110 | message(program) = 111 | message(timestamp) = Nov 30 17:43:00 112 | 113 | message(clock) = 1291160614 114 | message(facility) = local5 115 | message(host) = cfood 116 | message(msg) = balancer[44238]: agent_heartbeat: cfood.hou.flightaware.com (sock7) 0.58 117 | message(priority) = debug 118 | message(program) = balancer 119 | message(timestamp) = Nov 30 17:43:34 120 | 121 | message(clock) = 1291160626 122 | message(facility) = mail 123 | message(host) = cfood 124 | message(msg) = postfix/smtp[47864]: E338E629299: to=, relay=none, delay=374433, delays=374431/0.45/1.2/0, dsn=4.4.3, status=deferred (Host or domain name not found. Name service error for name=aperturehealth.com type=MX: Host not found, try again) 125 | message(priority) = info 126 | message(program) = postfix 127 | message(timestamp) = Nov 30 17:43:46 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/ttymsg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1989, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. All advertising materials mentioning features or use of this software 14 | * must display the following acknowledgement: 15 | * This product includes software developed by the University of 16 | * California, Berkeley and its contributors. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | 36 | __FBSDID("$FreeBSD: src/usr.bin/wall/ttymsg.c,v 1.12.10.1 2009/08/03 08:13:06 kensmith Exp $"); 37 | 38 | #ifndef lint 39 | static const char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93"; 40 | #endif 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "ttymsg.h" 55 | 56 | /* 57 | * Display the contents of a uio structure on a terminal. Used by wall(1), 58 | * syslogd(8), and talkd(8). Forks and finishes in child if write would block, 59 | * waiting up to tmout seconds. Returns pointer to error string on unexpected 60 | * error; string is not newline-terminated. Various "normal" errors are 61 | * ignored (exclusive-use, lack of permission, etc.). 62 | */ 63 | const char * 64 | ttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout) 65 | { 66 | struct iovec localiov[7]; 67 | ssize_t left, wret; 68 | int cnt, fd; 69 | static char device[MAXNAMLEN] = _PATH_DEV; 70 | static char errbuf[1024]; 71 | char *p; 72 | int forked; 73 | 74 | forked = 0; 75 | if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0]))) 76 | return ("too many iov's (change code in wall/ttymsg.c)"); 77 | 78 | p = device + sizeof(_PATH_DEV) - 1; 79 | strlcpy(p, line, sizeof(device)); 80 | if (strncmp(p, "pts/", 4) == 0) 81 | p += 4; 82 | if (strchr(p, '/') != NULL) { 83 | /* A slash is an attempt to break security... */ 84 | (void) snprintf(errbuf, sizeof(errbuf), 85 | "Too many '/' in \"%s\"", device); 86 | return (errbuf); 87 | } 88 | 89 | /* 90 | * open will fail on slip lines or exclusive-use lines 91 | * if not running as root; not an error. 92 | */ 93 | if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { 94 | if (errno == EBUSY || errno == EACCES) 95 | return (NULL); 96 | (void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device, 97 | strerror(errno)); 98 | return (errbuf); 99 | } 100 | 101 | for (cnt = 0, left = 0; cnt < iovcnt; ++cnt) 102 | left += iov[cnt].iov_len; 103 | 104 | for (;;) { 105 | wret = writev(fd, iov, iovcnt); 106 | if (wret >= left) 107 | break; 108 | if (wret >= 0) { 109 | left -= wret; 110 | if (iov != localiov) { 111 | bcopy(iov, localiov, 112 | iovcnt * sizeof(struct iovec)); 113 | iov = localiov; 114 | } 115 | for (cnt = 0; (size_t)wret >= iov->iov_len; ++cnt) { 116 | wret -= iov->iov_len; 117 | ++iov; 118 | --iovcnt; 119 | } 120 | if (wret) { 121 | iov->iov_base = (char *)iov->iov_base + wret; 122 | iov->iov_len -= wret; 123 | } 124 | continue; 125 | } 126 | if (errno == EWOULDBLOCK) { 127 | int cpid; 128 | 129 | if (forked) { 130 | (void) close(fd); 131 | _exit(1); 132 | } 133 | cpid = fork(); 134 | if (cpid < 0) { 135 | (void) snprintf(errbuf, sizeof(errbuf), 136 | "fork: %s", strerror(errno)); 137 | (void) close(fd); 138 | return (errbuf); 139 | } 140 | if (cpid) { /* parent */ 141 | (void) close(fd); 142 | return (NULL); 143 | } 144 | forked++; 145 | /* wait at most tmout seconds */ 146 | (void) signal(SIGALRM, SIG_DFL); 147 | (void) signal(SIGTERM, SIG_DFL); /* XXX */ 148 | (void) sigsetmask(0); 149 | (void) alarm((u_int)tmout); 150 | (void) fcntl(fd, F_SETFL, 0); /* clear O_NONBLOCK */ 151 | continue; 152 | } 153 | /* 154 | * We get ENODEV on a slip line if we're running as root, 155 | * and EIO if the line just went away. 156 | */ 157 | if (errno == ENODEV || errno == EIO) 158 | break; 159 | (void) close(fd); 160 | if (forked) 161 | _exit(1); 162 | (void) snprintf(errbuf, sizeof(errbuf), 163 | "%s: %s", device, strerror(errno)); 164 | return (errbuf); 165 | } 166 | 167 | (void) close(fd); 168 | if (forked) 169 | _exit(0); 170 | return (NULL); 171 | } 172 | -------------------------------------------------------------------------------- /src/syslogd.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1983, 1986, 1991, 1993 2 | .\" The Regents of the University of California. All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 4. Neither the name of the University nor the names of its contributors 13 | .\" may be used to endorse or promote products derived from this software 14 | .\" without specific prior written permission. 15 | .\" 16 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | .\" SUCH DAMAGE. 27 | .\" 28 | .\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93 29 | .\" $FreeBSD: src/usr.sbin/syslogd/syslogd.8,v 1.64.2.1 2009/08/03 08:13:06 kensmith Exp $ 30 | .\" 31 | .\" $Id: syslogd.8,v 1.2 2009-02-11 22:45:20 karl Exp $ 32 | .\" 33 | .Dd May 13, 2008 34 | .Dt SYSLOGD 8 35 | .Os 36 | .Sh NAME 37 | .Nm syslogd 38 | .Nd log systems messages 39 | .Sh SYNOPSIS 40 | .Nm 41 | .Op Fl 468ACcdknosuv 42 | .Op Fl a Ar allowed_peer 43 | .Op Fl b Ar bind_address 44 | .Op Fl f Ar config_file 45 | .Op Fl l Oo Ar mode : Oc Ns Ar path 46 | .Op Fl m Ar mark_interval 47 | .Op Fl P Ar pid_file 48 | .Op Fl p Ar log_socket 49 | .Sh DESCRIPTION 50 | The 51 | .Nm 52 | utility reads and logs messages to the system console, log files, other 53 | machines and/or users as specified by its configuration file. 54 | .Pp 55 | The options are as follows: 56 | .Bl -tag -width indent 57 | .It Fl 4 58 | Force 59 | .Nm 60 | to use IPv4 addresses only. 61 | .It Fl 6 62 | Force 63 | .Nm 64 | to use IPv6 addresses only. 65 | .It Fl 8 66 | Tells 67 | .Nm 68 | not to interfere with 8-bit data. Normally 69 | .Nm 70 | will replace C1 control characters 71 | .Pq ISO 8859 and Unicode characters 72 | with their 73 | .Dq M- Ns Em x 74 | equivalent. 75 | Note, this option does not change the way 76 | .Nm 77 | alters control characters 78 | .Pq see Xr iscntrl 3 . 79 | They will always be replaced with their 80 | .Dq ^ Ns Em x 81 | equivalent. 82 | .It Fl A 83 | Ordinarily, 84 | .Nm 85 | tries to send the message to only one address 86 | even if the host has more than one A or AAAA record. 87 | If this option is specified, 88 | .Nm 89 | tries to send the message to all addresses. 90 | .It Fl a Ar allowed_peer 91 | Allow 92 | .Ar allowed_peer 93 | to log to this 94 | .Nm 95 | using UDP datagrams. 96 | Multiple 97 | .Fl a 98 | options may be specified. 99 | .Pp 100 | The 101 | .Ar allowed_peer 102 | option may be any of the following: 103 | .Bl -tag -width "ipaddr/masklen[:service]XX" 104 | .It Xo 105 | .Sm off 106 | .Ar ipaddr 107 | .No / Ar masklen 108 | .Op : Ar service 109 | .Sm on 110 | .Xc 111 | Accept datagrams from 112 | .Ar ipaddr 113 | (in the usual dotted quad notation) with 114 | .Ar masklen 115 | bits being taken into account when doing the address comparison. 116 | .Ar ipaddr 117 | can be also IPv6 address by enclosing the address with 118 | .Ql \&[ 119 | and 120 | .Ql \&] . 121 | If specified, 122 | .Ar service 123 | is the name or number of an UDP service (see 124 | .Xr services 5 ) 125 | the source packet must belong to. 126 | A 127 | .Ar service 128 | of 129 | .Ql \&* 130 | allows packets being sent from any UDP port. 131 | The default 132 | .Ar service 133 | is 134 | .Ql syslog . 135 | If 136 | .Ar ipaddr 137 | is IPv4 address, a missing 138 | .Ar masklen 139 | will be substituted by the historic class A or class B netmasks if 140 | .Ar ipaddr 141 | belongs into the address range of class A or B, respectively, or 142 | by 24 otherwise. 143 | If 144 | .Ar ipaddr 145 | is IPv6 address, a missing 146 | .Ar masklen 147 | will be substituted by 128. 148 | .It Xo 149 | .Sm off 150 | .Ar domainname Op : Ar service 151 | .Sm on 152 | .Xc 153 | Accept datagrams where the reverse address lookup yields 154 | .Ar domainname 155 | for the sender address. 156 | The meaning of 157 | .Ar service 158 | is as explained above. 159 | .It Xo 160 | .Sm off 161 | .No * Ar domainname Op : Ar service 162 | .Sm on 163 | .Xc 164 | Same as before, except that any source host whose name 165 | .Em ends 166 | in 167 | .Ar domainname 168 | will get permission. 169 | .El 170 | .Pp 171 | The 172 | .Fl a 173 | options are ignored if the 174 | .Fl s 175 | option is also specified. 176 | .It Fl b Ar bind_address 177 | Specify one specific IP address or hostname to bind to. 178 | If a hostname is specified, 179 | the IPv4 or IPv6 address which corresponds to it is used. 180 | .It Fl C 181 | Create log files that do not exist (permission is set to 182 | .Li 0600 ) . 183 | .It Fl c 184 | Disable the compression of repeated instances of the same line 185 | into a single line of the form 186 | .Dq Li "last message repeated N times" 187 | when the output is a pipe to another program. 188 | If specified twice, disable this compression in all cases. 189 | .It Fl d 190 | Put 191 | .Nm 192 | into debugging mode. 193 | This is probably only of use to developers working on 194 | .Nm . 195 | .It Fl f 196 | Specify the pathname of an alternate configuration file; 197 | the default is 198 | .Pa /etc/syslog.conf . 199 | .It Fl k 200 | Disable the translation of 201 | messages received with facility 202 | .Dq kern 203 | to facility 204 | .Dq user . 205 | Usually the 206 | .Dq kern 207 | facility is reserved for messages read directly from 208 | .Pa /dev/klog . 209 | .It Fl m 210 | Select the number of minutes between 211 | .Dq mark 212 | messages; the default is 20 minutes. 213 | .It Fl n 214 | Disable dns query for every request. 215 | .It Fl o 216 | Prefix kernel messages with the full kernel boot file as determined by 217 | .Xr getbootfile 3 . 218 | Without this, the kernel message prefix is always 219 | .Dq Li kernel: . 220 | .It Fl p 221 | Specify the pathname of an alternate log socket to be used instead; 222 | the default is 223 | .Pa /var/run/log . 224 | .It Fl P 225 | Specify an alternative file in which to store the process ID. 226 | The default is 227 | .Pa /var/run/syslog.pid . 228 | .It Fl S 229 | Specify the pathname of an alternate log socket for privileged 230 | applications to be used instead; the default is 231 | .Pa /var/run/logpriv . 232 | .It Fl l 233 | Specify a location where 234 | .Nm 235 | should place an additional log socket. 236 | The primary use for this is to place additional log sockets in 237 | .Pa /var/run/log 238 | of various chroot filespaces. 239 | File permissions for socket can be specified in octal representation 240 | before socket name, delimited with a colon. 241 | Path to socket location must be absolute. 242 | .It Fl s 243 | Operate in secure mode. 244 | Do not log messages from remote machines. 245 | If 246 | specified twice, no network socket will be opened at all, which also 247 | disables logging to remote machines. 248 | .It Fl T 249 | Always use the local time and date for messages received from the network, 250 | instead of the timestamp field supplied in the message by the remote host. 251 | This is useful if some of the originating hosts can't keep time properly 252 | or are unable to generate a correct timestamp. 253 | .It Fl u 254 | Unique priority logging. 255 | Only log messages at the specified priority. 256 | Without this option, messages at the stated priority or higher are logged. 257 | This option changes the default comparison from 258 | .Dq => 259 | to 260 | .Dq = . 261 | .It Fl v 262 | Verbose logging. 263 | If specified once, the numeric facility and priority are 264 | logged with each locally-written message. 265 | If specified more than once, 266 | the names of the facility and priority are logged with each locally-written 267 | message. 268 | .El 269 | .Pp 270 | The 271 | .Nm 272 | utility reads its configuration file when it starts up and whenever it 273 | receives a hangup signal. 274 | For information on the format of the configuration file, 275 | see 276 | .Xr syslog.conf 5 . 277 | .Pp 278 | The 279 | .Nm 280 | utility reads messages from the 281 | .Ux 282 | domain sockets 283 | .Pa /var/run/log 284 | and 285 | .Pa /var/run/logpriv , 286 | from an Internet domain socket specified in 287 | .Pa /etc/services , 288 | and from the special device 289 | .Pa /dev/klog 290 | (to read kernel messages). 291 | .Pp 292 | The 293 | .Nm 294 | utility creates its process ID file, 295 | by default 296 | .Pa /var/run/syslog.pid , 297 | and stores its process 298 | ID there. 299 | This can be used to kill or reconfigure 300 | .Nm . 301 | .Pp 302 | The message sent to 303 | .Nm 304 | should consist of a single line. 305 | The message can contain a priority code, which should be a preceding 306 | decimal number in angle braces, for example, 307 | .Sq Aq 5 . 308 | This priority code should map into the priorities defined in the 309 | include file 310 | .In sys/syslog.h . 311 | .Pp 312 | For security reasons, 313 | .Nm 314 | will not append to log files that do not exist (unless 315 | .Fl C 316 | option is specified); 317 | therefore, they must be created manually before running 318 | .Nm . 319 | .Pp 320 | The date and time are taken from the received message. 321 | If the format of the timestamp field is incorrect, 322 | time obtained from the local host is used instead. 323 | This can be overriden by the 324 | .Fl T 325 | flag. 326 | .Sh FILES 327 | .Bl -tag -width /var/run/syslog.pid -compact 328 | .It Pa /etc/syslog.conf 329 | configuration file 330 | .It Pa /var/run/syslog.pid 331 | default process ID file 332 | .It Pa /var/run/log 333 | name of the 334 | .Ux 335 | domain datagram log socket 336 | .It Pa /var/run/logpriv 337 | .Ux 338 | socket for privileged applications 339 | .It Pa /dev/klog 340 | kernel log device 341 | .El 342 | .Sh SEE ALSO 343 | .Xr logger 1 , 344 | .Xr syslog 3 , 345 | .Xr services 5 , 346 | .Xr syslog.conf 5 , 347 | .Xr newsyslog 8 348 | .Sh HISTORY 349 | The 350 | .Nm 351 | utility appeared in 352 | .Bx 4.3 . 353 | .Pp 354 | The 355 | .Fl a , 356 | .Fl s , 357 | .Fl u , 358 | and 359 | .Fl v 360 | options are 361 | .Fx 2.2 362 | extensions. 363 | .Sh BUGS 364 | The ability to log messages received in UDP packets is equivalent to 365 | an unauthenticated remote disk-filling service, and should probably be 366 | disabled by default. 367 | Some sort of 368 | .No inter- Ns Nm syslogd 369 | authentication mechanism ought to be worked out. 370 | To prevent the worst 371 | abuse, use of the 372 | .Fl a 373 | option is therefore highly recommended. 374 | .Pp 375 | The 376 | .Fl a 377 | matching algorithm does not pretend to be very efficient; use of numeric 378 | IP addresses is faster than domain name comparison. 379 | Since the allowed 380 | peer list is being walked linearly, peer groups where frequent messages 381 | are being anticipated from should be put early into the 382 | .Fl a 383 | list. 384 | .Pp 385 | The log socket was moved from 386 | .Pa /dev 387 | to ease the use of a read-only root file system. 388 | This may confuse 389 | some old binaries so that a symbolic link might be used for a 390 | transitional period. 391 | -------------------------------------------------------------------------------- /src/syslog.conf.5: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1990, 1991, 1993 2 | .\" The Regents of the University of California. All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 4. Neither the name of the University nor the names of its contributors 13 | .\" may be used to endorse or promote products derived from this software 14 | .\" without specific prior written permission. 15 | .\" 16 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | .\" SUCH DAMAGE. 27 | .\" 28 | .\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93 29 | .\" $FreeBSD: src/usr.sbin/syslogd/syslog.conf.5,v 1.47.2.1 2009/08/03 08:13:06 kensmith Exp $ 30 | .\" 31 | .Dd December 23, 2008 32 | .Dt SYSLOG.CONF 5 33 | .Os 34 | .Sh NAME 35 | .Nm syslog.conf 36 | .Nd 37 | .Xr syslogd 8 38 | configuration file 39 | .Sh DESCRIPTION 40 | The 41 | .Nm 42 | file is the configuration file for the 43 | .Xr syslogd 8 44 | program. 45 | It consists of 46 | blocks of lines separated by 47 | .Em program 48 | and 49 | .Em hostname 50 | specifications (separations appear alone on their lines), 51 | with each line containing two fields: the 52 | .Em selector 53 | field which specifies the types of messages and priorities to which the 54 | line applies, and an 55 | .Em action 56 | field which specifies the action to be taken if a message 57 | .Xr syslogd 8 58 | receives matches the selection criteria. 59 | The 60 | .Em selector 61 | field is separated from the 62 | .Em action 63 | field by one or more tab characters or spaces. 64 | .Pp 65 | Note that if you use spaces as separators, your 66 | .Nm 67 | might be incompatible with other Unices or Unix-like systems. 68 | This functionality was added for ease of configuration 69 | (e.g.\& it is possible to cut-and-paste into 70 | .Nm ) , 71 | and to avoid possible mistakes. 72 | This change however preserves 73 | backwards compatibility with the old style of 74 | .Nm 75 | (i.e., tab characters only). 76 | .Pp 77 | The 78 | .Em selectors 79 | are encoded as a 80 | .Em facility , 81 | a period 82 | .Pq Dq \&. , 83 | an optional set of comparison flags 84 | .Pq Oo \&! Oc Op <=> , 85 | and a 86 | .Em level , 87 | with no intervening white-space. 88 | Both the 89 | .Em facility 90 | and the 91 | .Em level 92 | are case insensitive. 93 | .Pp 94 | The 95 | .Em facility 96 | describes the part of the system generating the message, and is one of 97 | the following keywords: 98 | .Cm auth , authpriv , console , cron , daemon , ftp , kern , lpr , 99 | .Cm mail , mark , news , ntp , security , syslog , user , uucp , 100 | and 101 | .Cm local0 102 | through 103 | .Cm local7 . 104 | These keywords (with the exception of mark) correspond to 105 | similar 106 | .Dq Dv LOG_ 107 | values specified to the 108 | .Xr openlog 3 109 | and 110 | .Xr syslog 3 111 | library routines. 112 | .Pp 113 | The 114 | .Em comparison flags 115 | may be used to specify exactly what is logged. 116 | The default comparison is 117 | .Dq => 118 | (or, if you prefer, 119 | .Dq >= ) , 120 | which means that messages from the specified 121 | .Em facility 122 | list, and of a priority 123 | level equal to or greater than 124 | .Em level 125 | will be logged. 126 | Comparison flags beginning with 127 | .Dq Li \&! 128 | will have their logical sense inverted. 129 | Thus 130 | .Dq !=info 131 | means all levels except info and 132 | .Dq !notice 133 | has the same meaning as 134 | .Dq 46 | __FBSDID("$FreeBSD: src/usr.sbin/syslogd/syslogd.c,v 1.163.2.2 2009/12/19 19:35:53 attilio Exp $"); 47 | 48 | /* 49 | * syslogd -- log system messages 50 | * 51 | * This program implements a system log. It takes a series of lines. 52 | * Each line may have a priority, signified as "" as 53 | * the first characters of the line. If this is 54 | * not present, a default priority is used. 55 | * 56 | * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will 57 | * cause it to reread its configuration file. 58 | * 59 | * Defined Constants: 60 | * 61 | * MAXLINE -- the maximum line length that can be handled. 62 | * DEFUPRI -- the default priority for user messages 63 | * DEFSPRI -- the default priority for kernel messages 64 | * 65 | * Author: Eric Allman 66 | * extensive changes by Ralph Campbell 67 | * more extensive changes by Eric Allman (again) 68 | * Extension to log by program name as well as facility and priority 69 | * by Peter da Silva. 70 | * -u and -v by Harlan Stenn. 71 | * Priority comparison code by Harlan Stenn. 72 | */ 73 | 74 | #define MAXLINE 1024 /* maximum line length */ 75 | #define MAXSVLINE 120 /* maximum saved line length */ 76 | #define DEFUPRI (LOG_USER|LOG_NOTICE) 77 | #define DEFSPRI (LOG_KERN|LOG_CRIT) 78 | #define TIMERINTVL 30 /* interval for checking flush, mark */ 79 | #define TTYMSGTIME 1 /* timeout passed to ttymsg */ 80 | 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | 95 | #include 96 | #include 97 | #include 98 | 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | #include 108 | #include 109 | #include 110 | #include 111 | #include 112 | #include 113 | 114 | #include 115 | 116 | #include "pathnames.h" 117 | #include "ttymsg.h" 118 | 119 | #define SYSLOG_NAMES 120 | #include 121 | 122 | const char *ConfFile = _PATH_LOGCONF; 123 | const char *PidFile = _PATH_LOGPID; 124 | const char ctty[] = _PATH_CONSOLE; 125 | 126 | #define dprintf if (Debug) printf 127 | 128 | #define MAXUNAMES 20 /* maximum number of user names */ 129 | 130 | Tcl_Interp *interp; 131 | 132 | /* 133 | * Unix sockets. 134 | * We have two default sockets, one with 666 permissions, 135 | * and one for privileged programs. 136 | */ 137 | struct funix { 138 | int s; 139 | const char *name; 140 | mode_t mode; 141 | STAILQ_ENTRY(funix) next; 142 | }; 143 | struct funix funix_secure = { -1, _PATH_LOG_PRIV, S_IRUSR | S_IWUSR, 144 | { NULL } }; 145 | struct funix funix_default = { -1, _PATH_LOG, DEFFILEMODE, 146 | { &funix_secure } }; 147 | 148 | STAILQ_HEAD(, funix) funixes = { &funix_default, 149 | &(funix_secure.next.stqe_next) }; 150 | 151 | /* 152 | * Flags to logmsg(). 153 | */ 154 | 155 | #define IGN_CONS 0x001 /* don't print on console */ 156 | #define SYNC_FILE 0x002 /* do fsync on file after printing */ 157 | #define ADDDATE 0x004 /* add a date to the message */ 158 | #define MARK 0x008 /* this message is a mark */ 159 | #define ISKERNEL 0x010 /* kernel generated message */ 160 | 161 | /* 162 | * This structure represents the files that will have log 163 | * copies printed. 164 | * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY 165 | * or if f_type if F_PIPE and f_pid > 0. 166 | */ 167 | 168 | struct filed { 169 | struct filed *f_next; /* next in linked list */ 170 | short f_type; /* entry type, see below */ 171 | short f_file; /* file descriptor */ 172 | time_t f_time; /* time this was last written */ 173 | char *f_host; /* host from which to recd. */ 174 | u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */ 175 | u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */ 176 | #define PRI_LT 0x1 177 | #define PRI_EQ 0x2 178 | #define PRI_GT 0x4 179 | char *f_program; /* program this applies to */ 180 | union { 181 | char f_uname[MAXUNAMES][UT_NAMESIZE+1]; 182 | struct { 183 | char f_hname[MAXHOSTNAMELEN]; 184 | struct addrinfo *f_addr; 185 | 186 | } f_forw; /* forwarding address */ 187 | char f_fname[MAXPATHLEN]; 188 | struct { 189 | char f_pname[MAXPATHLEN]; 190 | pid_t f_pid; 191 | } f_pipe; 192 | } f_un; 193 | char f_prevline[MAXSVLINE]; /* last message logged */ 194 | char f_lasttime[16]; /* time of last occurrence */ 195 | char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */ 196 | int f_prevpri; /* pri of f_prevline */ 197 | int f_prevlen; /* length of f_prevline */ 198 | int f_prevcount; /* repetition cnt of prevline */ 199 | u_int f_repeatcount; /* number of "repeated" msgs */ 200 | int f_flags; /* file-specific flags */ 201 | #define FFLAG_SYNC 0x01 202 | #define FFLAG_NEEDSYNC 0x02 203 | }; 204 | 205 | /* 206 | * Queue of about-to-be dead processes we should watch out for. 207 | */ 208 | 209 | TAILQ_HEAD(stailhead, deadq_entry) deadq_head; 210 | struct stailhead *deadq_headp; 211 | 212 | struct deadq_entry { 213 | pid_t dq_pid; 214 | int dq_timeout; 215 | TAILQ_ENTRY(deadq_entry) dq_entries; 216 | }; 217 | 218 | /* 219 | * The timeout to apply to processes waiting on the dead queue. Unit 220 | * of measure is `mark intervals', i.e. 20 minutes by default. 221 | * Processes on the dead queue will be terminated after that time. 222 | */ 223 | 224 | #define DQ_TIMO_INIT 2 225 | 226 | typedef struct deadq_entry *dq_t; 227 | 228 | 229 | /* 230 | * Struct to hold records of network addresses that are allowed to log 231 | * to us. 232 | */ 233 | struct allowedpeer { 234 | int isnumeric; 235 | u_short port; 236 | union { 237 | struct { 238 | struct sockaddr_storage addr; 239 | struct sockaddr_storage mask; 240 | } numeric; 241 | char *name; 242 | } u; 243 | #define a_addr u.numeric.addr 244 | #define a_mask u.numeric.mask 245 | #define a_name u.name 246 | }; 247 | 248 | 249 | /* 250 | * Intervals at which we flush out "message repeated" messages, 251 | * in seconds after previous message is logged. After each flush, 252 | * we move to the next interval until we reach the largest. 253 | */ 254 | int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */ 255 | #define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1) 256 | #define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) 257 | #define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ 258 | (f)->f_repeatcount = MAXREPEAT; \ 259 | } 260 | 261 | /* values for f_type */ 262 | #define F_UNUSED 0 /* unused entry */ 263 | #define F_FILE 1 /* regular file */ 264 | #define F_TTY 2 /* terminal */ 265 | #define F_CONSOLE 3 /* console terminal */ 266 | #define F_FORW 4 /* remote machine */ 267 | #define F_USERS 5 /* list of users */ 268 | #define F_WALL 6 /* everyone logged on */ 269 | #define F_PIPE 7 /* pipe to program */ 270 | 271 | const char *TypeNames[8] = { 272 | "UNUSED", "FILE", "TTY", "CONSOLE", 273 | "FORW", "USERS", "WALL", "PIPE" 274 | }; 275 | 276 | static struct filed *Files; /* Log files that we write to */ 277 | static struct filed consfile; /* Console */ 278 | 279 | static int Debug; /* debug flag */ 280 | static int resolve = 1; /* resolve hostname */ 281 | static char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */ 282 | static const char *LocalDomain; /* our local domain name */ 283 | static int *finet; /* Internet datagram socket */ 284 | static int fklog = -1; /* /dev/klog */ 285 | static int Initialized; /* set when we have initialized ourselves */ 286 | static int MarkInterval = 20 * 60; /* interval between marks in seconds */ 287 | static int MarkSeq; /* mark sequence number */ 288 | static int SecureMode; /* when true, receive only unix domain socks */ 289 | #ifdef INET6 290 | static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ 291 | #else 292 | static int family = PF_INET; /* protocol family (IPv4 only) */ 293 | #endif 294 | static int mask_C1 = 1; /* mask characters from 0x80 - 0x9F */ 295 | static int send_to_all; /* send message to all IPv4/IPv6 addresses */ 296 | static int use_bootfile; /* log entire bootfile for every kern msg */ 297 | static int no_compress; /* don't compress messages (1=pipes, 2=all) */ 298 | static int logflags = O_WRONLY|O_APPEND; /* flags used to open log files */ 299 | 300 | static char bootfile[MAXLINE+1]; /* booted kernel file */ 301 | 302 | struct allowedpeer *AllowedPeers; /* List of allowed peers */ 303 | static int NumAllowed; /* Number of entries in AllowedPeers */ 304 | static int RemoteAddDate; /* Always set the date on remote messages */ 305 | 306 | static int UniquePriority; /* Only log specified priority? */ 307 | static int LogFacPri; /* Put facility and priority in log message: */ 308 | /* 0=no, 1=numeric, 2=names */ 309 | static int KeepKernFac; /* Keep remotely logged kernel facility */ 310 | static int needdofsync = 0; /* Are any file(s) waiting to be fsynced? */ 311 | static struct pidfh *pfh; 312 | 313 | volatile sig_atomic_t MarkSet, WantDie; 314 | 315 | static int allowaddr(char *); 316 | static void cfline(const char *, struct filed *, 317 | const char *, const char *); 318 | static const char *cvthname(struct sockaddr *); 319 | static void deadq_enter(pid_t, const char *); 320 | static int deadq_remove(pid_t); 321 | static int decode(const char *, CODE *); 322 | static void die(int); 323 | static void dodie(int); 324 | static void dofsync(void); 325 | static void domark(int); 326 | static void fprintlog(struct filed *, int, const char *); 327 | static int *socksetup(int, const char *); 328 | static void init(int); 329 | static void logerror(const char *); 330 | static void logmsg(int, const char *, const char *, int); 331 | static void log_deadchild(pid_t, int, const char *); 332 | static void markit(void); 333 | static int skip_message(const char *, const char *, int); 334 | static void printline(const char *, char *, int); 335 | static void printsys(char *); 336 | static int p_open(const char *, pid_t *); 337 | static void readklog(void); 338 | static void reapchild(int); 339 | static void usage(void); 340 | static int validate(struct sockaddr *, const char *); 341 | static void unmapped(struct sockaddr *); 342 | static void wallmsg(struct filed *, struct iovec *, const int iovlen); 343 | static int waitdaemon(int, int, int); 344 | static void timedout(int); 345 | static void double_rbuf(int); 346 | 347 | static void init_tcl(void); 348 | static void call_tcl (void); 349 | static void tcl_tick (void); 350 | 351 | static void 352 | init_tcl (void) 353 | { 354 | interp = Tcl_CreateInterp(); 355 | if (Tcl_Init (interp) == TCL_ERROR) { 356 | dprintf("tcl init failed: %s\n", Tcl_GetStringResult (interp)); 357 | return; 358 | } 359 | 360 | if (Tcl_Eval (interp, "source "_PATH_TCLCONF) == TCL_ERROR) { 361 | dprintf("tcl startup failed: %s\n", Tcl_GetStringResult (interp)); 362 | } 363 | } 364 | 365 | int 366 | main(int argc, char *argv[]) 367 | { 368 | int ch, i, fdsrmax = 0, l; 369 | struct sockaddr_un sunx, fromunix; 370 | struct sockaddr_storage frominet; 371 | fd_set *fdsr = NULL; 372 | char line[MAXLINE + 1]; 373 | const char *bindhostname, *hname; 374 | struct timeval tv, *tvp; 375 | struct sigaction sact; 376 | struct funix *fx, *fx1; 377 | sigset_t mask; 378 | pid_t ppid = 1, spid; 379 | socklen_t len; 380 | 381 | if (madvise(NULL, 0, MADV_PROTECT) != 0) 382 | dprintf("madvise() failed: %s\n", strerror(errno)); 383 | 384 | init_tcl (); 385 | 386 | bindhostname = NULL; 387 | while ((ch = getopt(argc, argv, "468Aa:b:cCdf:kl:m:nop:P:sS:Tuv")) 388 | != -1) 389 | switch (ch) { 390 | case '4': 391 | family = PF_INET; 392 | break; 393 | #ifdef INET6 394 | case '6': 395 | family = PF_INET6; 396 | break; 397 | #endif 398 | case '8': 399 | mask_C1 = 0; 400 | break; 401 | case 'A': 402 | send_to_all++; 403 | break; 404 | case 'a': /* allow specific network addresses only */ 405 | if (allowaddr(optarg) == -1) 406 | usage(); 407 | break; 408 | case 'b': 409 | bindhostname = optarg; 410 | break; 411 | case 'c': 412 | no_compress++; 413 | break; 414 | case 'C': 415 | logflags |= O_CREAT; 416 | break; 417 | case 'd': /* debug */ 418 | Debug++; 419 | break; 420 | case 'f': /* configuration file */ 421 | ConfFile = optarg; 422 | break; 423 | case 'k': /* keep remote kern fac */ 424 | KeepKernFac = 1; 425 | break; 426 | case 'l': 427 | { 428 | long perml; 429 | mode_t mode; 430 | char *name, *ep; 431 | 432 | if (optarg[0] == '/') { 433 | mode = DEFFILEMODE; 434 | name = optarg; 435 | } else if ((name = strchr(optarg, ':')) != NULL) { 436 | *name++ = '\0'; 437 | if (name[0] != '/') 438 | errx(1, "socket name must be absolute " 439 | "path"); 440 | if (isdigit(*optarg)) { 441 | perml = strtol(optarg, &ep, 8); 442 | if (*ep || perml < 0 || 443 | perml & ~(S_IRWXU|S_IRWXG|S_IRWXO)) 444 | errx(1, "invalid mode %s, exiting", 445 | optarg); 446 | mode = (mode_t )perml; 447 | } else 448 | errx(1, "invalid mode %s, exiting", 449 | optarg); 450 | } else /* doesn't begin with '/', and no ':' */ 451 | errx(1, "can't parse path %s", optarg); 452 | 453 | if (strlen(name) >= sizeof(sunx.sun_path)) 454 | errx(1, "%s path too long, exiting", name); 455 | if ((fx = malloc(sizeof(struct funix))) == NULL) 456 | errx(1, "malloc failed"); 457 | fx->s = -1; 458 | fx->name = name; 459 | fx->mode = mode; 460 | STAILQ_INSERT_TAIL(&funixes, fx, next); 461 | break; 462 | } 463 | case 'm': /* mark interval */ 464 | MarkInterval = atoi(optarg) * 60; 465 | break; 466 | case 'n': 467 | resolve = 0; 468 | break; 469 | case 'o': 470 | use_bootfile = 1; 471 | break; 472 | case 'p': /* path */ 473 | if (strlen(optarg) >= sizeof(sunx.sun_path)) 474 | errx(1, "%s path too long, exiting", optarg); 475 | funix_default.name = optarg; 476 | break; 477 | case 'P': /* path for alt. PID */ 478 | PidFile = optarg; 479 | break; 480 | case 's': /* no network mode */ 481 | SecureMode++; 482 | break; 483 | case 'S': /* path for privileged originator */ 484 | if (strlen(optarg) >= sizeof(sunx.sun_path)) 485 | errx(1, "%s path too long, exiting", optarg); 486 | funix_secure.name = optarg; 487 | break; 488 | case 'T': 489 | RemoteAddDate = 1; 490 | break; 491 | case 'u': /* only log specified priority */ 492 | UniquePriority++; 493 | break; 494 | case 'v': /* log facility and priority */ 495 | LogFacPri++; 496 | break; 497 | default: 498 | usage(); 499 | } 500 | if ((argc -= optind) != 0) 501 | usage(); 502 | 503 | pfh = pidfile_open(PidFile, 0600, &spid); 504 | if (pfh == NULL) { 505 | if (errno == EEXIST) 506 | errx(1, "syslogd already running, pid: %d", spid); 507 | warn("cannot open pid file"); 508 | } 509 | 510 | if (!Debug) { 511 | ppid = waitdaemon(0, 0, 30); 512 | if (ppid < 0) { 513 | warn("could not become daemon"); 514 | pidfile_remove(pfh); 515 | exit(1); 516 | } 517 | } else { 518 | setlinebuf(stdout); 519 | } 520 | 521 | if (NumAllowed) 522 | endservent(); 523 | 524 | consfile.f_type = F_CONSOLE; 525 | (void)strlcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1, 526 | sizeof(consfile.f_un.f_fname)); 527 | (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile)); 528 | (void)signal(SIGTERM, dodie); 529 | (void)signal(SIGINT, Debug ? dodie : SIG_IGN); 530 | (void)signal(SIGQUIT, Debug ? dodie : SIG_IGN); 531 | /* 532 | * We don't want the SIGCHLD and SIGHUP handlers to interfere 533 | * with each other; they are likely candidates for being called 534 | * simultaneously (SIGHUP closes pipe descriptor, process dies, 535 | * SIGCHLD happens). 536 | */ 537 | sigemptyset(&mask); 538 | sigaddset(&mask, SIGHUP); 539 | sact.sa_handler = reapchild; 540 | sact.sa_mask = mask; 541 | sact.sa_flags = SA_RESTART; 542 | (void)sigaction(SIGCHLD, &sact, NULL); 543 | (void)signal(SIGALRM, domark); 544 | (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */ 545 | (void)alarm(TIMERINTVL); 546 | 547 | TAILQ_INIT(&deadq_head); 548 | 549 | #ifndef SUN_LEN 550 | #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 551 | #endif 552 | STAILQ_FOREACH_SAFE(fx, &funixes, next, fx1) { 553 | (void)unlink(fx->name); 554 | memset(&sunx, 0, sizeof(sunx)); 555 | sunx.sun_family = AF_LOCAL; 556 | (void)strlcpy(sunx.sun_path, fx->name, sizeof(sunx.sun_path)); 557 | fx->s = socket(PF_LOCAL, SOCK_DGRAM, 0); 558 | if (fx->s < 0 || 559 | bind(fx->s, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 || 560 | chmod(fx->name, fx->mode) < 0) { 561 | (void)snprintf(line, sizeof line, 562 | "cannot create %s", fx->name); 563 | logerror(line); 564 | dprintf("cannot create %s (%d)\n", fx->name, errno); 565 | if (fx == &funix_default || fx == &funix_secure) 566 | die(0); 567 | else { 568 | STAILQ_REMOVE(&funixes, fx, funix, next); 569 | continue; 570 | } 571 | double_rbuf(fx->s); 572 | } 573 | } 574 | if (SecureMode <= 1) 575 | finet = socksetup(family, bindhostname); 576 | 577 | if (finet) { 578 | if (SecureMode) { 579 | for (i = 0; i < *finet; i++) { 580 | if (shutdown(finet[i+1], SHUT_RD) < 0) { 581 | logerror("shutdown"); 582 | if (!Debug) 583 | die(0); 584 | } 585 | } 586 | } else { 587 | dprintf("listening on inet and/or inet6 socket\n"); 588 | } 589 | dprintf("sending on inet and/or inet6 socket\n"); 590 | } 591 | 592 | if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0) 593 | if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0) 594 | fklog = -1; 595 | if (fklog < 0) 596 | dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); 597 | 598 | /* tuck my process id away */ 599 | pidfile_write(pfh); 600 | 601 | dprintf("off & running....\n"); 602 | 603 | init(0); 604 | /* prevent SIGHUP and SIGCHLD handlers from running in parallel */ 605 | sigemptyset(&mask); 606 | sigaddset(&mask, SIGCHLD); 607 | sact.sa_handler = init; 608 | sact.sa_mask = mask; 609 | sact.sa_flags = SA_RESTART; 610 | (void)sigaction(SIGHUP, &sact, NULL); 611 | 612 | // we set tv_usec to 50000 and prevent select from being called 613 | // with a null timeval so that we can invoke a "tick" proc in 614 | // the tcl interpreter 20 times a second. this allows tcl to 615 | // invoke update or whatever, keeping its event loop, sockets, 616 | // etc, alive 617 | tvp = &tv; 618 | tv.tv_sec = 0; 619 | tv.tv_usec = 50000; 620 | 621 | if (fklog != -1 && fklog > fdsrmax) 622 | fdsrmax = fklog; 623 | if (finet && !SecureMode) { 624 | for (i = 0; i < *finet; i++) { 625 | if (finet[i+1] != -1 && finet[i+1] > fdsrmax) 626 | fdsrmax = finet[i+1]; 627 | } 628 | } 629 | STAILQ_FOREACH(fx, &funixes, next) 630 | if (fx->s > fdsrmax) 631 | fdsrmax = fx->s; 632 | 633 | fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS), 634 | sizeof(fd_mask)); 635 | if (fdsr == NULL) 636 | errx(1, "calloc fd_set"); 637 | 638 | for (;;) { 639 | if (MarkSet) 640 | markit(); 641 | if (WantDie) 642 | die(WantDie); 643 | 644 | bzero(fdsr, howmany(fdsrmax+1, NFDBITS) * 645 | sizeof(fd_mask)); 646 | 647 | if (fklog != -1) 648 | FD_SET(fklog, fdsr); 649 | if (finet && !SecureMode) { 650 | for (i = 0; i < *finet; i++) { 651 | if (finet[i+1] != -1) 652 | FD_SET(finet[i+1], fdsr); 653 | } 654 | } 655 | STAILQ_FOREACH(fx, &funixes, next) 656 | FD_SET(fx->s, fdsr); 657 | 658 | i = select(fdsrmax+1, fdsr, NULL, NULL, 659 | &tv); 660 | tcl_tick(); 661 | switch (i) { 662 | case 0: 663 | dofsync(); 664 | needdofsync = 0; 665 | if (tvp) { 666 | tvp = NULL; 667 | if (ppid != 1) 668 | kill(ppid, SIGALRM); 669 | } 670 | continue; 671 | case -1: 672 | if (errno != EINTR) 673 | logerror("select"); 674 | continue; 675 | } 676 | if (fklog != -1 && FD_ISSET(fklog, fdsr)) 677 | readklog(); 678 | if (finet && !SecureMode) { 679 | for (i = 0; i < *finet; i++) { 680 | if (FD_ISSET(finet[i+1], fdsr)) { 681 | len = sizeof(frominet); 682 | l = recvfrom(finet[i+1], line, MAXLINE, 683 | 0, (struct sockaddr *)&frominet, 684 | &len); 685 | if (l > 0) { 686 | line[l] = '\0'; 687 | hname = cvthname((struct sockaddr *)&frominet); 688 | unmapped((struct sockaddr *)&frominet); 689 | if (validate((struct sockaddr *)&frominet, hname)) 690 | printline(hname, line, RemoteAddDate ? ADDDATE : 0); 691 | } else if (l < 0 && errno != EINTR) 692 | logerror("recvfrom inet"); 693 | } 694 | } 695 | } 696 | STAILQ_FOREACH(fx, &funixes, next) { 697 | if (FD_ISSET(fx->s, fdsr)) { 698 | len = sizeof(fromunix); 699 | l = recvfrom(fx->s, line, MAXLINE, 0, 700 | (struct sockaddr *)&fromunix, &len); 701 | if (l > 0) { 702 | line[l] = '\0'; 703 | printline(LocalHostName, line, 0); 704 | } else if (l < 0 && errno != EINTR) 705 | logerror("recvfrom unix"); 706 | } 707 | } 708 | } 709 | if (fdsr) 710 | free(fdsr); 711 | } 712 | 713 | static void 714 | unmapped(struct sockaddr *sa) 715 | { 716 | struct sockaddr_in6 *sin6; 717 | struct sockaddr_in sin4; 718 | 719 | if (sa->sa_family != AF_INET6) 720 | return; 721 | if (sa->sa_len != sizeof(struct sockaddr_in6) || 722 | sizeof(sin4) > sa->sa_len) 723 | return; 724 | sin6 = (struct sockaddr_in6 *)sa; 725 | if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 726 | return; 727 | 728 | memset(&sin4, 0, sizeof(sin4)); 729 | sin4.sin_family = AF_INET; 730 | sin4.sin_len = sizeof(struct sockaddr_in); 731 | memcpy(&sin4.sin_addr, &sin6->sin6_addr.s6_addr[12], 732 | sizeof(sin4.sin_addr)); 733 | sin4.sin_port = sin6->sin6_port; 734 | 735 | memcpy(sa, &sin4, sin4.sin_len); 736 | } 737 | 738 | static void 739 | usage(void) 740 | { 741 | 742 | fprintf(stderr, "%s\n%s\n%s\n%s\n", 743 | "usage: syslogd [-468ACcdknosTuv] [-a allowed_peer]", 744 | " [-b bind_address] [-f config_file]", 745 | " [-l [mode:]path] [-m mark_interval]", 746 | " [-P pid_file] [-p log_socket]"); 747 | exit(1); 748 | } 749 | 750 | static void 751 | set_tcl_var(const char *varName, Tcl_Obj *value) { 752 | if (Tcl_SetVar2Ex (interp, "message", varName, value, TCL_LEAVE_ERR_MSG) == NULL) { 753 | dprintf("tcl set var for %s failed: %s\n", varName, Tcl_GetStringResult (interp)); 754 | } 755 | } 756 | 757 | static void 758 | call_tcl () { 759 | if (Tcl_Eval (interp, "syslog message") == TCL_ERROR) { 760 | dprintf("tcl eval failed: %s\n", Tcl_GetStringResult (interp)); 761 | } 762 | 763 | Tcl_UnsetVar (interp, "message", 0); 764 | } 765 | 766 | static void 767 | tcl_tick () { 768 | while (Tcl_DoOneEvent (TCL_DONT_WAIT)) continue; 769 | } 770 | 771 | /* 772 | * Take a raw input line, decode the message, and print the message 773 | * on the appropriate log files. 774 | */ 775 | static void 776 | printline(const char *hname, char *msg, int flags) 777 | { 778 | char *p, *q; 779 | long n; 780 | int c, pri; 781 | char line[MAXLINE + 1]; 782 | 783 | /* test for special codes */ 784 | p = msg; 785 | pri = DEFUPRI; 786 | if (*p == '<') { 787 | errno = 0; 788 | n = strtol(p + 1, &q, 10); 789 | if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { 790 | p = q + 1; 791 | pri = n; 792 | } 793 | } 794 | if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) 795 | pri = DEFUPRI; 796 | 797 | /* 798 | * Don't allow users to log kernel messages. 799 | * NOTE: since LOG_KERN == 0 this will also match 800 | * messages with no facility specified. 801 | */ 802 | if ((pri & LOG_FACMASK) == LOG_KERN && !KeepKernFac) 803 | pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); 804 | 805 | q = line; 806 | 807 | while ((c = (unsigned char)*p++) != '\0' && 808 | q < &line[sizeof(line) - 4]) { 809 | if (mask_C1 && (c & 0x80) && c < 0xA0) { 810 | c &= 0x7F; 811 | *q++ = 'M'; 812 | *q++ = '-'; 813 | } 814 | if (isascii(c) && iscntrl(c)) { 815 | if (c == '\n') { 816 | *q++ = ' '; 817 | } else if (c == '\t') { 818 | *q++ = '\t'; 819 | } else { 820 | *q++ = '^'; 821 | *q++ = c ^ 0100; 822 | } 823 | } else { 824 | *q++ = c; 825 | } 826 | } 827 | *q = '\0'; 828 | 829 | logmsg(pri, line, hname, flags); 830 | } 831 | 832 | /* 833 | * Read /dev/klog while data are available, split into lines. 834 | */ 835 | static void 836 | readklog(void) 837 | { 838 | char *p, *q, line[MAXLINE + 1]; 839 | int len, i; 840 | 841 | len = 0; 842 | for (;;) { 843 | i = read(fklog, line + len, MAXLINE - 1 - len); 844 | if (i > 0) { 845 | line[i + len] = '\0'; 846 | } else { 847 | if (i < 0 && errno != EINTR && errno != EAGAIN) { 848 | logerror("klog"); 849 | fklog = -1; 850 | } 851 | break; 852 | } 853 | 854 | for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { 855 | *q = '\0'; 856 | printsys(p); 857 | } 858 | len = strlen(p); 859 | if (len >= MAXLINE - 1) { 860 | printsys(p); 861 | len = 0; 862 | } 863 | if (len > 0) 864 | memmove(line, p, len + 1); 865 | } 866 | if (len > 0) 867 | printsys(line); 868 | } 869 | 870 | /* 871 | * Take a raw input line from /dev/klog, format similar to syslog(). 872 | */ 873 | static void 874 | printsys(char *msg) 875 | { 876 | char *p, *q; 877 | long n; 878 | int flags, isprintf, pri; 879 | 880 | flags = ISKERNEL | SYNC_FILE | ADDDATE; /* fsync after write */ 881 | p = msg; 882 | pri = DEFSPRI; 883 | isprintf = 1; 884 | if (*p == '<') { 885 | errno = 0; 886 | n = strtol(p + 1, &q, 10); 887 | if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { 888 | p = q + 1; 889 | pri = n; 890 | isprintf = 0; 891 | } 892 | } 893 | /* 894 | * Kernel printf's and LOG_CONSOLE messages have been displayed 895 | * on the console already. 896 | */ 897 | if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE) 898 | flags |= IGN_CONS; 899 | if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) 900 | pri = DEFSPRI; 901 | logmsg(pri, p, LocalHostName, flags); 902 | } 903 | 904 | static time_t now; 905 | 906 | /* 907 | * Match a program or host name against a specification. 908 | * Return a non-0 value if the message must be ignored 909 | * based on the specification. 910 | */ 911 | static int 912 | skip_message(const char *name, const char *spec, int checkcase) 913 | { 914 | const char *s; 915 | char prev, next; 916 | int exclude = 0; 917 | /* Behaviour on explicit match */ 918 | 919 | if (spec == NULL) 920 | return 0; 921 | switch (*spec) { 922 | case '-': 923 | exclude = 1; 924 | /*FALLTHROUGH*/ 925 | case '+': 926 | spec++; 927 | break; 928 | default: 929 | break; 930 | } 931 | if (checkcase) 932 | s = strstr (spec, name); 933 | else 934 | s = strcasestr (spec, name); 935 | 936 | if (s != NULL) { 937 | prev = (s == spec ? ',' : *(s - 1)); 938 | next = *(s + strlen (name)); 939 | 940 | if (prev == ',' && (next == '\0' || next == ',')) 941 | /* Explicit match: skip iff the spec is an 942 | exclusive one. */ 943 | return exclude; 944 | } 945 | 946 | /* No explicit match for this name: skip the message iff 947 | the spec is an inclusive one. */ 948 | return !exclude; 949 | } 950 | 951 | /* 952 | * Log a message to the appropriate log files, users, etc. based on 953 | * the priority. 954 | */ 955 | static void 956 | logmsg(int pri, const char *msg, const char *from, int flags) 957 | { 958 | struct filed *f; 959 | int i, fac, maskedfac, msglen, omask, prilev; 960 | const char *timestamp; 961 | char prog[NAME_MAX+1]; 962 | char buf[MAXLINE+1]; 963 | CODE *c; 964 | 965 | dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n", 966 | pri, flags, from, msg); 967 | 968 | omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM)); 969 | 970 | /* 971 | * Check to see if msg looks non-standard. 972 | */ 973 | msglen = strlen(msg); 974 | if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' || 975 | msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') 976 | flags |= ADDDATE; 977 | 978 | (void)time(&now); 979 | if (flags & ADDDATE) { 980 | timestamp = ctime(&now) + 4; 981 | } else { 982 | timestamp = msg; 983 | msg += 16; 984 | msglen -= 16; 985 | } 986 | 987 | set_tcl_var ("clock", Tcl_NewLongObj (now)); 988 | set_tcl_var ("timestamp", Tcl_NewStringObj (timestamp, sizeof(f->f_lasttime))); 989 | set_tcl_var ("host", Tcl_NewStringObj (from, -1)); 990 | 991 | /* skip leading blanks */ 992 | while (isspace(*msg)) { 993 | msg++; 994 | msglen--; 995 | } 996 | 997 | /* extract facility and priority level */ 998 | if (flags & MARK) 999 | fac = LOG_NFACILITIES; 1000 | else 1001 | fac = LOG_FAC(pri); 1002 | 1003 | /* Check maximum facility number. */ 1004 | if (fac > LOG_NFACILITIES) { 1005 | (void)sigsetmask(omask); 1006 | return; 1007 | } 1008 | 1009 | maskedfac = pri & LOG_FACMASK; 1010 | /* get the facility name if possible and tell Tcl about it */ 1011 | for (c = facilitynames; c->c_name; c++) { 1012 | if (c->c_val == maskedfac) { 1013 | break; 1014 | } 1015 | } 1016 | 1017 | if (c->c_name == NULL) { 1018 | set_tcl_var ("facility", Tcl_NewIntObj (fac)); 1019 | } else { 1020 | set_tcl_var ("facility", Tcl_NewStringObj (c->c_name, -1)); 1021 | } 1022 | 1023 | /* get the privilege level name if possible and tell Tcl about it */ 1024 | prilev = LOG_PRI(pri); 1025 | 1026 | for (c = prioritynames; c->c_name; c++) { 1027 | if (c->c_val == prilev) { 1028 | break; 1029 | } 1030 | } 1031 | 1032 | if (c->c_name == NULL) { 1033 | set_tcl_var ("priority", Tcl_NewIntObj (prilev)); 1034 | } else { 1035 | set_tcl_var ("priority", Tcl_NewStringObj (c->c_name, -1)); 1036 | } 1037 | 1038 | /* extract program name */ 1039 | for (i = 0; i < NAME_MAX; i++) { 1040 | if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[' || 1041 | msg[i] == '/' || isspace(msg[i])) 1042 | break; 1043 | prog[i] = msg[i]; 1044 | } 1045 | prog[i] = 0; 1046 | 1047 | set_tcl_var ("program", Tcl_NewStringObj (prog, -1)); 1048 | 1049 | /* add kernel prefix for kernel messages */ 1050 | if (flags & ISKERNEL) { 1051 | snprintf(buf, sizeof(buf), "%s: %s", 1052 | use_bootfile ? bootfile : "kernel", msg); 1053 | msg = buf; 1054 | msglen = strlen(buf); 1055 | 1056 | set_tcl_var ("kernel", Tcl_NewStringObj (bootfile, -1)); 1057 | } 1058 | 1059 | set_tcl_var ("msg", Tcl_NewStringObj (msg, -1)); 1060 | 1061 | call_tcl (); 1062 | 1063 | /* log the message to the particular outputs */ 1064 | if (!Initialized) { 1065 | f = &consfile; 1066 | /* 1067 | * Open in non-blocking mode to avoid hangs during open 1068 | * and close(waiting for the port to drain). 1069 | */ 1070 | f->f_file = open(ctty, O_WRONLY | O_NONBLOCK, 0); 1071 | 1072 | if (f->f_file >= 0) { 1073 | (void)strlcpy(f->f_lasttime, timestamp, 1074 | sizeof(f->f_lasttime)); 1075 | fprintlog(f, flags, msg); 1076 | (void)close(f->f_file); 1077 | } 1078 | (void)sigsetmask(omask); 1079 | return; 1080 | } 1081 | for (f = Files; f; f = f->f_next) { 1082 | /* skip messages that are incorrect priority */ 1083 | if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev)) 1084 | ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev)) 1085 | ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev)) 1086 | ) 1087 | || f->f_pmask[fac] == INTERNAL_NOPRI) 1088 | continue; 1089 | 1090 | /* skip messages with the incorrect hostname */ 1091 | if (skip_message(from, f->f_host, 0)) 1092 | continue; 1093 | 1094 | /* skip messages with the incorrect program name */ 1095 | if (skip_message(prog, f->f_program, 1)) 1096 | continue; 1097 | 1098 | /* skip message to console if it has already been printed */ 1099 | if (f->f_type == F_CONSOLE && (flags & IGN_CONS)) 1100 | continue; 1101 | 1102 | /* don't output marks to recently written files */ 1103 | if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) 1104 | continue; 1105 | 1106 | /* 1107 | * suppress duplicate lines to this file 1108 | */ 1109 | if (no_compress - (f->f_type != F_PIPE) < 1 && 1110 | (flags & MARK) == 0 && msglen == f->f_prevlen && 1111 | f->f_prevline && !strcmp(msg, f->f_prevline) && 1112 | !strcasecmp(from, f->f_prevhost)) { 1113 | (void)strlcpy(f->f_lasttime, timestamp, 1114 | sizeof(f->f_lasttime)); 1115 | f->f_prevcount++; 1116 | dprintf("msg repeated %d times, %ld sec of %d\n", 1117 | f->f_prevcount, (long)(now - f->f_time), 1118 | repeatinterval[f->f_repeatcount]); 1119 | /* 1120 | * If domark would have logged this by now, 1121 | * flush it now (so we don't hold isolated messages), 1122 | * but back off so we'll flush less often 1123 | * in the future. 1124 | */ 1125 | if (now > REPEATTIME(f)) { 1126 | fprintlog(f, flags, (char *)NULL); 1127 | BACKOFF(f); 1128 | } 1129 | } else { 1130 | /* new line, save it */ 1131 | if (f->f_prevcount) 1132 | fprintlog(f, 0, (char *)NULL); 1133 | f->f_repeatcount = 0; 1134 | f->f_prevpri = pri; 1135 | (void)strlcpy(f->f_lasttime, timestamp, 1136 | sizeof(f->f_lasttime)); 1137 | (void)strlcpy(f->f_prevhost, from, 1138 | sizeof(f->f_prevhost)); 1139 | if (msglen < MAXSVLINE) { 1140 | f->f_prevlen = msglen; 1141 | (void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline)); 1142 | fprintlog(f, flags, (char *)NULL); 1143 | } else { 1144 | f->f_prevline[0] = 0; 1145 | f->f_prevlen = 0; 1146 | fprintlog(f, flags, msg); 1147 | } 1148 | } 1149 | } 1150 | (void)sigsetmask(omask); 1151 | } 1152 | 1153 | static void 1154 | dofsync(void) 1155 | { 1156 | struct filed *f; 1157 | 1158 | for (f = Files; f; f = f->f_next) { 1159 | if ((f->f_type == F_FILE) && 1160 | (f->f_flags & FFLAG_NEEDSYNC)) { 1161 | f->f_flags &= ~FFLAG_NEEDSYNC; 1162 | (void)fsync(f->f_file); 1163 | } 1164 | } 1165 | } 1166 | 1167 | #define IOV_SIZE 7 1168 | static void 1169 | fprintlog(struct filed *f, int flags, const char *msg) 1170 | { 1171 | struct iovec iov[IOV_SIZE]; 1172 | struct iovec *v; 1173 | struct addrinfo *r; 1174 | int i, l, lsent = 0; 1175 | char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL; 1176 | char nul[] = "", space[] = " ", lf[] = "\n", crlf[] = "\r\n"; 1177 | const char *msgret; 1178 | 1179 | v = iov; 1180 | if (f->f_type == F_WALL) { 1181 | v->iov_base = greetings; 1182 | /* The time displayed is not synchornized with the other log 1183 | * destinations (like messages). Following fragment was using 1184 | * ctime(&now), which was updating the time every 30 sec. 1185 | * With f_lasttime, time is synchronized correctly. 1186 | */ 1187 | v->iov_len = snprintf(greetings, sizeof greetings, 1188 | "\r\n\7Message from syslogd@%s at %.24s ...\r\n", 1189 | f->f_prevhost, f->f_lasttime); 1190 | if (v->iov_len > 0) 1191 | v++; 1192 | v->iov_base = nul; 1193 | v->iov_len = 0; 1194 | v++; 1195 | } else { 1196 | v->iov_base = f->f_lasttime; 1197 | v->iov_len = strlen(f->f_lasttime); 1198 | v++; 1199 | v->iov_base = space; 1200 | v->iov_len = 1; 1201 | v++; 1202 | } 1203 | 1204 | if (LogFacPri) { 1205 | static char fp_buf[30]; /* Hollow laugh */ 1206 | int fac = f->f_prevpri & LOG_FACMASK; 1207 | int pri = LOG_PRI(f->f_prevpri); 1208 | const char *f_s = NULL; 1209 | char f_n[5]; /* Hollow laugh */ 1210 | const char *p_s = NULL; 1211 | char p_n[5]; /* Hollow laugh */ 1212 | 1213 | if (LogFacPri > 1) { 1214 | CODE *c; 1215 | 1216 | for (c = facilitynames; c->c_name; c++) { 1217 | if (c->c_val == fac) { 1218 | f_s = c->c_name; 1219 | break; 1220 | } 1221 | } 1222 | for (c = prioritynames; c->c_name; c++) { 1223 | if (c->c_val == pri) { 1224 | p_s = c->c_name; 1225 | break; 1226 | } 1227 | } 1228 | } 1229 | if (!f_s) { 1230 | snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac)); 1231 | f_s = f_n; 1232 | } 1233 | if (!p_s) { 1234 | snprintf(p_n, sizeof p_n, "%d", pri); 1235 | p_s = p_n; 1236 | } 1237 | snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s); 1238 | v->iov_base = fp_buf; 1239 | v->iov_len = strlen(fp_buf); 1240 | } else { 1241 | v->iov_base = nul; 1242 | v->iov_len = 0; 1243 | } 1244 | v++; 1245 | 1246 | v->iov_base = f->f_prevhost; 1247 | v->iov_len = strlen(v->iov_base); 1248 | v++; 1249 | v->iov_base = space; 1250 | v->iov_len = 1; 1251 | v++; 1252 | 1253 | if (msg) { 1254 | wmsg = strdup(msg); /* XXX iov_base needs a `const' sibling. */ 1255 | if (wmsg == NULL) { 1256 | logerror("strdup"); 1257 | exit(1); 1258 | } 1259 | v->iov_base = wmsg; 1260 | v->iov_len = strlen(msg); 1261 | } else if (f->f_prevcount > 1) { 1262 | v->iov_base = repbuf; 1263 | v->iov_len = snprintf(repbuf, sizeof repbuf, 1264 | "last message repeated %d times", f->f_prevcount); 1265 | } else if (f->f_prevline) { 1266 | v->iov_base = f->f_prevline; 1267 | v->iov_len = f->f_prevlen; 1268 | } else { 1269 | return; 1270 | } 1271 | v++; 1272 | 1273 | dprintf("Logging to %s", TypeNames[f->f_type]); 1274 | f->f_time = now; 1275 | 1276 | switch (f->f_type) { 1277 | int port; 1278 | case F_UNUSED: 1279 | dprintf("\n"); 1280 | break; 1281 | 1282 | case F_FORW: 1283 | port = (int)ntohs(((struct sockaddr_in *) 1284 | (f->f_un.f_forw.f_addr->ai_addr))->sin_port); 1285 | if (port != 514) { 1286 | dprintf(" %s:%d\n", f->f_un.f_forw.f_hname, port); 1287 | } else { 1288 | dprintf(" %s\n", f->f_un.f_forw.f_hname); 1289 | } 1290 | /* check for local vs remote messages */ 1291 | if (strcasecmp(f->f_prevhost, LocalHostName)) 1292 | l = snprintf(line, sizeof line - 1, 1293 | "<%d>%.15s Forwarded from %s: %s", 1294 | f->f_prevpri, (char *)iov[0].iov_base, 1295 | f->f_prevhost, (char *)iov[5].iov_base); 1296 | else 1297 | l = snprintf(line, sizeof line - 1, "<%d>%.15s %s", 1298 | f->f_prevpri, (char *)iov[0].iov_base, 1299 | (char *)iov[5].iov_base); 1300 | if (l < 0) 1301 | l = 0; 1302 | else if (l > MAXLINE) 1303 | l = MAXLINE; 1304 | 1305 | if (finet) { 1306 | for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) { 1307 | for (i = 0; i < *finet; i++) { 1308 | #if 0 1309 | /* 1310 | * should we check AF first, or just 1311 | * trial and error? FWD 1312 | */ 1313 | if (r->ai_family == 1314 | address_family_of(finet[i+1])) 1315 | #endif 1316 | lsent = sendto(finet[i+1], line, l, 0, 1317 | r->ai_addr, r->ai_addrlen); 1318 | if (lsent == l) 1319 | break; 1320 | } 1321 | if (lsent == l && !send_to_all) 1322 | break; 1323 | } 1324 | dprintf("lsent/l: %d/%d\n", lsent, l); 1325 | if (lsent != l) { 1326 | int e = errno; 1327 | logerror("sendto"); 1328 | errno = e; 1329 | switch (errno) { 1330 | case ENOBUFS: 1331 | case ENETDOWN: 1332 | case EHOSTUNREACH: 1333 | case EHOSTDOWN: 1334 | break; 1335 | /* case EBADF: */ 1336 | /* case EACCES: */ 1337 | /* case ENOTSOCK: */ 1338 | /* case EFAULT: */ 1339 | /* case EMSGSIZE: */ 1340 | /* case EAGAIN: */ 1341 | /* case ENOBUFS: */ 1342 | /* case ECONNREFUSED: */ 1343 | default: 1344 | dprintf("removing entry\n"); 1345 | f->f_type = F_UNUSED; 1346 | break; 1347 | } 1348 | } 1349 | } 1350 | break; 1351 | 1352 | case F_FILE: 1353 | dprintf(" %s\n", f->f_un.f_fname); 1354 | v->iov_base = lf; 1355 | v->iov_len = 1; 1356 | if (writev(f->f_file, iov, IOV_SIZE) < 0) { 1357 | /* 1358 | * If writev(2) fails for potentially transient errors 1359 | * like the filesystem being full, ignore it. 1360 | * Otherwise remove this logfile from the list. 1361 | */ 1362 | if (errno != ENOSPC) { 1363 | int e = errno; 1364 | (void)close(f->f_file); 1365 | f->f_type = F_UNUSED; 1366 | errno = e; 1367 | logerror(f->f_un.f_fname); 1368 | } 1369 | } else if ((flags & SYNC_FILE) && (f->f_flags & FFLAG_SYNC)) { 1370 | f->f_flags |= FFLAG_NEEDSYNC; 1371 | needdofsync = 1; 1372 | } 1373 | break; 1374 | 1375 | case F_PIPE: 1376 | dprintf(" %s\n", f->f_un.f_pipe.f_pname); 1377 | v->iov_base = lf; 1378 | v->iov_len = 1; 1379 | if (f->f_un.f_pipe.f_pid == 0) { 1380 | if ((f->f_file = p_open(f->f_un.f_pipe.f_pname, 1381 | &f->f_un.f_pipe.f_pid)) < 0) { 1382 | f->f_type = F_UNUSED; 1383 | logerror(f->f_un.f_pipe.f_pname); 1384 | break; 1385 | } 1386 | } 1387 | if (writev(f->f_file, iov, IOV_SIZE) < 0) { 1388 | int e = errno; 1389 | (void)close(f->f_file); 1390 | if (f->f_un.f_pipe.f_pid > 0) 1391 | deadq_enter(f->f_un.f_pipe.f_pid, 1392 | f->f_un.f_pipe.f_pname); 1393 | f->f_un.f_pipe.f_pid = 0; 1394 | errno = e; 1395 | logerror(f->f_un.f_pipe.f_pname); 1396 | } 1397 | break; 1398 | 1399 | case F_CONSOLE: 1400 | if (flags & IGN_CONS) { 1401 | dprintf(" (ignored)\n"); 1402 | break; 1403 | } 1404 | /* FALLTHROUGH */ 1405 | 1406 | case F_TTY: 1407 | dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname); 1408 | v->iov_base = crlf; 1409 | v->iov_len = 2; 1410 | 1411 | errno = 0; /* ttymsg() only sometimes returns an errno */ 1412 | if ((msgret = ttymsg(iov, IOV_SIZE, f->f_un.f_fname, 10))) { 1413 | f->f_type = F_UNUSED; 1414 | logerror(msgret); 1415 | } 1416 | break; 1417 | 1418 | case F_USERS: 1419 | case F_WALL: 1420 | dprintf("\n"); 1421 | v->iov_base = crlf; 1422 | v->iov_len = 2; 1423 | wallmsg(f, iov, IOV_SIZE); 1424 | break; 1425 | } 1426 | f->f_prevcount = 0; 1427 | free(wmsg); 1428 | } 1429 | 1430 | /* 1431 | * WALLMSG -- Write a message to the world at large 1432 | * 1433 | * Write the specified message to either the entire 1434 | * world, or a list of approved users. 1435 | */ 1436 | static void 1437 | wallmsg(struct filed *f, struct iovec *iov, const int iovlen) 1438 | { 1439 | static int reenter; /* avoid calling ourselves */ 1440 | FILE *uf; 1441 | struct utmp ut; 1442 | int i; 1443 | const char *p; 1444 | char line[sizeof(ut.ut_line) + 1]; 1445 | 1446 | if (reenter++) 1447 | return; 1448 | if ((uf = fopen(_PATH_UTMP, "r")) == NULL) { 1449 | logerror(_PATH_UTMP); 1450 | reenter = 0; 1451 | return; 1452 | } 1453 | /* NOSTRICT */ 1454 | while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) { 1455 | if (ut.ut_name[0] == '\0') 1456 | continue; 1457 | /* We must use strncpy since ut_* may not be NUL terminated. */ 1458 | strncpy(line, ut.ut_line, sizeof(line) - 1); 1459 | line[sizeof(line) - 1] = '\0'; 1460 | if (f->f_type == F_WALL) { 1461 | if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME)) != 1462 | NULL) { 1463 | errno = 0; /* already in msg */ 1464 | logerror(p); 1465 | } 1466 | continue; 1467 | } 1468 | /* should we send the message to this user? */ 1469 | for (i = 0; i < MAXUNAMES; i++) { 1470 | if (!f->f_un.f_uname[i][0]) 1471 | break; 1472 | if (!strncmp(f->f_un.f_uname[i], ut.ut_name, 1473 | UT_NAMESIZE)) { 1474 | if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME)) 1475 | != NULL) { 1476 | errno = 0; /* already in msg */ 1477 | logerror(p); 1478 | } 1479 | break; 1480 | } 1481 | } 1482 | } 1483 | (void)fclose(uf); 1484 | reenter = 0; 1485 | } 1486 | 1487 | static void 1488 | reapchild(int signo __unused) 1489 | { 1490 | int status; 1491 | pid_t pid; 1492 | struct filed *f; 1493 | 1494 | while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) { 1495 | if (!Initialized) 1496 | /* Don't tell while we are initting. */ 1497 | continue; 1498 | 1499 | /* First, look if it's a process from the dead queue. */ 1500 | if (deadq_remove(pid)) 1501 | goto oncemore; 1502 | 1503 | /* Now, look in list of active processes. */ 1504 | for (f = Files; f; f = f->f_next) 1505 | if (f->f_type == F_PIPE && 1506 | f->f_un.f_pipe.f_pid == pid) { 1507 | (void)close(f->f_file); 1508 | f->f_un.f_pipe.f_pid = 0; 1509 | log_deadchild(pid, status, 1510 | f->f_un.f_pipe.f_pname); 1511 | break; 1512 | } 1513 | oncemore: 1514 | continue; 1515 | } 1516 | } 1517 | 1518 | /* 1519 | * Return a printable representation of a host address. 1520 | */ 1521 | static const char * 1522 | cvthname(struct sockaddr *f) 1523 | { 1524 | int error, hl; 1525 | sigset_t omask, nmask; 1526 | static char hname[NI_MAXHOST], ip[NI_MAXHOST]; 1527 | 1528 | error = getnameinfo((struct sockaddr *)f, 1529 | ((struct sockaddr *)f)->sa_len, 1530 | ip, sizeof ip, NULL, 0, NI_NUMERICHOST); 1531 | dprintf("cvthname(%s)\n", ip); 1532 | 1533 | if (error) { 1534 | dprintf("Malformed from address %s\n", gai_strerror(error)); 1535 | return ("???"); 1536 | } 1537 | if (!resolve) 1538 | return (ip); 1539 | 1540 | sigemptyset(&nmask); 1541 | sigaddset(&nmask, SIGHUP); 1542 | sigprocmask(SIG_BLOCK, &nmask, &omask); 1543 | error = getnameinfo((struct sockaddr *)f, 1544 | ((struct sockaddr *)f)->sa_len, 1545 | hname, sizeof hname, NULL, 0, NI_NAMEREQD); 1546 | sigprocmask(SIG_SETMASK, &omask, NULL); 1547 | if (error) { 1548 | dprintf("Host name for your address (%s) unknown\n", ip); 1549 | return (ip); 1550 | } 1551 | hl = strlen(hname); 1552 | if (hl > 0 && hname[hl-1] == '.') 1553 | hname[--hl] = '\0'; 1554 | trimdomain(hname, hl); 1555 | return (hname); 1556 | } 1557 | 1558 | static void 1559 | dodie(int signo) 1560 | { 1561 | 1562 | WantDie = signo; 1563 | } 1564 | 1565 | static void 1566 | domark(int signo __unused) 1567 | { 1568 | 1569 | MarkSet = 1; 1570 | } 1571 | 1572 | /* 1573 | * Print syslogd errors some place. 1574 | */ 1575 | static void 1576 | logerror(const char *type) 1577 | { 1578 | char buf[512]; 1579 | static int recursed = 0; 1580 | 1581 | /* If there's an error while trying to log an error, give up. */ 1582 | if (recursed) 1583 | return; 1584 | recursed++; 1585 | if (errno) 1586 | (void)snprintf(buf, 1587 | sizeof buf, "syslogd: %s: %s", type, strerror(errno)); 1588 | else 1589 | (void)snprintf(buf, sizeof buf, "syslogd: %s", type); 1590 | errno = 0; 1591 | dprintf("%s\n", buf); 1592 | logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE); 1593 | recursed--; 1594 | } 1595 | 1596 | static void 1597 | die(int signo) 1598 | { 1599 | struct filed *f; 1600 | struct funix *fx; 1601 | int was_initialized; 1602 | char buf[100]; 1603 | 1604 | was_initialized = Initialized; 1605 | Initialized = 0; /* Don't log SIGCHLDs. */ 1606 | for (f = Files; f != NULL; f = f->f_next) { 1607 | /* flush any pending output */ 1608 | if (f->f_prevcount) 1609 | fprintlog(f, 0, (char *)NULL); 1610 | if (f->f_type == F_PIPE && f->f_un.f_pipe.f_pid > 0) { 1611 | (void)close(f->f_file); 1612 | f->f_un.f_pipe.f_pid = 0; 1613 | } 1614 | } 1615 | Initialized = was_initialized; 1616 | if (signo) { 1617 | dprintf("syslogd: exiting on signal %d\n", signo); 1618 | (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo); 1619 | errno = 0; 1620 | logerror(buf); 1621 | } 1622 | STAILQ_FOREACH(fx, &funixes, next) 1623 | (void)unlink(fx->name); 1624 | pidfile_remove(pfh); 1625 | 1626 | exit(1); 1627 | } 1628 | 1629 | /* 1630 | * INIT -- Initialize syslogd from configuration table 1631 | */ 1632 | static void 1633 | init(int signo) 1634 | { 1635 | int i; 1636 | FILE *cf; 1637 | struct filed *f, *next, **nextp; 1638 | char *p; 1639 | char cline[LINE_MAX]; 1640 | char prog[NAME_MAX+1]; 1641 | char host[MAXHOSTNAMELEN]; 1642 | char oldLocalHostName[MAXHOSTNAMELEN]; 1643 | char hostMsg[2*MAXHOSTNAMELEN+40]; 1644 | char bootfileMsg[LINE_MAX]; 1645 | 1646 | dprintf("init\n"); 1647 | 1648 | /* 1649 | * Load hostname (may have changed). 1650 | */ 1651 | if (signo != 0) 1652 | (void)strlcpy(oldLocalHostName, LocalHostName, 1653 | sizeof(oldLocalHostName)); 1654 | if (gethostname(LocalHostName, sizeof(LocalHostName))) 1655 | err(EX_OSERR, "gethostname() failed"); 1656 | if ((p = strchr(LocalHostName, '.')) != NULL) { 1657 | *p++ = '\0'; 1658 | LocalDomain = p; 1659 | } else { 1660 | LocalDomain = ""; 1661 | } 1662 | 1663 | /* 1664 | * Close all open log files. 1665 | */ 1666 | Initialized = 0; 1667 | for (f = Files; f != NULL; f = next) { 1668 | /* flush any pending output */ 1669 | if (f->f_prevcount) 1670 | fprintlog(f, 0, (char *)NULL); 1671 | 1672 | switch (f->f_type) { 1673 | case F_FILE: 1674 | case F_FORW: 1675 | case F_CONSOLE: 1676 | case F_TTY: 1677 | (void)close(f->f_file); 1678 | break; 1679 | case F_PIPE: 1680 | if (f->f_un.f_pipe.f_pid > 0) { 1681 | (void)close(f->f_file); 1682 | deadq_enter(f->f_un.f_pipe.f_pid, 1683 | f->f_un.f_pipe.f_pname); 1684 | } 1685 | f->f_un.f_pipe.f_pid = 0; 1686 | break; 1687 | } 1688 | next = f->f_next; 1689 | if (f->f_program) free(f->f_program); 1690 | if (f->f_host) free(f->f_host); 1691 | free((char *)f); 1692 | } 1693 | Files = NULL; 1694 | nextp = &Files; 1695 | 1696 | /* open the configuration file */ 1697 | if ((cf = fopen(ConfFile, "r")) == NULL) { 1698 | dprintf("cannot open %s\n", ConfFile); 1699 | *nextp = (struct filed *)calloc(1, sizeof(*f)); 1700 | if (*nextp == NULL) { 1701 | logerror("calloc"); 1702 | exit(1); 1703 | } 1704 | cfline("*.ERR\t/dev/console", *nextp, "*", "*"); 1705 | (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f)); 1706 | if ((*nextp)->f_next == NULL) { 1707 | logerror("calloc"); 1708 | exit(1); 1709 | } 1710 | cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*"); 1711 | Initialized = 1; 1712 | return; 1713 | } 1714 | 1715 | /* 1716 | * Foreach line in the conf table, open that file. 1717 | */ 1718 | f = NULL; 1719 | (void)strlcpy(host, "*", sizeof(host)); 1720 | (void)strlcpy(prog, "*", sizeof(prog)); 1721 | while (fgets(cline, sizeof(cline), cf) != NULL) { 1722 | /* 1723 | * check for end-of-section, comments, strip off trailing 1724 | * spaces and newline character. #!prog is treated specially: 1725 | * following lines apply only to that program. 1726 | */ 1727 | for (p = cline; isspace(*p); ++p) 1728 | continue; 1729 | if (*p == 0) 1730 | continue; 1731 | if (*p == '#') { 1732 | p++; 1733 | if (*p != '!' && *p != '+' && *p != '-') 1734 | continue; 1735 | } 1736 | if (*p == '+' || *p == '-') { 1737 | host[0] = *p++; 1738 | while (isspace(*p)) 1739 | p++; 1740 | if ((!*p) || (*p == '*')) { 1741 | (void)strlcpy(host, "*", sizeof(host)); 1742 | continue; 1743 | } 1744 | if (*p == '@') 1745 | p = LocalHostName; 1746 | for (i = 1; i < MAXHOSTNAMELEN - 1; i++) { 1747 | if (!isalnum(*p) && *p != '.' && *p != '-' 1748 | && *p != ',' && *p != ':' && *p != '%') 1749 | break; 1750 | host[i] = *p++; 1751 | } 1752 | host[i] = '\0'; 1753 | continue; 1754 | } 1755 | if (*p == '!') { 1756 | p++; 1757 | while (isspace(*p)) p++; 1758 | if ((!*p) || (*p == '*')) { 1759 | (void)strlcpy(prog, "*", sizeof(prog)); 1760 | continue; 1761 | } 1762 | for (i = 0; i < NAME_MAX; i++) { 1763 | if (!isprint(p[i]) || isspace(p[i])) 1764 | break; 1765 | prog[i] = p[i]; 1766 | } 1767 | prog[i] = 0; 1768 | continue; 1769 | } 1770 | for (p = cline + 1; *p != '\0'; p++) { 1771 | if (*p != '#') 1772 | continue; 1773 | if (*(p - 1) == '\\') { 1774 | strcpy(p - 1, p); 1775 | p--; 1776 | continue; 1777 | } 1778 | *p = '\0'; 1779 | break; 1780 | } 1781 | for (i = strlen(cline) - 1; i >= 0 && isspace(cline[i]); i--) 1782 | cline[i] = '\0'; 1783 | f = (struct filed *)calloc(1, sizeof(*f)); 1784 | if (f == NULL) { 1785 | logerror("calloc"); 1786 | exit(1); 1787 | } 1788 | *nextp = f; 1789 | nextp = &f->f_next; 1790 | cfline(cline, f, prog, host); 1791 | } 1792 | 1793 | /* close the configuration file */ 1794 | (void)fclose(cf); 1795 | 1796 | Initialized = 1; 1797 | 1798 | if (Debug) { 1799 | int port; 1800 | for (f = Files; f; f = f->f_next) { 1801 | for (i = 0; i <= LOG_NFACILITIES; i++) 1802 | if (f->f_pmask[i] == INTERNAL_NOPRI) 1803 | printf("X "); 1804 | else 1805 | printf("%d ", f->f_pmask[i]); 1806 | printf("%s: ", TypeNames[f->f_type]); 1807 | switch (f->f_type) { 1808 | case F_FILE: 1809 | printf("%s", f->f_un.f_fname); 1810 | break; 1811 | 1812 | case F_CONSOLE: 1813 | case F_TTY: 1814 | printf("%s%s", _PATH_DEV, f->f_un.f_fname); 1815 | break; 1816 | 1817 | case F_FORW: 1818 | port = (int)ntohs(((struct sockaddr_in *) 1819 | (f->f_un.f_forw.f_addr->ai_addr))->sin_port); 1820 | if (port != 514) { 1821 | printf("%s:%d", 1822 | f->f_un.f_forw.f_hname, port); 1823 | } else { 1824 | printf("%s", f->f_un.f_forw.f_hname); 1825 | } 1826 | break; 1827 | 1828 | case F_PIPE: 1829 | printf("%s", f->f_un.f_pipe.f_pname); 1830 | break; 1831 | 1832 | case F_USERS: 1833 | for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++) 1834 | printf("%s, ", f->f_un.f_uname[i]); 1835 | break; 1836 | } 1837 | if (f->f_program) 1838 | printf(" (%s)", f->f_program); 1839 | printf("\n"); 1840 | } 1841 | } 1842 | 1843 | logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE); 1844 | dprintf("syslogd: restarted\n"); 1845 | /* 1846 | * Log a change in hostname, but only on a restart. 1847 | */ 1848 | if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) { 1849 | (void)snprintf(hostMsg, sizeof(hostMsg), 1850 | "syslogd: hostname changed, \"%s\" to \"%s\"", 1851 | oldLocalHostName, LocalHostName); 1852 | logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE); 1853 | dprintf("%s\n", hostMsg); 1854 | } 1855 | /* 1856 | * Log the kernel boot file if we aren't going to use it as 1857 | * the prefix, and if this is *not* a restart. 1858 | */ 1859 | if (signo == 0 && !use_bootfile) { 1860 | (void)snprintf(bootfileMsg, sizeof(bootfileMsg), 1861 | "syslogd: kernel boot file is %s", bootfile); 1862 | logmsg(LOG_KERN|LOG_INFO, bootfileMsg, LocalHostName, ADDDATE); 1863 | dprintf("%s\n", bootfileMsg); 1864 | } 1865 | } 1866 | 1867 | /* 1868 | * Crack a configuration file line 1869 | */ 1870 | static void 1871 | cfline(const char *line, struct filed *f, const char *prog, const char *host) 1872 | { 1873 | struct addrinfo hints, *res; 1874 | int error, i, pri, syncfile; 1875 | const char *p, *q; 1876 | char *bp; 1877 | char buf[MAXLINE], ebuf[100]; 1878 | 1879 | dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host); 1880 | 1881 | errno = 0; /* keep strerror() stuff out of logerror messages */ 1882 | 1883 | /* clear out file entry */ 1884 | memset(f, 0, sizeof(*f)); 1885 | for (i = 0; i <= LOG_NFACILITIES; i++) 1886 | f->f_pmask[i] = INTERNAL_NOPRI; 1887 | 1888 | /* save hostname if any */ 1889 | if (host && *host == '*') 1890 | host = NULL; 1891 | if (host) { 1892 | int hl; 1893 | 1894 | f->f_host = strdup(host); 1895 | if (f->f_host == NULL) { 1896 | logerror("strdup"); 1897 | exit(1); 1898 | } 1899 | hl = strlen(f->f_host); 1900 | if (hl > 0 && f->f_host[hl-1] == '.') 1901 | f->f_host[--hl] = '\0'; 1902 | trimdomain(f->f_host, hl); 1903 | } 1904 | 1905 | /* save program name if any */ 1906 | if (prog && *prog == '*') 1907 | prog = NULL; 1908 | if (prog) { 1909 | f->f_program = strdup(prog); 1910 | if (f->f_program == NULL) { 1911 | logerror("strdup"); 1912 | exit(1); 1913 | } 1914 | } 1915 | 1916 | /* scan through the list of selectors */ 1917 | for (p = line; *p && *p != '\t' && *p != ' ';) { 1918 | int pri_done; 1919 | int pri_cmp; 1920 | int pri_invert; 1921 | 1922 | /* find the end of this facility name list */ 1923 | for (q = p; *q && *q != '\t' && *q != ' ' && *q++ != '.'; ) 1924 | continue; 1925 | 1926 | /* get the priority comparison */ 1927 | pri_cmp = 0; 1928 | pri_done = 0; 1929 | pri_invert = 0; 1930 | if (*q == '!') { 1931 | pri_invert = 1; 1932 | q++; 1933 | } 1934 | while (!pri_done) { 1935 | switch (*q) { 1936 | case '<': 1937 | pri_cmp |= PRI_LT; 1938 | q++; 1939 | break; 1940 | case '=': 1941 | pri_cmp |= PRI_EQ; 1942 | q++; 1943 | break; 1944 | case '>': 1945 | pri_cmp |= PRI_GT; 1946 | q++; 1947 | break; 1948 | default: 1949 | pri_done++; 1950 | break; 1951 | } 1952 | } 1953 | 1954 | /* collect priority name */ 1955 | for (bp = buf; *q && !strchr("\t,; ", *q); ) 1956 | *bp++ = *q++; 1957 | *bp = '\0'; 1958 | 1959 | /* skip cruft */ 1960 | while (strchr(",;", *q)) 1961 | q++; 1962 | 1963 | /* decode priority name */ 1964 | if (*buf == '*') { 1965 | pri = LOG_PRIMASK; 1966 | pri_cmp = PRI_LT | PRI_EQ | PRI_GT; 1967 | } else { 1968 | /* Ignore trailing spaces. */ 1969 | for (i = strlen(buf) - 1; i >= 0 && buf[i] == ' '; i--) 1970 | buf[i] = '\0'; 1971 | 1972 | pri = decode(buf, prioritynames); 1973 | if (pri < 0) { 1974 | (void)snprintf(ebuf, sizeof ebuf, 1975 | "unknown priority name \"%s\"", buf); 1976 | logerror(ebuf); 1977 | return; 1978 | } 1979 | } 1980 | if (!pri_cmp) 1981 | pri_cmp = (UniquePriority) 1982 | ? (PRI_EQ) 1983 | : (PRI_EQ | PRI_GT) 1984 | ; 1985 | if (pri_invert) 1986 | pri_cmp ^= PRI_LT | PRI_EQ | PRI_GT; 1987 | 1988 | /* scan facilities */ 1989 | while (*p && !strchr("\t.; ", *p)) { 1990 | for (bp = buf; *p && !strchr("\t,;. ", *p); ) 1991 | *bp++ = *p++; 1992 | *bp = '\0'; 1993 | 1994 | if (*buf == '*') { 1995 | for (i = 0; i < LOG_NFACILITIES; i++) { 1996 | f->f_pmask[i] = pri; 1997 | f->f_pcmp[i] = pri_cmp; 1998 | } 1999 | } else { 2000 | i = decode(buf, facilitynames); 2001 | if (i < 0) { 2002 | (void)snprintf(ebuf, sizeof ebuf, 2003 | "unknown facility name \"%s\"", 2004 | buf); 2005 | logerror(ebuf); 2006 | return; 2007 | } 2008 | f->f_pmask[i >> 3] = pri; 2009 | f->f_pcmp[i >> 3] = pri_cmp; 2010 | } 2011 | while (*p == ',' || *p == ' ') 2012 | p++; 2013 | } 2014 | 2015 | p = q; 2016 | } 2017 | 2018 | /* skip to action part */ 2019 | while (*p == '\t' || *p == ' ') 2020 | p++; 2021 | 2022 | if (*p == '-') { 2023 | syncfile = 0; 2024 | p++; 2025 | } else 2026 | syncfile = 1; 2027 | 2028 | switch (*p) { 2029 | case '@': 2030 | { 2031 | char *tp; 2032 | /* 2033 | * scan forward to see if there is a port defined. 2034 | * so we can't use strlcpy.. 2035 | */ 2036 | i = sizeof(f->f_un.f_forw.f_hname); 2037 | tp = f->f_un.f_forw.f_hname; 2038 | p++; 2039 | 2040 | while (*p && (*p != ':') && (i-- > 0)) { 2041 | *tp++ = *p++; 2042 | } 2043 | *tp = '\0'; 2044 | } 2045 | /* See if we copied a domain and have a port */ 2046 | if (*p == ':') 2047 | p++; 2048 | else 2049 | p = NULL; 2050 | 2051 | memset(&hints, 0, sizeof(hints)); 2052 | hints.ai_family = family; 2053 | hints.ai_socktype = SOCK_DGRAM; 2054 | error = getaddrinfo(f->f_un.f_forw.f_hname, 2055 | p ? p : "syslog", &hints, &res); 2056 | if (error) { 2057 | logerror(gai_strerror(error)); 2058 | break; 2059 | } 2060 | f->f_un.f_forw.f_addr = res; 2061 | f->f_type = F_FORW; 2062 | break; 2063 | 2064 | case '/': 2065 | if ((f->f_file = open(p, logflags, 0600)) < 0) { 2066 | f->f_type = F_UNUSED; 2067 | logerror(p); 2068 | break; 2069 | } 2070 | if (syncfile) 2071 | f->f_flags |= FFLAG_SYNC; 2072 | if (isatty(f->f_file)) { 2073 | if (strcmp(p, ctty) == 0) 2074 | f->f_type = F_CONSOLE; 2075 | else 2076 | f->f_type = F_TTY; 2077 | (void)strlcpy(f->f_un.f_fname, p + sizeof(_PATH_DEV) - 1, 2078 | sizeof(f->f_un.f_fname)); 2079 | } else { 2080 | (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname)); 2081 | f->f_type = F_FILE; 2082 | } 2083 | break; 2084 | 2085 | case '|': 2086 | f->f_un.f_pipe.f_pid = 0; 2087 | (void)strlcpy(f->f_un.f_pipe.f_pname, p + 1, 2088 | sizeof(f->f_un.f_pipe.f_pname)); 2089 | f->f_type = F_PIPE; 2090 | break; 2091 | 2092 | case '*': 2093 | f->f_type = F_WALL; 2094 | break; 2095 | 2096 | default: 2097 | for (i = 0; i < MAXUNAMES && *p; i++) { 2098 | for (q = p; *q && *q != ','; ) 2099 | q++; 2100 | (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE); 2101 | if ((q - p) > UT_NAMESIZE) 2102 | f->f_un.f_uname[i][UT_NAMESIZE] = '\0'; 2103 | else 2104 | f->f_un.f_uname[i][q - p] = '\0'; 2105 | while (*q == ',' || *q == ' ') 2106 | q++; 2107 | p = q; 2108 | } 2109 | f->f_type = F_USERS; 2110 | break; 2111 | } 2112 | } 2113 | 2114 | 2115 | /* 2116 | * Decode a symbolic name to a numeric value 2117 | */ 2118 | static int 2119 | decode(const char *name, CODE *codetab) 2120 | { 2121 | CODE *c; 2122 | char *p, buf[40]; 2123 | 2124 | if (isdigit(*name)) 2125 | return (atoi(name)); 2126 | 2127 | for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) { 2128 | if (isupper(*name)) 2129 | *p = tolower(*name); 2130 | else 2131 | *p = *name; 2132 | } 2133 | *p = '\0'; 2134 | for (c = codetab; c->c_name; c++) 2135 | if (!strcmp(buf, c->c_name)) 2136 | return (c->c_val); 2137 | 2138 | return (-1); 2139 | } 2140 | 2141 | static void 2142 | markit(void) 2143 | { 2144 | struct filed *f; 2145 | dq_t q, next; 2146 | 2147 | now = time((time_t *)NULL); 2148 | MarkSeq += TIMERINTVL; 2149 | if (MarkSeq >= MarkInterval) { 2150 | logmsg(LOG_INFO, "-- MARK --", 2151 | LocalHostName, ADDDATE|MARK); 2152 | MarkSeq = 0; 2153 | } 2154 | 2155 | for (f = Files; f; f = f->f_next) { 2156 | if (f->f_prevcount && now >= REPEATTIME(f)) { 2157 | dprintf("flush %s: repeated %d times, %d sec.\n", 2158 | TypeNames[f->f_type], f->f_prevcount, 2159 | repeatinterval[f->f_repeatcount]); 2160 | fprintlog(f, 0, (char *)NULL); 2161 | BACKOFF(f); 2162 | } 2163 | } 2164 | 2165 | /* Walk the dead queue, and see if we should signal somebody. */ 2166 | for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = next) { 2167 | next = TAILQ_NEXT(q, dq_entries); 2168 | 2169 | switch (q->dq_timeout) { 2170 | case 0: 2171 | /* Already signalled once, try harder now. */ 2172 | if (kill(q->dq_pid, SIGKILL) != 0) 2173 | (void)deadq_remove(q->dq_pid); 2174 | break; 2175 | 2176 | case 1: 2177 | /* 2178 | * Timed out on dead queue, send terminate 2179 | * signal. Note that we leave the removal 2180 | * from the dead queue to reapchild(), which 2181 | * will also log the event (unless the process 2182 | * didn't even really exist, in case we simply 2183 | * drop it from the dead queue). 2184 | */ 2185 | if (kill(q->dq_pid, SIGTERM) != 0) 2186 | (void)deadq_remove(q->dq_pid); 2187 | /* FALLTHROUGH */ 2188 | 2189 | default: 2190 | q->dq_timeout--; 2191 | } 2192 | } 2193 | MarkSet = 0; 2194 | (void)alarm(TIMERINTVL); 2195 | } 2196 | 2197 | /* 2198 | * fork off and become a daemon, but wait for the child to come online 2199 | * before returing to the parent, or we get disk thrashing at boot etc. 2200 | * Set a timer so we don't hang forever if it wedges. 2201 | */ 2202 | static int 2203 | waitdaemon(int nochdir, int noclose, int maxwait) 2204 | { 2205 | int fd; 2206 | int status; 2207 | pid_t pid, childpid; 2208 | 2209 | switch (childpid = fork()) { 2210 | case -1: 2211 | return (-1); 2212 | case 0: 2213 | break; 2214 | default: 2215 | signal(SIGALRM, timedout); 2216 | alarm(maxwait); 2217 | while ((pid = wait3(&status, 0, NULL)) != -1) { 2218 | if (WIFEXITED(status)) 2219 | errx(1, "child pid %d exited with return code %d", 2220 | pid, WEXITSTATUS(status)); 2221 | if (WIFSIGNALED(status)) 2222 | errx(1, "child pid %d exited on signal %d%s", 2223 | pid, WTERMSIG(status), 2224 | WCOREDUMP(status) ? " (core dumped)" : 2225 | ""); 2226 | if (pid == childpid) /* it's gone... */ 2227 | break; 2228 | } 2229 | exit(0); 2230 | } 2231 | 2232 | if (setsid() == -1) 2233 | return (-1); 2234 | 2235 | if (!nochdir) 2236 | (void)chdir("/"); 2237 | 2238 | if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 2239 | (void)dup2(fd, STDIN_FILENO); 2240 | (void)dup2(fd, STDOUT_FILENO); 2241 | (void)dup2(fd, STDERR_FILENO); 2242 | if (fd > 2) 2243 | (void)close (fd); 2244 | } 2245 | return (getppid()); 2246 | } 2247 | 2248 | /* 2249 | * We get a SIGALRM from the child when it's running and finished doing it's 2250 | * fsync()'s or O_SYNC writes for all the boot messages. 2251 | * 2252 | * We also get a signal from the kernel if the timer expires, so check to 2253 | * see what happened. 2254 | */ 2255 | static void 2256 | timedout(int sig __unused) 2257 | { 2258 | int left; 2259 | left = alarm(0); 2260 | signal(SIGALRM, SIG_DFL); 2261 | if (left == 0) 2262 | errx(1, "timed out waiting for child"); 2263 | else 2264 | _exit(0); 2265 | } 2266 | 2267 | /* 2268 | * Add `s' to the list of allowable peer addresses to accept messages 2269 | * from. 2270 | * 2271 | * `s' is a string in the form: 2272 | * 2273 | * [*]domainname[:{servicename|portnumber|*}] 2274 | * 2275 | * or 2276 | * 2277 | * netaddr/maskbits[:{servicename|portnumber|*}] 2278 | * 2279 | * Returns -1 on error, 0 if the argument was valid. 2280 | */ 2281 | static int 2282 | allowaddr(char *s) 2283 | { 2284 | char *cp1, *cp2; 2285 | struct allowedpeer ap; 2286 | struct servent *se; 2287 | int masklen = -1; 2288 | struct addrinfo hints, *res; 2289 | struct in_addr *addrp, *maskp; 2290 | #ifdef INET6 2291 | int i; 2292 | u_int32_t *addr6p, *mask6p; 2293 | #endif 2294 | char ip[NI_MAXHOST]; 2295 | 2296 | #ifdef INET6 2297 | if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL) 2298 | #endif 2299 | cp1 = s; 2300 | if ((cp1 = strrchr(cp1, ':'))) { 2301 | /* service/port provided */ 2302 | *cp1++ = '\0'; 2303 | if (strlen(cp1) == 1 && *cp1 == '*') 2304 | /* any port allowed */ 2305 | ap.port = 0; 2306 | else if ((se = getservbyname(cp1, "udp"))) { 2307 | ap.port = ntohs(se->s_port); 2308 | } else { 2309 | ap.port = strtol(cp1, &cp2, 0); 2310 | if (*cp2 != '\0') 2311 | return (-1); /* port not numeric */ 2312 | } 2313 | } else { 2314 | if ((se = getservbyname("syslog", "udp"))) 2315 | ap.port = ntohs(se->s_port); 2316 | else 2317 | /* sanity, should not happen */ 2318 | ap.port = 514; 2319 | } 2320 | 2321 | if ((cp1 = strchr(s, '/')) != NULL && 2322 | strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) { 2323 | *cp1 = '\0'; 2324 | if ((masklen = atoi(cp1 + 1)) < 0) 2325 | return (-1); 2326 | } 2327 | #ifdef INET6 2328 | if (*s == '[') { 2329 | cp2 = s + strlen(s) - 1; 2330 | if (*cp2 == ']') { 2331 | ++s; 2332 | *cp2 = '\0'; 2333 | } else { 2334 | cp2 = NULL; 2335 | } 2336 | } else { 2337 | cp2 = NULL; 2338 | } 2339 | #endif 2340 | memset(&hints, 0, sizeof(hints)); 2341 | hints.ai_family = PF_UNSPEC; 2342 | hints.ai_socktype = SOCK_DGRAM; 2343 | hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 2344 | if (getaddrinfo(s, NULL, &hints, &res) == 0) { 2345 | ap.isnumeric = 1; 2346 | memcpy(&ap.a_addr, res->ai_addr, res->ai_addrlen); 2347 | memset(&ap.a_mask, 0, sizeof(ap.a_mask)); 2348 | ap.a_mask.ss_family = res->ai_family; 2349 | if (res->ai_family == AF_INET) { 2350 | ap.a_mask.ss_len = sizeof(struct sockaddr_in); 2351 | maskp = &((struct sockaddr_in *)&ap.a_mask)->sin_addr; 2352 | addrp = &((struct sockaddr_in *)&ap.a_addr)->sin_addr; 2353 | if (masklen < 0) { 2354 | /* use default netmask */ 2355 | if (IN_CLASSA(ntohl(addrp->s_addr))) 2356 | maskp->s_addr = htonl(IN_CLASSA_NET); 2357 | else if (IN_CLASSB(ntohl(addrp->s_addr))) 2358 | maskp->s_addr = htonl(IN_CLASSB_NET); 2359 | else 2360 | maskp->s_addr = htonl(IN_CLASSC_NET); 2361 | } else if (masklen <= 32) { 2362 | /* convert masklen to netmask */ 2363 | if (masklen == 0) 2364 | maskp->s_addr = 0; 2365 | else 2366 | maskp->s_addr = htonl(~((1 << (32 - masklen)) - 1)); 2367 | } else { 2368 | freeaddrinfo(res); 2369 | return (-1); 2370 | } 2371 | /* Lose any host bits in the network number. */ 2372 | addrp->s_addr &= maskp->s_addr; 2373 | } 2374 | #ifdef INET6 2375 | else if (res->ai_family == AF_INET6 && masklen <= 128) { 2376 | ap.a_mask.ss_len = sizeof(struct sockaddr_in6); 2377 | if (masklen < 0) 2378 | masklen = 128; 2379 | mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr; 2380 | /* convert masklen to netmask */ 2381 | while (masklen > 0) { 2382 | if (masklen < 32) { 2383 | *mask6p = htonl(~(0xffffffff >> masklen)); 2384 | break; 2385 | } 2386 | *mask6p++ = 0xffffffff; 2387 | masklen -= 32; 2388 | } 2389 | /* Lose any host bits in the network number. */ 2390 | mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr; 2391 | addr6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_addr)->sin6_addr; 2392 | for (i = 0; i < 4; i++) 2393 | addr6p[i] &= mask6p[i]; 2394 | } 2395 | #endif 2396 | else { 2397 | freeaddrinfo(res); 2398 | return (-1); 2399 | } 2400 | freeaddrinfo(res); 2401 | } else { 2402 | /* arg `s' is domain name */ 2403 | ap.isnumeric = 0; 2404 | ap.a_name = s; 2405 | if (cp1) 2406 | *cp1 = '/'; 2407 | #ifdef INET6 2408 | if (cp2) { 2409 | *cp2 = ']'; 2410 | --s; 2411 | } 2412 | #endif 2413 | } 2414 | 2415 | if (Debug) { 2416 | printf("allowaddr: rule %d: ", NumAllowed); 2417 | if (ap.isnumeric) { 2418 | printf("numeric, "); 2419 | getnameinfo((struct sockaddr *)&ap.a_addr, 2420 | ((struct sockaddr *)&ap.a_addr)->sa_len, 2421 | ip, sizeof ip, NULL, 0, NI_NUMERICHOST); 2422 | printf("addr = %s, ", ip); 2423 | getnameinfo((struct sockaddr *)&ap.a_mask, 2424 | ((struct sockaddr *)&ap.a_mask)->sa_len, 2425 | ip, sizeof ip, NULL, 0, NI_NUMERICHOST); 2426 | printf("mask = %s; ", ip); 2427 | } else { 2428 | printf("domainname = %s; ", ap.a_name); 2429 | } 2430 | printf("port = %d\n", ap.port); 2431 | } 2432 | 2433 | if ((AllowedPeers = realloc(AllowedPeers, 2434 | ++NumAllowed * sizeof(struct allowedpeer))) 2435 | == NULL) { 2436 | logerror("realloc"); 2437 | exit(1); 2438 | } 2439 | memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer)); 2440 | return (0); 2441 | } 2442 | 2443 | /* 2444 | * Validate that the remote peer has permission to log to us. 2445 | */ 2446 | static int 2447 | validate(struct sockaddr *sa, const char *hname) 2448 | { 2449 | int i; 2450 | size_t l1, l2; 2451 | char *cp, name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV]; 2452 | struct allowedpeer *ap; 2453 | struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL; 2454 | #ifdef INET6 2455 | int j, reject; 2456 | struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL; 2457 | #endif 2458 | struct addrinfo hints, *res; 2459 | u_short sport; 2460 | 2461 | if (NumAllowed == 0) 2462 | /* traditional behaviour, allow everything */ 2463 | return (1); 2464 | 2465 | (void)strlcpy(name, hname, sizeof(name)); 2466 | memset(&hints, 0, sizeof(hints)); 2467 | hints.ai_family = PF_UNSPEC; 2468 | hints.ai_socktype = SOCK_DGRAM; 2469 | hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 2470 | if (getaddrinfo(name, NULL, &hints, &res) == 0) 2471 | freeaddrinfo(res); 2472 | else if (strchr(name, '.') == NULL) { 2473 | strlcat(name, ".", sizeof name); 2474 | strlcat(name, LocalDomain, sizeof name); 2475 | } 2476 | if (getnameinfo(sa, sa->sa_len, ip, sizeof ip, port, sizeof port, 2477 | NI_NUMERICHOST | NI_NUMERICSERV) != 0) 2478 | return (0); /* for safety, should not occur */ 2479 | dprintf("validate: dgram from IP %s, port %s, name %s;\n", 2480 | ip, port, name); 2481 | sport = atoi(port); 2482 | 2483 | /* now, walk down the list */ 2484 | for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) { 2485 | if (ap->port != 0 && ap->port != sport) { 2486 | dprintf("rejected in rule %d due to port mismatch.\n", i); 2487 | continue; 2488 | } 2489 | 2490 | if (ap->isnumeric) { 2491 | if (ap->a_addr.ss_family != sa->sa_family) { 2492 | dprintf("rejected in rule %d due to address family mismatch.\n", i); 2493 | continue; 2494 | } 2495 | if (ap->a_addr.ss_family == AF_INET) { 2496 | sin4 = (struct sockaddr_in *)sa; 2497 | a4p = (struct sockaddr_in *)&ap->a_addr; 2498 | m4p = (struct sockaddr_in *)&ap->a_mask; 2499 | if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr) 2500 | != a4p->sin_addr.s_addr) { 2501 | dprintf("rejected in rule %d due to IP mismatch.\n", i); 2502 | continue; 2503 | } 2504 | } 2505 | #ifdef INET6 2506 | else if (ap->a_addr.ss_family == AF_INET6) { 2507 | sin6 = (struct sockaddr_in6 *)sa; 2508 | a6p = (struct sockaddr_in6 *)&ap->a_addr; 2509 | m6p = (struct sockaddr_in6 *)&ap->a_mask; 2510 | if (a6p->sin6_scope_id != 0 && 2511 | sin6->sin6_scope_id != a6p->sin6_scope_id) { 2512 | dprintf("rejected in rule %d due to scope mismatch.\n", i); 2513 | continue; 2514 | } 2515 | reject = 0; 2516 | for (j = 0; j < 16; j += 4) { 2517 | if ((*(u_int32_t *)&sin6->sin6_addr.s6_addr[j] & *(u_int32_t *)&m6p->sin6_addr.s6_addr[j]) 2518 | != *(u_int32_t *)&a6p->sin6_addr.s6_addr[j]) { 2519 | ++reject; 2520 | break; 2521 | } 2522 | } 2523 | if (reject) { 2524 | dprintf("rejected in rule %d due to IP mismatch.\n", i); 2525 | continue; 2526 | } 2527 | } 2528 | #endif 2529 | else 2530 | continue; 2531 | } else { 2532 | cp = ap->a_name; 2533 | l1 = strlen(name); 2534 | if (*cp == '*') { 2535 | /* allow wildmatch */ 2536 | cp++; 2537 | l2 = strlen(cp); 2538 | if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) { 2539 | dprintf("rejected in rule %d due to name mismatch.\n", i); 2540 | continue; 2541 | } 2542 | } else { 2543 | /* exact match */ 2544 | l2 = strlen(cp); 2545 | if (l2 != l1 || memcmp(cp, name, l1) != 0) { 2546 | dprintf("rejected in rule %d due to name mismatch.\n", i); 2547 | continue; 2548 | } 2549 | } 2550 | } 2551 | dprintf("accepted in rule %d.\n", i); 2552 | return (1); /* hooray! */ 2553 | } 2554 | return (0); 2555 | } 2556 | 2557 | /* 2558 | * Fairly similar to popen(3), but returns an open descriptor, as 2559 | * opposed to a FILE *. 2560 | */ 2561 | static int 2562 | p_open(const char *prog, pid_t *rpid) 2563 | { 2564 | int pfd[2], nulldesc, i; 2565 | pid_t pid; 2566 | sigset_t omask, mask; 2567 | char *argv[4]; /* sh -c cmd NULL */ 2568 | char errmsg[200]; 2569 | 2570 | if (pipe(pfd) == -1) 2571 | return (-1); 2572 | if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1) 2573 | /* we are royally screwed anyway */ 2574 | return (-1); 2575 | 2576 | sigemptyset(&mask); 2577 | sigaddset(&mask, SIGALRM); 2578 | sigaddset(&mask, SIGHUP); 2579 | sigprocmask(SIG_BLOCK, &mask, &omask); 2580 | switch ((pid = fork())) { 2581 | case -1: 2582 | sigprocmask(SIG_SETMASK, &omask, 0); 2583 | close(nulldesc); 2584 | return (-1); 2585 | 2586 | case 0: 2587 | argv[0] = strdup("sh"); 2588 | argv[1] = strdup("-c"); 2589 | argv[2] = strdup(prog); 2590 | argv[3] = NULL; 2591 | if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) { 2592 | logerror("strdup"); 2593 | exit(1); 2594 | } 2595 | 2596 | alarm(0); 2597 | (void)setsid(); /* Avoid catching SIGHUPs. */ 2598 | 2599 | /* 2600 | * Throw away pending signals, and reset signal 2601 | * behaviour to standard values. 2602 | */ 2603 | signal(SIGALRM, SIG_IGN); 2604 | signal(SIGHUP, SIG_IGN); 2605 | sigprocmask(SIG_SETMASK, &omask, 0); 2606 | signal(SIGPIPE, SIG_DFL); 2607 | signal(SIGQUIT, SIG_DFL); 2608 | signal(SIGALRM, SIG_DFL); 2609 | signal(SIGHUP, SIG_DFL); 2610 | 2611 | dup2(pfd[0], STDIN_FILENO); 2612 | dup2(nulldesc, STDOUT_FILENO); 2613 | dup2(nulldesc, STDERR_FILENO); 2614 | for (i = getdtablesize(); i > 2; i--) 2615 | (void)close(i); 2616 | 2617 | (void)execvp(_PATH_BSHELL, argv); 2618 | _exit(255); 2619 | } 2620 | 2621 | sigprocmask(SIG_SETMASK, &omask, 0); 2622 | close(nulldesc); 2623 | close(pfd[0]); 2624 | /* 2625 | * Avoid blocking on a hung pipe. With O_NONBLOCK, we are 2626 | * supposed to get an EWOULDBLOCK on writev(2), which is 2627 | * caught by the logic above anyway, which will in turn close 2628 | * the pipe, and fork a new logging subprocess if necessary. 2629 | * The stale subprocess will be killed some time later unless 2630 | * it terminated itself due to closing its input pipe (so we 2631 | * get rid of really dead puppies). 2632 | */ 2633 | if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) { 2634 | /* This is bad. */ 2635 | (void)snprintf(errmsg, sizeof errmsg, 2636 | "Warning: cannot change pipe to PID %d to " 2637 | "non-blocking behaviour.", 2638 | (int)pid); 2639 | logerror(errmsg); 2640 | } 2641 | *rpid = pid; 2642 | return (pfd[1]); 2643 | } 2644 | 2645 | static void 2646 | deadq_enter(pid_t pid, const char *name) 2647 | { 2648 | dq_t p; 2649 | int status; 2650 | 2651 | /* 2652 | * Be paranoid, if we can't signal the process, don't enter it 2653 | * into the dead queue (perhaps it's already dead). If possible, 2654 | * we try to fetch and log the child's status. 2655 | */ 2656 | if (kill(pid, 0) != 0) { 2657 | if (waitpid(pid, &status, WNOHANG) > 0) 2658 | log_deadchild(pid, status, name); 2659 | return; 2660 | } 2661 | 2662 | p = malloc(sizeof(struct deadq_entry)); 2663 | if (p == NULL) { 2664 | logerror("malloc"); 2665 | exit(1); 2666 | } 2667 | 2668 | p->dq_pid = pid; 2669 | p->dq_timeout = DQ_TIMO_INIT; 2670 | TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries); 2671 | } 2672 | 2673 | static int 2674 | deadq_remove(pid_t pid) 2675 | { 2676 | dq_t q; 2677 | 2678 | TAILQ_FOREACH(q, &deadq_head, dq_entries) { 2679 | if (q->dq_pid == pid) { 2680 | TAILQ_REMOVE(&deadq_head, q, dq_entries); 2681 | free(q); 2682 | return (1); 2683 | } 2684 | } 2685 | 2686 | return (0); 2687 | } 2688 | 2689 | static void 2690 | log_deadchild(pid_t pid, int status, const char *name) 2691 | { 2692 | int code; 2693 | char buf[256]; 2694 | const char *reason; 2695 | 2696 | errno = 0; /* Keep strerror() stuff out of logerror messages. */ 2697 | if (WIFSIGNALED(status)) { 2698 | reason = "due to signal"; 2699 | code = WTERMSIG(status); 2700 | } else { 2701 | reason = "with status"; 2702 | code = WEXITSTATUS(status); 2703 | if (code == 0) 2704 | return; 2705 | } 2706 | (void)snprintf(buf, sizeof buf, 2707 | "Logging subprocess %d (%s) exited %s %d.", 2708 | pid, name, reason, code); 2709 | logerror(buf); 2710 | } 2711 | 2712 | static int * 2713 | socksetup(int af, const char *bindhostname) 2714 | { 2715 | struct addrinfo hints, *res, *r; 2716 | int error, maxs, *s, *socks; 2717 | 2718 | memset(&hints, 0, sizeof(hints)); 2719 | hints.ai_flags = AI_PASSIVE; 2720 | hints.ai_family = af; 2721 | hints.ai_socktype = SOCK_DGRAM; 2722 | error = getaddrinfo(bindhostname, "syslog", &hints, &res); 2723 | if (error) { 2724 | logerror(gai_strerror(error)); 2725 | errno = 0; 2726 | die(0); 2727 | } 2728 | 2729 | /* Count max number of sockets we may open */ 2730 | for (maxs = 0, r = res; r; r = r->ai_next, maxs++); 2731 | socks = malloc((maxs+1) * sizeof(int)); 2732 | if (socks == NULL) { 2733 | logerror("couldn't allocate memory for sockets"); 2734 | die(0); 2735 | } 2736 | 2737 | *socks = 0; /* num of sockets counter at start of array */ 2738 | s = socks + 1; 2739 | for (r = res; r; r = r->ai_next) { 2740 | int on = 1; 2741 | *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 2742 | if (*s < 0) { 2743 | logerror("socket"); 2744 | continue; 2745 | } 2746 | if (r->ai_family == AF_INET6) { 2747 | if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 2748 | (char *)&on, sizeof (on)) < 0) { 2749 | logerror("setsockopt"); 2750 | close(*s); 2751 | continue; 2752 | } 2753 | } 2754 | if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 2755 | (char *)&on, sizeof (on)) < 0) { 2756 | logerror("setsockopt"); 2757 | close(*s); 2758 | continue; 2759 | } 2760 | if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 2761 | close(*s); 2762 | logerror("bind"); 2763 | continue; 2764 | } 2765 | 2766 | double_rbuf(*s); 2767 | 2768 | (*socks)++; 2769 | s++; 2770 | } 2771 | 2772 | if (*socks == 0) { 2773 | free(socks); 2774 | if (Debug) 2775 | return (NULL); 2776 | else 2777 | die(0); 2778 | } 2779 | if (res) 2780 | freeaddrinfo(res); 2781 | 2782 | return (socks); 2783 | } 2784 | 2785 | static void 2786 | double_rbuf(int fd) 2787 | { 2788 | socklen_t slen, len; 2789 | 2790 | if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, &slen) == 0) { 2791 | len *= 2; 2792 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, slen); 2793 | } 2794 | } 2795 | --------------------------------------------------------------------------------