1994-05-26 05:23:31 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1983, 1988, 1993, 1994
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef lint
|
1995-10-12 17:18:39 +00:00
|
|
|
static const char copyright[] =
|
1994-05-26 05:23:31 +00:00
|
|
|
"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
|
|
|
|
The Regents of the University of California. All rights reserved.\n";
|
1997-10-20 12:55:49 +00:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
#if 0
|
1994-05-26 05:23:31 +00:00
|
|
|
static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
|
1997-10-20 12:55:49 +00:00
|
|
|
#endif
|
1995-10-12 17:18:39 +00:00
|
|
|
static const char rcsid[] =
|
1999-08-28 01:35:59 +00:00
|
|
|
"$FreeBSD$";
|
1994-05-26 05:23:31 +00:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* syslogd -- log system messages
|
|
|
|
*
|
|
|
|
* This program implements a system log. It takes a series of lines.
|
|
|
|
* Each line may have a priority, signified as "<n>" as
|
|
|
|
* the first characters of the line. If this is
|
|
|
|
* not present, a default priority is used.
|
|
|
|
*
|
|
|
|
* To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
|
|
|
|
* cause it to reread its configuration file.
|
|
|
|
*
|
|
|
|
* Defined Constants:
|
|
|
|
*
|
|
|
|
* MAXLINE -- the maximimum line length that can be handled.
|
|
|
|
* DEFUPRI -- the default priority for user messages
|
|
|
|
* DEFSPRI -- the default priority for kernel messages
|
|
|
|
*
|
|
|
|
* Author: Eric Allman
|
|
|
|
* extensive changes by Ralph Campbell
|
|
|
|
* more extensive changes by Eric Allman (again)
|
1994-12-29 22:02:25 +00:00
|
|
|
* Extension to log by program name as well as facility and priority
|
|
|
|
* by Peter da Silva.
|
1998-07-22 06:15:19 +00:00
|
|
|
* -u and -v by Harlan Stenn.
|
|
|
|
* Priority comparison code by Harlan Stenn.
|
1994-05-26 05:23:31 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAXLINE 1024 /* maximum line length */
|
|
|
|
#define MAXSVLINE 120 /* maximum saved line length */
|
|
|
|
#define DEFUPRI (LOG_USER|LOG_NOTICE)
|
|
|
|
#define DEFSPRI (LOG_KERN|LOG_CRIT)
|
|
|
|
#define TIMERINTVL 30 /* interval for checking flush, mark */
|
1996-11-18 21:48:29 +00:00
|
|
|
#define TTYMSGTIME 1 /* timed out passed to ttymsg */
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/socket.h>
|
1997-02-22 12:59:36 +00:00
|
|
|
#include <sys/queue.h>
|
1994-05-26 05:23:31 +00:00
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
1994-12-29 22:02:25 +00:00
|
|
|
#include <sys/syslimits.h>
|
|
|
|
#include <paths.h>
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include <ctype.h>
|
1997-10-20 12:55:49 +00:00
|
|
|
#include <err.h>
|
1994-05-26 05:23:31 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
1997-05-03 22:17:43 +00:00
|
|
|
#include <sysexits.h>
|
1994-05-26 05:23:31 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <utmp.h>
|
|
|
|
#include "pathnames.h"
|
|
|
|
|
|
|
|
#define SYSLOG_NAMES
|
|
|
|
#include <sys/syslog.h>
|
1996-10-28 08:25:13 +00:00
|
|
|
|
2000-12-16 18:25:48 +00:00
|
|
|
#ifdef NI_WITHSCOPEID
|
|
|
|
static const int withscopeid = NI_WITHSCOPEID;
|
|
|
|
#else
|
|
|
|
static const int withscopeid = 0;
|
|
|
|
#endif
|
|
|
|
|
1995-10-12 17:18:39 +00:00
|
|
|
const char *ConfFile = _PATH_LOGCONF;
|
|
|
|
const char *PidFile = _PATH_LOGPID;
|
|
|
|
const char ctty[] = _PATH_CONSOLE;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
#define dprintf if (Debug) printf
|
|
|
|
|
|
|
|
#define MAXUNAMES 20 /* maximum number of user names */
|
|
|
|
|
1998-06-25 19:39:19 +00:00
|
|
|
#define MAXFUNIX 20
|
|
|
|
|
|
|
|
int nfunix = 1;
|
|
|
|
char *funixn[MAXFUNIX] = { _PATH_LOG };
|
|
|
|
int funix[MAXFUNIX];
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/*
|
|
|
|
* Flags to logmsg().
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define IGN_CONS 0x001 /* don't print on console */
|
|
|
|
#define SYNC_FILE 0x002 /* do fsync on file after printing */
|
|
|
|
#define ADDDATE 0x004 /* add a date to the message */
|
|
|
|
#define MARK 0x008 /* this message is a mark */
|
1998-06-24 23:50:20 +00:00
|
|
|
#define ISKERNEL 0x010 /* kernel generated message */
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This structure represents the files that will have log
|
|
|
|
* copies printed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct filed {
|
|
|
|
struct filed *f_next; /* next in linked list */
|
|
|
|
short f_type; /* entry type, see below */
|
|
|
|
short f_file; /* file descriptor */
|
|
|
|
time_t f_time; /* time this was last written */
|
2000-07-24 14:10:17 +00:00
|
|
|
char *f_host; /* host from which to recd. */
|
1994-05-26 05:23:31 +00:00
|
|
|
u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
|
1998-07-22 06:15:19 +00:00
|
|
|
u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */
|
|
|
|
#define PRI_LT 0x1
|
|
|
|
#define PRI_EQ 0x2
|
|
|
|
#define PRI_GT 0x4
|
1994-12-29 22:02:25 +00:00
|
|
|
char *f_program; /* program this applies to */
|
1994-05-26 05:23:31 +00:00
|
|
|
union {
|
|
|
|
char f_uname[MAXUNAMES][UT_NAMESIZE+1];
|
|
|
|
struct {
|
2001-03-09 21:24:13 +00:00
|
|
|
char f_hname[MAXHOSTNAMELEN];
|
2000-12-16 18:25:48 +00:00
|
|
|
struct addrinfo *f_addr;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
} f_forw; /* forwarding address */
|
|
|
|
char f_fname[MAXPATHLEN];
|
1997-02-22 12:59:36 +00:00
|
|
|
struct {
|
|
|
|
char f_pname[MAXPATHLEN];
|
|
|
|
pid_t f_pid;
|
|
|
|
} f_pipe;
|
1994-05-26 05:23:31 +00:00
|
|
|
} f_un;
|
|
|
|
char f_prevline[MAXSVLINE]; /* last message logged */
|
|
|
|
char f_lasttime[16]; /* time of last occurrence */
|
2001-03-09 21:24:13 +00:00
|
|
|
char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */
|
1994-05-26 05:23:31 +00:00
|
|
|
int f_prevpri; /* pri of f_prevline */
|
|
|
|
int f_prevlen; /* length of f_prevline */
|
|
|
|
int f_prevcount; /* repetition cnt of prevline */
|
|
|
|
int f_repeatcount; /* number of "repeated" msgs */
|
|
|
|
};
|
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
/*
|
|
|
|
* Queue of about-to-be dead processes we should watch out for.
|
|
|
|
*/
|
|
|
|
|
2000-05-26 02:09:24 +00:00
|
|
|
TAILQ_HEAD(stailhead, deadq_entry) deadq_head;
|
1997-02-22 12:59:36 +00:00
|
|
|
struct stailhead *deadq_headp;
|
|
|
|
|
|
|
|
struct deadq_entry {
|
|
|
|
pid_t dq_pid;
|
|
|
|
int dq_timeout;
|
2000-05-26 02:09:24 +00:00
|
|
|
TAILQ_ENTRY(deadq_entry) dq_entries;
|
1997-02-22 12:59:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The timeout to apply to processes waiting on the dead queue. Unit
|
|
|
|
* of measure is `mark intervals', i.e. 20 minutes by default.
|
|
|
|
* Processes on the dead queue will be terminated after that time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DQ_TIMO_INIT 2
|
|
|
|
|
|
|
|
typedef struct deadq_entry *dq_t;
|
|
|
|
|
|
|
|
|
1997-05-03 22:17:43 +00:00
|
|
|
/*
|
|
|
|
* Struct to hold records of network addresses that are allowed to log
|
|
|
|
* to us.
|
|
|
|
*/
|
|
|
|
struct allowedpeer {
|
|
|
|
int isnumeric;
|
|
|
|
u_short port;
|
|
|
|
union {
|
|
|
|
struct {
|
2000-12-16 18:25:48 +00:00
|
|
|
struct sockaddr_storage addr;
|
|
|
|
struct sockaddr_storage mask;
|
1997-05-03 22:17:43 +00:00
|
|
|
} numeric;
|
|
|
|
char *name;
|
|
|
|
} u;
|
|
|
|
#define a_addr u.numeric.addr
|
|
|
|
#define a_mask u.numeric.mask
|
|
|
|
#define a_name u.name
|
|
|
|
};
|
|
|
|
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/*
|
|
|
|
* Intervals at which we flush out "message repeated" messages,
|
|
|
|
* in seconds after previous message is logged. After each flush,
|
|
|
|
* we move to the next interval until we reach the largest.
|
|
|
|
*/
|
|
|
|
int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
|
|
|
|
#define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
|
|
|
|
#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
|
|
|
|
#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
|
|
|
|
(f)->f_repeatcount = MAXREPEAT; \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* values for f_type */
|
|
|
|
#define F_UNUSED 0 /* unused entry */
|
|
|
|
#define F_FILE 1 /* regular file */
|
|
|
|
#define F_TTY 2 /* terminal */
|
|
|
|
#define F_CONSOLE 3 /* console terminal */
|
|
|
|
#define F_FORW 4 /* remote machine */
|
|
|
|
#define F_USERS 5 /* list of users */
|
|
|
|
#define F_WALL 6 /* everyone logged on */
|
1997-02-22 12:59:36 +00:00
|
|
|
#define F_PIPE 7 /* pipe to program */
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
char *TypeNames[8] = {
|
1994-05-26 05:23:31 +00:00
|
|
|
"UNUSED", "FILE", "TTY", "CONSOLE",
|
1997-02-22 12:59:36 +00:00
|
|
|
"FORW", "USERS", "WALL", "PIPE"
|
1994-05-26 05:23:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct filed *Files;
|
|
|
|
struct filed consfile;
|
|
|
|
|
|
|
|
int Debug; /* debug flag */
|
2000-07-29 01:37:37 +00:00
|
|
|
int resolve = 1; /* resolve hostname */
|
2001-03-09 21:24:13 +00:00
|
|
|
char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */
|
1994-05-26 05:23:31 +00:00
|
|
|
char *LocalDomain; /* our local domain name */
|
2000-12-16 18:25:48 +00:00
|
|
|
int *finet = NULL; /* Internet datagram socket */
|
1999-05-02 12:47:09 +00:00
|
|
|
int fklog = -1; /* /dev/klog */
|
1994-05-26 05:23:31 +00:00
|
|
|
int Initialized = 0; /* set when we have initialized ourselves */
|
|
|
|
int MarkInterval = 20 * 60; /* interval between marks in seconds */
|
|
|
|
int MarkSeq = 0; /* mark sequence number */
|
1998-04-24 17:32:23 +00:00
|
|
|
int SecureMode = 0; /* when true, receive only unix domain socks */
|
2000-12-16 18:25:48 +00:00
|
|
|
#ifdef INET6
|
|
|
|
int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
|
|
|
|
#else
|
|
|
|
int family = PF_INET; /* protocol family (IPv4 only) */
|
|
|
|
#endif
|
|
|
|
int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */
|
1996-07-22 16:35:50 +00:00
|
|
|
|
1995-11-14 23:39:39 +00:00
|
|
|
char bootfile[MAXLINE+1]; /* booted kernel file */
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1997-05-03 22:17:43 +00:00
|
|
|
struct allowedpeer *AllowedPeers;
|
|
|
|
int NumAllowed = 0; /* # of AllowedPeer entries */
|
|
|
|
|
1998-07-22 06:15:19 +00:00
|
|
|
int UniquePriority = 0; /* Only log specified priority? */
|
|
|
|
int LogFacPri = 0; /* Put facility and priority in log message: */
|
|
|
|
/* 0=no, 1=numeric, 2=names */
|
2000-10-17 10:19:42 +00:00
|
|
|
int KeepKernFac = 0; /* Keep remotely logged kernel facility */
|
1998-07-22 06:15:19 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
volatile sig_atomic_t MarkSet, WantDie;
|
|
|
|
|
1997-05-03 22:17:43 +00:00
|
|
|
int allowaddr __P((char *));
|
2000-07-24 14:10:17 +00:00
|
|
|
void cfline __P((char *, struct filed *, char *, char *));
|
2000-12-16 18:25:48 +00:00
|
|
|
char *cvthname __P((struct sockaddr *));
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
void deadq_enter __P((pid_t, const char *));
|
|
|
|
int deadq_remove __P((pid_t));
|
1994-05-26 05:23:31 +00:00
|
|
|
int decode __P((const char *, CODE *));
|
|
|
|
void die __P((int));
|
2001-09-01 07:57:35 +00:00
|
|
|
void dodie __P((int));
|
1994-05-26 05:23:31 +00:00
|
|
|
void domark __P((int));
|
|
|
|
void fprintlog __P((struct filed *, int, char *));
|
2000-12-16 18:25:48 +00:00
|
|
|
int* socksetup __P((int));
|
1994-05-26 05:23:31 +00:00
|
|
|
void init __P((int));
|
1995-08-23 04:54:49 +00:00
|
|
|
void logerror __P((const char *));
|
1994-05-26 05:23:31 +00:00
|
|
|
void logmsg __P((int, char *, char *, int));
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
void log_deadchild __P((pid_t, int, const char *));
|
2001-09-01 07:57:35 +00:00
|
|
|
void markit __P((void));
|
1994-05-26 05:23:31 +00:00
|
|
|
void printline __P((char *, char *));
|
|
|
|
void printsys __P((char *));
|
1997-02-22 12:59:36 +00:00
|
|
|
int p_open __P((char *, pid_t *));
|
1999-05-02 12:47:09 +00:00
|
|
|
void readklog __P((void));
|
1994-05-26 05:23:31 +00:00
|
|
|
void reapchild __P((int));
|
|
|
|
char *ttymsg __P((struct iovec *, int, char *, int));
|
1997-10-20 12:55:49 +00:00
|
|
|
static void usage __P((void));
|
2000-12-16 18:25:48 +00:00
|
|
|
int validate __P((struct sockaddr *, const char *));
|
|
|
|
static void unmapped __P((struct sockaddr *));
|
1994-05-26 05:23:31 +00:00
|
|
|
void wallmsg __P((struct filed *, struct iovec *));
|
1996-10-05 15:20:51 +00:00
|
|
|
int waitdaemon __P((int, int, int));
|
1996-11-18 21:48:29 +00:00
|
|
|
void timedout __P((int));
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
main(argc, argv)
|
|
|
|
int argc;
|
|
|
|
char *argv[];
|
|
|
|
{
|
2001-09-01 07:57:35 +00:00
|
|
|
int ch, i, fdsrmax = 0, l;
|
1994-05-26 05:23:31 +00:00
|
|
|
struct sockaddr_un sunx, fromunix;
|
2000-12-16 18:25:48 +00:00
|
|
|
struct sockaddr_storage frominet;
|
2001-09-01 07:57:35 +00:00
|
|
|
fd_set *fdsr = NULL;
|
1994-05-26 05:23:31 +00:00
|
|
|
FILE *fp;
|
2001-08-27 21:37:15 +00:00
|
|
|
char *hname, line[MAXLINE + 1];
|
1996-10-05 15:20:51 +00:00
|
|
|
struct timeval tv, *tvp;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
struct sigaction sact;
|
|
|
|
sigset_t mask;
|
1998-08-25 21:16:47 +00:00
|
|
|
pid_t ppid = 1;
|
2000-01-14 15:09:06 +00:00
|
|
|
socklen_t len;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
2001-05-10 15:48:33 +00:00
|
|
|
while ((ch = getopt(argc, argv, "46Aa:df:kl:m:np:P:suv")) != -1)
|
2000-08-03 15:19:27 +00:00
|
|
|
switch (ch) {
|
2000-12-16 18:25:48 +00:00
|
|
|
case '4':
|
|
|
|
family = PF_INET;
|
|
|
|
break;
|
|
|
|
#ifdef INET6
|
|
|
|
case '6':
|
|
|
|
family = PF_INET6;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 'A':
|
|
|
|
send_to_all++;
|
|
|
|
break;
|
1997-05-03 22:17:43 +00:00
|
|
|
case 'a': /* allow specific network addresses only */
|
|
|
|
if (allowaddr(optarg) == -1)
|
|
|
|
usage();
|
|
|
|
break;
|
2000-08-03 15:19:27 +00:00
|
|
|
case 'd': /* debug */
|
|
|
|
Debug++;
|
|
|
|
break;
|
1994-05-26 05:23:31 +00:00
|
|
|
case 'f': /* configuration file */
|
|
|
|
ConfFile = optarg;
|
|
|
|
break;
|
2000-10-17 10:19:42 +00:00
|
|
|
case 'k': /* keep remote kern fac */
|
|
|
|
KeepKernFac = 1;
|
|
|
|
break;
|
2000-08-03 15:19:27 +00:00
|
|
|
case 'l':
|
|
|
|
if (nfunix < MAXFUNIX)
|
|
|
|
funixn[nfunix++] = optarg;
|
|
|
|
else
|
|
|
|
warnx("out of descriptors, ignoring %s",
|
|
|
|
optarg);
|
|
|
|
break;
|
1994-05-26 05:23:31 +00:00
|
|
|
case 'm': /* mark interval */
|
|
|
|
MarkInterval = atoi(optarg) * 60;
|
|
|
|
break;
|
2000-07-29 04:02:34 +00:00
|
|
|
case 'n':
|
|
|
|
resolve = 0;
|
|
|
|
break;
|
1994-05-26 05:23:31 +00:00
|
|
|
case 'p': /* path */
|
1998-06-25 19:39:19 +00:00
|
|
|
funixn[0] = optarg;
|
1994-05-26 05:23:31 +00:00
|
|
|
break;
|
2001-05-10 15:48:33 +00:00
|
|
|
case 'P': /* path for alt. PID */
|
|
|
|
PidFile = optarg;
|
|
|
|
break;
|
1996-07-22 16:35:50 +00:00
|
|
|
case 's': /* no network mode */
|
|
|
|
SecureMode++;
|
|
|
|
break;
|
1998-07-22 06:15:19 +00:00
|
|
|
case 'u': /* only log specified priority */
|
|
|
|
UniquePriority++;
|
|
|
|
break;
|
|
|
|
case 'v': /* log facility and priority */
|
|
|
|
LogFacPri++;
|
|
|
|
break;
|
1994-05-26 05:23:31 +00:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if ((argc -= optind) != 0)
|
|
|
|
usage();
|
|
|
|
|
1996-10-05 15:20:51 +00:00
|
|
|
if (!Debug) {
|
|
|
|
ppid = waitdaemon(0, 0, 30);
|
|
|
|
if (ppid < 0)
|
|
|
|
err(1, "could not become daemon");
|
|
|
|
} else
|
1994-05-26 05:23:31 +00:00
|
|
|
setlinebuf(stdout);
|
|
|
|
|
1997-05-03 22:17:43 +00:00
|
|
|
if (NumAllowed)
|
|
|
|
endservent();
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
consfile.f_type = F_CONSOLE;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1,
|
|
|
|
sizeof(consfile.f_un.f_fname));
|
|
|
|
(void)strlcpy(bootfile, getbootfile(), sizeof(bootfile));
|
|
|
|
(void)signal(SIGTERM, dodie);
|
|
|
|
(void)signal(SIGINT, Debug ? dodie : SIG_IGN);
|
|
|
|
(void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
/*
|
|
|
|
* We don't want the SIGCHLD and SIGHUP handlers to interfere
|
|
|
|
* with each other; they are likely candidates for being called
|
|
|
|
* simultaneously (SIGHUP closes pipe descriptor, process dies,
|
|
|
|
* SIGCHLD happens).
|
|
|
|
*/
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGHUP);
|
|
|
|
sact.sa_handler = reapchild;
|
|
|
|
sact.sa_mask = mask;
|
2000-02-29 08:02:29 +00:00
|
|
|
sact.sa_flags = SA_RESTART;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
(void)sigaction(SIGCHLD, &sact, NULL);
|
1994-05-26 05:23:31 +00:00
|
|
|
(void)signal(SIGALRM, domark);
|
1997-02-22 12:59:36 +00:00
|
|
|
(void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */
|
1994-05-26 05:23:31 +00:00
|
|
|
(void)alarm(TIMERINTVL);
|
|
|
|
|
1997-04-26 00:00:33 +00:00
|
|
|
TAILQ_INIT(&deadq_head);
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
#ifndef SUN_LEN
|
|
|
|
#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
|
|
|
|
#endif
|
1998-06-25 19:39:19 +00:00
|
|
|
for (i = 0; i < nfunix; i++) {
|
|
|
|
memset(&sunx, 0, sizeof(sunx));
|
|
|
|
sunx.sun_family = AF_UNIX;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
|
1998-06-25 19:39:19 +00:00
|
|
|
funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
|
|
if (funix[i] < 0 ||
|
|
|
|
bind(funix[i], (struct sockaddr *)&sunx,
|
|
|
|
SUN_LEN(&sunx)) < 0 ||
|
|
|
|
chmod(funixn[i], 0666) < 0) {
|
|
|
|
(void) snprintf(line, sizeof line,
|
|
|
|
"cannot create %s", funixn[i]);
|
|
|
|
logerror(line);
|
|
|
|
dprintf("cannot create %s (%d)\n", funixn[i], errno);
|
|
|
|
if (i == 0)
|
|
|
|
die(0);
|
|
|
|
}
|
|
|
|
}
|
1999-05-04 18:03:59 +00:00
|
|
|
if (SecureMode <= 1)
|
2000-12-16 18:25:48 +00:00
|
|
|
finet = socksetup(family);
|
|
|
|
|
|
|
|
if (finet) {
|
|
|
|
if (SecureMode) {
|
|
|
|
for (i = 0; i < *finet; i++) {
|
|
|
|
if (shutdown(finet[i+1], SHUT_RD) < 0) {
|
|
|
|
logerror("shutdown");
|
|
|
|
if (!Debug)
|
|
|
|
die(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
dprintf("listening on inet and/or inet6 socket\n");
|
|
|
|
dprintf("sending on inet and/or inet6 socket\n");
|
2000-01-14 15:09:06 +00:00
|
|
|
}
|
1998-04-24 17:32:23 +00:00
|
|
|
|
1999-05-02 12:47:09 +00:00
|
|
|
if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
|
|
|
|
if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0)
|
|
|
|
fklog = -1;
|
|
|
|
if (fklog < 0)
|
1994-05-26 05:23:31 +00:00
|
|
|
dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
|
|
|
|
|
|
|
|
/* tuck my process id away */
|
|
|
|
fp = fopen(PidFile, "w");
|
|
|
|
if (fp != NULL) {
|
|
|
|
fprintf(fp, "%d\n", getpid());
|
|
|
|
(void) fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
dprintf("off & running....\n");
|
|
|
|
|
|
|
|
init(0);
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
/* prevent SIGHUP and SIGCHLD handlers from running in parallel */
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
sact.sa_handler = init;
|
|
|
|
sact.sa_mask = mask;
|
2000-02-29 08:02:29 +00:00
|
|
|
sact.sa_flags = SA_RESTART;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
(void)sigaction(SIGHUP, &sact, NULL);
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1996-10-05 15:20:51 +00:00
|
|
|
tvp = &tv;
|
|
|
|
tv.tv_sec = tv.tv_usec = 0;
|
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
if (fklog != -1 && fklog > fdsrmax)
|
|
|
|
fdsrmax = fklog;
|
|
|
|
if (finet && !SecureMode) {
|
|
|
|
for (i = 0; i < *finet; i++) {
|
|
|
|
if (finet[i+1] != -1 && finet[i+1] > fdsrmax)
|
|
|
|
fdsrmax = finet[i+1];
|
1998-06-25 19:39:19 +00:00
|
|
|
}
|
2001-09-01 07:57:35 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < nfunix; i++) {
|
|
|
|
if (funix[i] != -1 && funix[i] > fdsrmax)
|
|
|
|
fdsrmax = funix[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS),
|
|
|
|
sizeof(fd_mask));
|
|
|
|
if (fdsr == NULL)
|
|
|
|
errx(1, "calloc fd_set");
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (MarkSet)
|
|
|
|
markit();
|
|
|
|
if (WantDie)
|
|
|
|
die(WantDie);
|
|
|
|
|
|
|
|
bzero(fdsr, howmany(fdsrmax+1, NFDBITS) *
|
|
|
|
sizeof(fd_mask));
|
|
|
|
|
|
|
|
if (fklog != -1)
|
|
|
|
FD_SET(fklog, fdsr);
|
2000-12-16 18:25:48 +00:00
|
|
|
if (finet && !SecureMode) {
|
|
|
|
for (i = 0; i < *finet; i++) {
|
2001-09-01 07:57:35 +00:00
|
|
|
if (finet[i+1] != -1)
|
|
|
|
FD_SET(finet[i+1], fdsr);
|
2000-12-16 18:25:48 +00:00
|
|
|
}
|
1998-06-25 19:39:19 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < nfunix; i++) {
|
2001-09-01 07:57:35 +00:00
|
|
|
if (funix[i] != -1)
|
|
|
|
FD_SET(funix[i], fdsr);
|
1998-06-25 19:39:19 +00:00
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
i = select(fdsrmax+1, fdsr, NULL, NULL, tvp);
|
|
|
|
switch (i) {
|
|
|
|
case 0:
|
1996-10-05 15:20:51 +00:00
|
|
|
if (tvp) {
|
|
|
|
tvp = NULL;
|
|
|
|
if (ppid != 1)
|
|
|
|
kill(ppid, SIGALRM);
|
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
continue;
|
2001-09-01 07:57:35 +00:00
|
|
|
case -1:
|
1994-05-26 05:23:31 +00:00
|
|
|
if (errno != EINTR)
|
|
|
|
logerror("select");
|
|
|
|
continue;
|
|
|
|
}
|
2001-09-01 07:57:35 +00:00
|
|
|
if (fklog != -1 && FD_ISSET(fklog, fdsr))
|
1999-05-02 12:47:09 +00:00
|
|
|
readklog();
|
2000-12-16 18:25:48 +00:00
|
|
|
if (finet && !SecureMode) {
|
|
|
|
for (i = 0; i < *finet; i++) {
|
2001-09-01 07:57:35 +00:00
|
|
|
if (FD_ISSET(finet[i+1], fdsr)) {
|
2000-12-16 18:25:48 +00:00
|
|
|
len = sizeof(frominet);
|
|
|
|
l = recvfrom(finet[i+1], line, MAXLINE,
|
|
|
|
0, (struct sockaddr *)&frominet,
|
|
|
|
&len);
|
|
|
|
if (l > 0) {
|
|
|
|
line[l] = '\0';
|
|
|
|
hname = cvthname((struct sockaddr *)&frominet);
|
|
|
|
unmapped((struct sockaddr *)&frominet);
|
|
|
|
if (validate((struct sockaddr *)&frominet, hname))
|
|
|
|
printline(hname, line);
|
|
|
|
} else if (l < 0 && errno != EINTR)
|
|
|
|
logerror("recvfrom inet");
|
|
|
|
}
|
|
|
|
}
|
1995-05-30 03:57:47 +00:00
|
|
|
}
|
1998-06-25 19:39:19 +00:00
|
|
|
for (i = 0; i < nfunix; i++) {
|
2001-09-01 07:57:35 +00:00
|
|
|
if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) {
|
1998-06-25 19:39:19 +00:00
|
|
|
len = sizeof(fromunix);
|
|
|
|
l = recvfrom(funix[i], line, MAXLINE, 0,
|
|
|
|
(struct sockaddr *)&fromunix, &len);
|
|
|
|
if (l > 0) {
|
|
|
|
line[l] = '\0';
|
|
|
|
printline(LocalHostName, line);
|
|
|
|
} else if (l < 0 && errno != EINTR)
|
|
|
|
logerror("recvfrom unix");
|
|
|
|
}
|
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
2001-09-01 07:57:35 +00:00
|
|
|
if (fdsr)
|
|
|
|
free(fdsr);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
2000-12-16 18:25:48 +00:00
|
|
|
static void
|
|
|
|
unmapped(sa)
|
|
|
|
struct sockaddr *sa;
|
|
|
|
{
|
|
|
|
struct sockaddr_in6 *sin6;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
|
|
|
if (sa->sa_family != AF_INET6)
|
|
|
|
return;
|
|
|
|
if (sa->sa_len != sizeof(struct sockaddr_in6) ||
|
|
|
|
sizeof(sin) > sa->sa_len)
|
|
|
|
return;
|
|
|
|
sin6 = (struct sockaddr_in6 *)sa;
|
|
|
|
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
sin.sin_len = sizeof(struct sockaddr_in);
|
|
|
|
memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
|
|
|
|
sizeof(sin.sin_addr));
|
|
|
|
sin.sin_port = sin6->sin6_port;
|
|
|
|
|
|
|
|
memcpy(sa, &sin, sin.sin_len);
|
|
|
|
}
|
|
|
|
|
1997-10-20 12:55:49 +00:00
|
|
|
static void
|
1994-05-26 05:23:31 +00:00
|
|
|
usage()
|
|
|
|
{
|
|
|
|
|
1998-06-25 19:39:19 +00:00
|
|
|
fprintf(stderr, "%s\n%s\n%s\n",
|
2000-12-16 18:25:48 +00:00
|
|
|
"usage: syslogd [-46Adnsuv] [-a allowed_peer] [-f config_file]",
|
2001-05-10 15:48:33 +00:00
|
|
|
" [-m mark_interval] [-l log_socket]",
|
|
|
|
" [-p log_socket] [-P pid_file]");
|
1994-05-26 05:23:31 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take a raw input line, decode the message, and print the message
|
|
|
|
* on the appropriate log files.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
printline(hname, msg)
|
|
|
|
char *hname;
|
|
|
|
char *msg;
|
|
|
|
{
|
|
|
|
int c, pri;
|
|
|
|
char *p, *q, line[MAXLINE + 1];
|
|
|
|
|
|
|
|
/* test for special codes */
|
|
|
|
pri = DEFUPRI;
|
|
|
|
p = msg;
|
|
|
|
if (*p == '<') {
|
|
|
|
pri = 0;
|
|
|
|
while (isdigit(*++p))
|
|
|
|
pri = 10 * pri + (*p - '0');
|
|
|
|
if (*p == '>')
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
|
|
|
|
pri = DEFUPRI;
|
|
|
|
|
|
|
|
/* don't allow users to log kernel messages */
|
2000-10-17 10:19:42 +00:00
|
|
|
if (LOG_FAC(pri) == LOG_KERN && !KeepKernFac)
|
1994-05-26 05:23:31 +00:00
|
|
|
pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
|
|
|
|
|
|
|
|
q = line;
|
|
|
|
|
2000-04-18 01:12:27 +00:00
|
|
|
while ((c = (unsigned char)*p++) != '\0' &&
|
2000-09-19 21:22:20 +00:00
|
|
|
q < &line[sizeof(line) - 4]) {
|
2000-04-18 01:12:27 +00:00
|
|
|
if ((c & 0x80) && c < 0xA0) {
|
|
|
|
c &= 0x7F;
|
|
|
|
*q++ = 'M';
|
|
|
|
*q++ = '-';
|
|
|
|
}
|
|
|
|
if (isascii(c) && iscntrl(c)) {
|
1994-05-26 05:23:31 +00:00
|
|
|
if (c == '\n')
|
|
|
|
*q++ = ' ';
|
|
|
|
else if (c == '\t')
|
|
|
|
*q++ = '\t';
|
|
|
|
else {
|
|
|
|
*q++ = '^';
|
|
|
|
*q++ = c ^ 0100;
|
|
|
|
}
|
2000-04-18 01:12:27 +00:00
|
|
|
} else
|
1994-05-26 05:23:31 +00:00
|
|
|
*q++ = c;
|
2000-04-18 01:12:27 +00:00
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
logmsg(pri, line, hname, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-05-02 12:47:09 +00:00
|
|
|
* Read /dev/klog while data are available, split into lines.
|
1994-05-26 05:23:31 +00:00
|
|
|
*/
|
|
|
|
void
|
1999-05-02 12:47:09 +00:00
|
|
|
readklog()
|
1994-05-26 05:23:31 +00:00
|
|
|
{
|
1999-05-02 12:47:09 +00:00
|
|
|
char *p, *q, line[MAXLINE + 1];
|
1999-05-06 13:57:57 +00:00
|
|
|
int len, i;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1999-05-06 13:57:57 +00:00
|
|
|
len = 0;
|
1999-05-02 12:47:09 +00:00
|
|
|
for (;;) {
|
1999-05-06 13:57:57 +00:00
|
|
|
i = read(fklog, line + len, MAXLINE - 1 - len);
|
1999-05-02 12:47:09 +00:00
|
|
|
if (i > 0)
|
1999-05-06 13:57:57 +00:00
|
|
|
line[i + len] = '\0';
|
1999-05-02 12:47:09 +00:00
|
|
|
else if (i < 0 && errno != EINTR && errno != EAGAIN) {
|
|
|
|
logerror("klog");
|
|
|
|
fklog = -1;
|
|
|
|
break;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
|
|
|
|
for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) {
|
|
|
|
*q = '\0';
|
|
|
|
printsys(p);
|
|
|
|
}
|
1999-05-06 13:57:57 +00:00
|
|
|
len = strlen(p);
|
|
|
|
if (len >= MAXLINE - 1) {
|
1999-05-02 12:47:09 +00:00
|
|
|
printsys(p);
|
1999-05-06 13:57:57 +00:00
|
|
|
len = 0;
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
1999-05-06 13:57:57 +00:00
|
|
|
if (len > 0)
|
|
|
|
memmove(line, p, len + 1);
|
1999-05-02 12:47:09 +00:00
|
|
|
}
|
1999-05-06 13:57:57 +00:00
|
|
|
if (len > 0)
|
1999-05-02 12:47:09 +00:00
|
|
|
printsys(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take a raw input line from /dev/klog, format similar to syslog().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
printsys(p)
|
|
|
|
char *p;
|
|
|
|
{
|
|
|
|
int pri, flags;
|
|
|
|
|
|
|
|
flags = ISKERNEL | SYNC_FILE | ADDDATE; /* fsync after write */
|
|
|
|
pri = DEFSPRI;
|
|
|
|
if (*p == '<') {
|
|
|
|
pri = 0;
|
|
|
|
while (isdigit(*++p))
|
|
|
|
pri = 10 * pri + (*p - '0');
|
|
|
|
if (*p == '>')
|
|
|
|
++p;
|
2001-02-04 15:16:08 +00:00
|
|
|
if ((pri & LOG_FACMASK) == LOG_CONSOLE)
|
2000-11-25 21:00:58 +00:00
|
|
|
flags |= IGN_CONS;
|
1999-05-02 12:47:09 +00:00
|
|
|
} else {
|
|
|
|
/* kernel printf's come out on console */
|
|
|
|
flags |= IGN_CONS;
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
1999-05-02 12:47:09 +00:00
|
|
|
if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
|
|
|
|
pri = DEFSPRI;
|
|
|
|
logmsg(pri, p, LocalHostName, flags);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
time_t now;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Log a message to the appropriate log files, users, etc. based on
|
|
|
|
* the priority.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
logmsg(pri, msg, from, flags)
|
|
|
|
int pri;
|
|
|
|
char *msg, *from;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
struct filed *f;
|
1998-06-24 23:50:20 +00:00
|
|
|
int i, fac, msglen, omask, prilev;
|
1994-05-26 05:23:31 +00:00
|
|
|
char *timestamp;
|
1994-12-29 22:02:25 +00:00
|
|
|
char prog[NAME_MAX+1];
|
1998-06-24 23:50:20 +00:00
|
|
|
char buf[MAXLINE+1];
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
|
|
|
|
pri, flags, from, msg);
|
|
|
|
|
|
|
|
omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if msg looks non-standard.
|
|
|
|
*/
|
|
|
|
msglen = strlen(msg);
|
|
|
|
if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
|
|
|
|
msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
|
|
|
|
flags |= ADDDATE;
|
|
|
|
|
|
|
|
(void)time(&now);
|
|
|
|
if (flags & ADDDATE)
|
|
|
|
timestamp = ctime(&now) + 4;
|
|
|
|
else {
|
|
|
|
timestamp = msg;
|
|
|
|
msg += 16;
|
|
|
|
msglen -= 16;
|
|
|
|
}
|
|
|
|
|
1994-12-29 22:02:25 +00:00
|
|
|
/* skip leading blanks */
|
2000-08-03 15:19:27 +00:00
|
|
|
while (isspace(*msg)) {
|
1994-12-29 22:02:25 +00:00
|
|
|
msg++;
|
|
|
|
msglen--;
|
|
|
|
}
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/* extract facility and priority level */
|
|
|
|
if (flags & MARK)
|
|
|
|
fac = LOG_NFACILITIES;
|
|
|
|
else
|
|
|
|
fac = LOG_FAC(pri);
|
|
|
|
prilev = LOG_PRI(pri);
|
|
|
|
|
1994-12-29 22:02:25 +00:00
|
|
|
/* extract program name */
|
2000-08-03 15:19:27 +00:00
|
|
|
for (i = 0; i < NAME_MAX; i++) {
|
|
|
|
if (!isalnum(msg[i]))
|
1994-12-29 22:02:25 +00:00
|
|
|
break;
|
|
|
|
prog[i] = msg[i];
|
|
|
|
}
|
|
|
|
prog[i] = 0;
|
|
|
|
|
1998-06-24 23:50:20 +00:00
|
|
|
/* add kernel prefix for kernel messages */
|
|
|
|
if (flags & ISKERNEL) {
|
|
|
|
snprintf(buf, sizeof(buf), "%s: %s", bootfile, msg);
|
|
|
|
msg = buf;
|
|
|
|
msglen = strlen(buf);
|
|
|
|
}
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/* log the message to the particular outputs */
|
|
|
|
if (!Initialized) {
|
|
|
|
f = &consfile;
|
|
|
|
f->f_file = open(ctty, O_WRONLY, 0);
|
|
|
|
|
|
|
|
if (f->f_file >= 0) {
|
|
|
|
fprintlog(f, flags, msg);
|
|
|
|
(void)close(f->f_file);
|
|
|
|
}
|
|
|
|
(void)sigsetmask(omask);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (f = Files; f; f = f->f_next) {
|
|
|
|
/* skip messages that are incorrect priority */
|
1998-07-22 06:15:19 +00:00
|
|
|
if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
|
|
|
|
||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
|
|
|
|
||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
|
|
|
|
)
|
|
|
|
|| f->f_pmask[fac] == INTERNAL_NOPRI)
|
1994-05-26 05:23:31 +00:00
|
|
|
continue;
|
2000-07-24 14:10:17 +00:00
|
|
|
/* skip messages with the incorrect hostname */
|
|
|
|
if (f->f_host)
|
|
|
|
switch (f->f_host[0]) {
|
|
|
|
case '+':
|
2000-08-03 15:19:27 +00:00
|
|
|
if (strcmp(from, f->f_host + 1) != 0)
|
2000-07-24 14:10:17 +00:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case '-':
|
2000-08-03 15:19:27 +00:00
|
|
|
if (strcmp(from, f->f_host + 1) == 0)
|
2000-07-24 14:10:17 +00:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1994-12-29 22:02:25 +00:00
|
|
|
/* skip messages with the incorrect program name */
|
2000-08-03 15:19:27 +00:00
|
|
|
if (f->f_program)
|
|
|
|
if (strcmp(prog, f->f_program) != 0)
|
1994-12-29 22:02:25 +00:00
|
|
|
continue;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* don't output marks to recently written files */
|
|
|
|
if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* suppress duplicate lines to this file
|
|
|
|
*/
|
|
|
|
if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
|
|
|
|
!strcmp(msg, f->f_prevline) &&
|
1999-08-17 01:25:16 +00:00
|
|
|
!strcasecmp(from, f->f_prevhost)) {
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_lasttime, timestamp, 16);
|
1994-05-26 05:23:31 +00:00
|
|
|
f->f_prevcount++;
|
|
|
|
dprintf("msg repeated %d times, %ld sec of %d\n",
|
1998-07-06 20:28:08 +00:00
|
|
|
f->f_prevcount, (long)(now - f->f_time),
|
1994-05-26 05:23:31 +00:00
|
|
|
repeatinterval[f->f_repeatcount]);
|
|
|
|
/*
|
|
|
|
* If domark would have logged this by now,
|
|
|
|
* flush it now (so we don't hold isolated messages),
|
|
|
|
* but back off so we'll flush less often
|
|
|
|
* in the future.
|
|
|
|
*/
|
|
|
|
if (now > REPEATTIME(f)) {
|
|
|
|
fprintlog(f, flags, (char *)NULL);
|
|
|
|
BACKOFF(f);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* new line, save it */
|
|
|
|
if (f->f_prevcount)
|
|
|
|
fprintlog(f, 0, (char *)NULL);
|
|
|
|
f->f_repeatcount = 0;
|
1996-07-22 16:35:50 +00:00
|
|
|
f->f_prevpri = pri;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_lasttime, timestamp, 16);
|
|
|
|
(void)strlcpy(f->f_prevhost, from,
|
|
|
|
sizeof(f->f_prevhost));
|
1994-05-26 05:23:31 +00:00
|
|
|
if (msglen < MAXSVLINE) {
|
|
|
|
f->f_prevlen = msglen;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
|
1994-05-26 05:23:31 +00:00
|
|
|
fprintlog(f, flags, (char *)NULL);
|
|
|
|
} else {
|
|
|
|
f->f_prevline[0] = 0;
|
|
|
|
f->f_prevlen = 0;
|
|
|
|
fprintlog(f, flags, msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(void)sigsetmask(omask);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fprintlog(f, flags, msg)
|
|
|
|
struct filed *f;
|
|
|
|
int flags;
|
|
|
|
char *msg;
|
|
|
|
{
|
1998-07-22 06:15:19 +00:00
|
|
|
struct iovec iov[7];
|
1994-05-26 05:23:31 +00:00
|
|
|
struct iovec *v;
|
2000-12-16 18:25:48 +00:00
|
|
|
struct addrinfo *r;
|
|
|
|
int i, l, lsent = 0;
|
1994-05-26 05:23:31 +00:00
|
|
|
char line[MAXLINE + 1], repbuf[80], greetings[200];
|
1996-11-18 21:48:29 +00:00
|
|
|
char *msgret;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
v = iov;
|
|
|
|
if (f->f_type == F_WALL) {
|
|
|
|
v->iov_base = greetings;
|
1997-09-19 22:22:04 +00:00
|
|
|
v->iov_len = snprintf(greetings, sizeof greetings,
|
1994-05-26 05:23:31 +00:00
|
|
|
"\r\n\7Message from syslogd@%s at %.24s ...\r\n",
|
|
|
|
f->f_prevhost, ctime(&now));
|
2001-08-20 13:24:39 +00:00
|
|
|
if (v->iov_len > 0)
|
|
|
|
v++;
|
1994-05-26 05:23:31 +00:00
|
|
|
v->iov_base = "";
|
|
|
|
v->iov_len = 0;
|
|
|
|
v++;
|
|
|
|
} else {
|
|
|
|
v->iov_base = f->f_lasttime;
|
|
|
|
v->iov_len = 15;
|
|
|
|
v++;
|
|
|
|
v->iov_base = " ";
|
|
|
|
v->iov_len = 1;
|
|
|
|
v++;
|
|
|
|
}
|
1998-07-22 06:15:19 +00:00
|
|
|
|
|
|
|
if (LogFacPri) {
|
|
|
|
static char fp_buf[30]; /* Hollow laugh */
|
|
|
|
int fac = f->f_prevpri & LOG_FACMASK;
|
|
|
|
int pri = LOG_PRI(f->f_prevpri);
|
2001-05-21 22:13:55 +00:00
|
|
|
const char *f_s = NULL;
|
1998-07-22 06:15:19 +00:00
|
|
|
char f_n[5]; /* Hollow laugh */
|
2001-05-21 22:13:55 +00:00
|
|
|
const char *p_s = NULL;
|
1998-07-22 06:15:19 +00:00
|
|
|
char p_n[5]; /* Hollow laugh */
|
|
|
|
|
|
|
|
if (LogFacPri > 1) {
|
|
|
|
CODE *c;
|
|
|
|
|
2000-01-13 12:59:58 +00:00
|
|
|
for (c = facilitynames; c->c_name; c++) {
|
1998-07-22 06:15:19 +00:00
|
|
|
if (c->c_val == fac) {
|
|
|
|
f_s = c->c_name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2000-01-13 12:59:58 +00:00
|
|
|
for (c = prioritynames; c->c_name; c++) {
|
1998-07-22 06:15:19 +00:00
|
|
|
if (c->c_val == pri) {
|
|
|
|
p_s = c->c_name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!f_s) {
|
|
|
|
snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac));
|
|
|
|
f_s = f_n;
|
|
|
|
}
|
|
|
|
if (!p_s) {
|
|
|
|
snprintf(p_n, sizeof p_n, "%d", pri);
|
|
|
|
p_s = p_n;
|
|
|
|
}
|
|
|
|
snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s);
|
|
|
|
v->iov_base = fp_buf;
|
|
|
|
v->iov_len = strlen(fp_buf);
|
|
|
|
} else {
|
|
|
|
v->iov_base="";
|
|
|
|
v->iov_len = 0;
|
|
|
|
}
|
|
|
|
v++;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
v->iov_base = f->f_prevhost;
|
|
|
|
v->iov_len = strlen(v->iov_base);
|
|
|
|
v++;
|
|
|
|
v->iov_base = " ";
|
|
|
|
v->iov_len = 1;
|
|
|
|
v++;
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
v->iov_base = msg;
|
|
|
|
v->iov_len = strlen(msg);
|
|
|
|
} else if (f->f_prevcount > 1) {
|
|
|
|
v->iov_base = repbuf;
|
2001-09-01 07:57:35 +00:00
|
|
|
v->iov_len = snprintf(repbuf, sizeof repbuf,
|
|
|
|
"last message repeated %d times", f->f_prevcount);
|
1994-05-26 05:23:31 +00:00
|
|
|
} else {
|
|
|
|
v->iov_base = f->f_prevline;
|
|
|
|
v->iov_len = f->f_prevlen;
|
|
|
|
}
|
|
|
|
v++;
|
|
|
|
|
|
|
|
dprintf("Logging to %s", TypeNames[f->f_type]);
|
|
|
|
f->f_time = now;
|
|
|
|
|
|
|
|
switch (f->f_type) {
|
|
|
|
case F_UNUSED:
|
|
|
|
dprintf("\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F_FORW:
|
|
|
|
dprintf(" %s\n", f->f_un.f_forw.f_hname);
|
1998-06-27 21:45:59 +00:00
|
|
|
/* check for local vs remote messages */
|
1999-08-17 01:25:16 +00:00
|
|
|
if (strcasecmp(f->f_prevhost, LocalHostName))
|
1998-06-27 21:45:59 +00:00
|
|
|
l = snprintf(line, sizeof line - 1,
|
|
|
|
"<%d>%.15s Forwarded from %s: %s",
|
|
|
|
f->f_prevpri, iov[0].iov_base, f->f_prevhost,
|
1998-07-27 13:04:14 +00:00
|
|
|
iov[5].iov_base);
|
1998-06-27 21:45:59 +00:00
|
|
|
else
|
|
|
|
l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
|
1998-07-27 13:04:14 +00:00
|
|
|
f->f_prevpri, iov[0].iov_base, iov[5].iov_base);
|
2001-08-20 13:24:39 +00:00
|
|
|
if (l < 0)
|
|
|
|
l = 0;
|
|
|
|
else if (l > MAXLINE)
|
1994-05-26 05:23:31 +00:00
|
|
|
l = MAXLINE;
|
2000-12-16 18:25:48 +00:00
|
|
|
|
|
|
|
if (finet) {
|
|
|
|
for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) {
|
|
|
|
for (i = 0; i < *finet; i++) {
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* should we check AF first, or just
|
|
|
|
* trial and error? FWD
|
|
|
|
*/
|
|
|
|
if (r->ai_family ==
|
|
|
|
address_family_of(finet[i+1]))
|
|
|
|
#endif
|
|
|
|
lsent = sendto(finet[i+1], line, l, 0,
|
|
|
|
r->ai_addr, r->ai_addrlen);
|
|
|
|
if (lsent == l)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lsent == l && !send_to_all)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lsent != l) {
|
|
|
|
int e = errno;
|
|
|
|
(void)close(f->f_file);
|
|
|
|
errno = e;
|
|
|
|
f->f_type = F_UNUSED;
|
|
|
|
logerror("sendto");
|
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F_FILE:
|
|
|
|
dprintf(" %s\n", f->f_un.f_fname);
|
1996-11-18 21:48:29 +00:00
|
|
|
v->iov_base = "\n";
|
|
|
|
v->iov_len = 1;
|
1998-07-22 06:15:19 +00:00
|
|
|
if (writev(f->f_file, iov, 7) < 0) {
|
1994-05-26 05:23:31 +00:00
|
|
|
int e = errno;
|
|
|
|
(void)close(f->f_file);
|
1996-11-18 21:48:29 +00:00
|
|
|
f->f_type = F_UNUSED;
|
|
|
|
errno = e;
|
|
|
|
logerror(f->f_un.f_fname);
|
1994-05-26 05:23:31 +00:00
|
|
|
} else if (flags & SYNC_FILE)
|
|
|
|
(void)fsync(f->f_file);
|
|
|
|
break;
|
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
case F_PIPE:
|
|
|
|
dprintf(" %s\n", f->f_un.f_pipe.f_pname);
|
|
|
|
v->iov_base = "\n";
|
|
|
|
v->iov_len = 1;
|
|
|
|
if (f->f_un.f_pipe.f_pid == 0) {
|
|
|
|
if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
|
|
|
|
&f->f_un.f_pipe.f_pid)) < 0) {
|
|
|
|
f->f_type = F_UNUSED;
|
|
|
|
logerror(f->f_un.f_pipe.f_pname);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1998-07-22 06:15:19 +00:00
|
|
|
if (writev(f->f_file, iov, 7) < 0) {
|
1997-02-22 12:59:36 +00:00
|
|
|
int e = errno;
|
|
|
|
(void)close(f->f_file);
|
|
|
|
if (f->f_un.f_pipe.f_pid > 0)
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
deadq_enter(f->f_un.f_pipe.f_pid,
|
|
|
|
f->f_un.f_pipe.f_pname);
|
1997-02-22 12:59:36 +00:00
|
|
|
f->f_un.f_pipe.f_pid = 0;
|
|
|
|
errno = e;
|
|
|
|
logerror(f->f_un.f_pipe.f_pname);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1996-11-26 02:24:42 +00:00
|
|
|
case F_CONSOLE:
|
|
|
|
if (flags & IGN_CONS) {
|
|
|
|
dprintf(" (ignored)\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
1996-11-18 21:48:29 +00:00
|
|
|
case F_TTY:
|
1996-11-26 02:24:42 +00:00
|
|
|
dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
|
1996-11-18 21:48:29 +00:00
|
|
|
v->iov_base = "\r\n";
|
|
|
|
v->iov_len = 2;
|
|
|
|
|
|
|
|
errno = 0; /* ttymsg() only sometimes returns an errno */
|
1998-07-22 06:15:19 +00:00
|
|
|
if ((msgret = ttymsg(iov, 7, f->f_un.f_fname, 10))) {
|
1996-11-18 21:48:29 +00:00
|
|
|
f->f_type = F_UNUSED;
|
|
|
|
logerror(msgret);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
case F_USERS:
|
|
|
|
case F_WALL:
|
|
|
|
dprintf("\n");
|
|
|
|
v->iov_base = "\r\n";
|
|
|
|
v->iov_len = 2;
|
|
|
|
wallmsg(f, iov);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
f->f_prevcount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* WALLMSG -- Write a message to the world at large
|
|
|
|
*
|
|
|
|
* Write the specified message to either the entire
|
|
|
|
* world, or a list of approved users.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
wallmsg(f, iov)
|
|
|
|
struct filed *f;
|
|
|
|
struct iovec *iov;
|
|
|
|
{
|
|
|
|
static int reenter; /* avoid calling ourselves */
|
|
|
|
FILE *uf;
|
|
|
|
struct utmp ut;
|
|
|
|
int i;
|
|
|
|
char *p;
|
|
|
|
char line[sizeof(ut.ut_line) + 1];
|
|
|
|
|
|
|
|
if (reenter++)
|
|
|
|
return;
|
|
|
|
if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
|
|
|
|
logerror(_PATH_UTMP);
|
|
|
|
reenter = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* NOSTRICT */
|
|
|
|
while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
|
|
|
|
if (ut.ut_name[0] == '\0')
|
|
|
|
continue;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(line, ut.ut_line, sizeof(line));
|
1994-05-26 05:23:31 +00:00
|
|
|
if (f->f_type == F_WALL) {
|
1998-07-22 06:15:19 +00:00
|
|
|
if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) != NULL) {
|
1994-05-26 05:23:31 +00:00
|
|
|
errno = 0; /* already in msg */
|
|
|
|
logerror(p);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* should we send the message to this user? */
|
|
|
|
for (i = 0; i < MAXUNAMES; i++) {
|
|
|
|
if (!f->f_un.f_uname[i][0])
|
|
|
|
break;
|
|
|
|
if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
|
|
|
|
UT_NAMESIZE)) {
|
1998-07-22 06:15:19 +00:00
|
|
|
if ((p = ttymsg(iov, 7, line, TTYMSGTIME))
|
1996-07-22 16:35:50 +00:00
|
|
|
!= NULL) {
|
1994-05-26 05:23:31 +00:00
|
|
|
errno = 0; /* already in msg */
|
|
|
|
logerror(p);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(void)fclose(uf);
|
|
|
|
reenter = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
reapchild(signo)
|
|
|
|
int signo;
|
|
|
|
{
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
int status;
|
1997-02-22 12:59:36 +00:00
|
|
|
pid_t pid;
|
|
|
|
struct filed *f;
|
|
|
|
|
|
|
|
while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
|
|
|
|
if (!Initialized)
|
|
|
|
/* Don't tell while we are initting. */
|
|
|
|
continue;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
/* First, look if it's a process from the dead queue. */
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
if (deadq_remove(pid))
|
|
|
|
goto oncemore;
|
1997-02-22 12:59:36 +00:00
|
|
|
|
|
|
|
/* Now, look in list of active processes. */
|
|
|
|
for (f = Files; f; f = f->f_next)
|
|
|
|
if (f->f_type == F_PIPE &&
|
|
|
|
f->f_un.f_pipe.f_pid == pid) {
|
|
|
|
(void)close(f->f_file);
|
|
|
|
f->f_un.f_pipe.f_pid = 0;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
log_deadchild(pid, status,
|
|
|
|
f->f_un.f_pipe.f_pname);
|
1997-02-22 12:59:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
oncemore:
|
2000-01-14 15:09:06 +00:00
|
|
|
continue;
|
1997-02-22 12:59:36 +00:00
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return a printable representation of a host address.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
cvthname(f)
|
2000-12-16 18:25:48 +00:00
|
|
|
struct sockaddr *f;
|
1994-05-26 05:23:31 +00:00
|
|
|
{
|
2000-12-16 18:25:48 +00:00
|
|
|
int error;
|
1998-12-28 00:39:14 +00:00
|
|
|
sigset_t omask, nmask;
|
1994-05-26 05:23:31 +00:00
|
|
|
char *p;
|
2000-12-16 18:25:48 +00:00
|
|
|
static char hname[NI_MAXHOST], ip[NI_MAXHOST];
|
1994-05-26 05:23:31 +00:00
|
|
|
|
2000-12-16 18:25:48 +00:00
|
|
|
error = getnameinfo((struct sockaddr *)f,
|
|
|
|
((struct sockaddr *)f)->sa_len,
|
|
|
|
ip, sizeof ip, NULL, 0,
|
|
|
|
NI_NUMERICHOST | withscopeid);
|
|
|
|
dprintf("cvthname(%s)\n", ip);
|
1994-05-26 05:23:31 +00:00
|
|
|
|
2000-12-16 18:25:48 +00:00
|
|
|
if (error) {
|
|
|
|
dprintf("Malformed from address %s\n", gai_strerror(error));
|
1994-05-26 05:23:31 +00:00
|
|
|
return ("???");
|
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
if (!resolve)
|
|
|
|
return (ip);
|
|
|
|
|
1998-12-28 00:39:14 +00:00
|
|
|
sigemptyset(&nmask);
|
|
|
|
sigaddset(&nmask, SIGHUP);
|
|
|
|
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
2000-12-16 18:25:48 +00:00
|
|
|
error = getnameinfo((struct sockaddr *)f,
|
|
|
|
((struct sockaddr *)f)->sa_len,
|
|
|
|
hname, sizeof hname, NULL, 0,
|
|
|
|
NI_NAMEREQD | withscopeid);
|
1998-12-28 00:39:14 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &omask, NULL);
|
2000-12-16 18:25:48 +00:00
|
|
|
if (error) {
|
|
|
|
dprintf("Host name for your address (%s) unknown\n", ip);
|
|
|
|
return (ip);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
if ((p = strchr(hname, '.')) && strcasecmp(p + 1, LocalDomain) == 0)
|
1994-05-26 05:23:31 +00:00
|
|
|
*p = '\0';
|
2000-12-16 18:25:48 +00:00
|
|
|
return (hname);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2001-09-01 07:57:35 +00:00
|
|
|
dodie(signo)
|
1994-05-26 05:23:31 +00:00
|
|
|
int signo;
|
|
|
|
{
|
1997-02-22 12:59:36 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
WantDie = signo;
|
|
|
|
}
|
1997-02-22 12:59:36 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
void
|
|
|
|
domark(signo)
|
|
|
|
int signo;
|
|
|
|
{
|
1997-02-22 12:59:36 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
MarkSet = 1;
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print syslogd errors some place.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
logerror(type)
|
1995-08-23 04:54:49 +00:00
|
|
|
const char *type;
|
1994-05-26 05:23:31 +00:00
|
|
|
{
|
1997-02-22 12:59:36 +00:00
|
|
|
char buf[512];
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
if (errno)
|
|
|
|
(void)snprintf(buf,
|
1997-09-19 22:22:04 +00:00
|
|
|
sizeof buf, "syslogd: %s: %s", type, strerror(errno));
|
1994-05-26 05:23:31 +00:00
|
|
|
else
|
1997-09-19 22:22:04 +00:00
|
|
|
(void)snprintf(buf, sizeof buf, "syslogd: %s", type);
|
1994-05-26 05:23:31 +00:00
|
|
|
errno = 0;
|
|
|
|
dprintf("%s\n", buf);
|
|
|
|
logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
die(signo)
|
|
|
|
int signo;
|
|
|
|
{
|
|
|
|
struct filed *f;
|
1997-03-14 01:27:02 +00:00
|
|
|
int was_initialized;
|
1994-05-26 05:23:31 +00:00
|
|
|
char buf[100];
|
1998-06-25 19:39:19 +00:00
|
|
|
int i;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
1997-03-14 01:27:02 +00:00
|
|
|
was_initialized = Initialized;
|
1997-02-22 12:59:36 +00:00
|
|
|
Initialized = 0; /* Don't log SIGCHLDs. */
|
1994-05-26 05:23:31 +00:00
|
|
|
for (f = Files; f != NULL; f = f->f_next) {
|
|
|
|
/* flush any pending output */
|
|
|
|
if (f->f_prevcount)
|
|
|
|
fprintlog(f, 0, (char *)NULL);
|
1997-02-22 12:59:36 +00:00
|
|
|
if (f->f_type == F_PIPE)
|
|
|
|
(void)close(f->f_file);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
1997-03-14 01:27:02 +00:00
|
|
|
Initialized = was_initialized;
|
1994-05-26 05:23:31 +00:00
|
|
|
if (signo) {
|
|
|
|
dprintf("syslogd: exiting on signal %d\n", signo);
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo);
|
1994-05-26 05:23:31 +00:00
|
|
|
errno = 0;
|
|
|
|
logerror(buf);
|
|
|
|
}
|
1998-06-25 19:39:19 +00:00
|
|
|
for (i = 0; i < nfunix; i++)
|
|
|
|
if (funixn[i] && funix[i] != -1)
|
|
|
|
(void)unlink(funixn[i]);
|
1996-11-18 21:48:29 +00:00
|
|
|
exit(1);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* INIT -- Initialize syslogd from configuration table
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
init(signo)
|
|
|
|
int signo;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
FILE *cf;
|
|
|
|
struct filed *f, *next, **nextp;
|
|
|
|
char *p;
|
|
|
|
char cline[LINE_MAX];
|
1994-12-29 22:02:25 +00:00
|
|
|
char prog[NAME_MAX+1];
|
2001-03-09 21:24:13 +00:00
|
|
|
char host[MAXHOSTNAMELEN];
|
2001-08-27 21:37:15 +00:00
|
|
|
char oldLocalHostName[MAXHOSTNAMELEN];
|
|
|
|
char hostMsg[2*MAXHOSTNAMELEN+40];
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
dprintf("init\n");
|
|
|
|
|
2001-08-27 21:37:15 +00:00
|
|
|
/*
|
|
|
|
* Load hostname (may have changed).
|
|
|
|
*/
|
|
|
|
if (signo != 0)
|
|
|
|
(void)strlcpy(oldLocalHostName, LocalHostName,
|
|
|
|
sizeof(oldLocalHostName));
|
|
|
|
if (gethostname(LocalHostName, sizeof(LocalHostName)))
|
|
|
|
err(EX_OSERR, "gethostname() failed");
|
|
|
|
if ((p = strchr(LocalHostName, '.')) != NULL) {
|
|
|
|
*p++ = '\0';
|
|
|
|
LocalDomain = p;
|
|
|
|
} else
|
|
|
|
LocalDomain = "";
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/*
|
|
|
|
* Close all open log files.
|
|
|
|
*/
|
|
|
|
Initialized = 0;
|
|
|
|
for (f = Files; f != NULL; f = next) {
|
|
|
|
/* flush any pending output */
|
|
|
|
if (f->f_prevcount)
|
|
|
|
fprintlog(f, 0, (char *)NULL);
|
|
|
|
|
|
|
|
switch (f->f_type) {
|
|
|
|
case F_FILE:
|
|
|
|
case F_FORW:
|
1996-11-26 02:24:42 +00:00
|
|
|
case F_CONSOLE:
|
|
|
|
case F_TTY:
|
1996-12-10 17:52:23 +00:00
|
|
|
(void)close(f->f_file);
|
1996-11-26 02:24:42 +00:00
|
|
|
break;
|
1997-02-22 12:59:36 +00:00
|
|
|
case F_PIPE:
|
|
|
|
(void)close(f->f_file);
|
|
|
|
if (f->f_un.f_pipe.f_pid > 0)
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
deadq_enter(f->f_un.f_pipe.f_pid,
|
|
|
|
f->f_un.f_pipe.f_pname);
|
1997-02-22 12:59:36 +00:00
|
|
|
f->f_un.f_pipe.f_pid = 0;
|
|
|
|
break;
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
next = f->f_next;
|
2000-08-03 15:19:27 +00:00
|
|
|
if (f->f_program) free(f->f_program);
|
2000-07-24 14:10:17 +00:00
|
|
|
if (f->f_host) free(f->f_host);
|
1994-05-26 05:23:31 +00:00
|
|
|
free((char *)f);
|
|
|
|
}
|
|
|
|
Files = NULL;
|
|
|
|
nextp = &Files;
|
|
|
|
|
|
|
|
/* open the configuration file */
|
|
|
|
if ((cf = fopen(ConfFile, "r")) == NULL) {
|
|
|
|
dprintf("cannot open %s\n", ConfFile);
|
|
|
|
*nextp = (struct filed *)calloc(1, sizeof(*f));
|
2000-07-24 14:10:17 +00:00
|
|
|
cfline("*.ERR\t/dev/console", *nextp, "*", "*");
|
1994-05-26 05:23:31 +00:00
|
|
|
(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
|
2000-07-24 14:10:17 +00:00
|
|
|
cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
|
1994-05-26 05:23:31 +00:00
|
|
|
Initialized = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Foreach line in the conf table, open that file.
|
|
|
|
*/
|
|
|
|
f = NULL;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(host, "*", sizeof(host));
|
|
|
|
(void)strlcpy(prog, "*", sizeof(prog));
|
1994-05-26 05:23:31 +00:00
|
|
|
while (fgets(cline, sizeof(cline), cf) != NULL) {
|
|
|
|
/*
|
|
|
|
* check for end-of-section, comments, strip off trailing
|
1994-12-29 22:02:25 +00:00
|
|
|
* spaces and newline character. #!prog is treated specially:
|
|
|
|
* following lines apply only to that program.
|
1994-05-26 05:23:31 +00:00
|
|
|
*/
|
|
|
|
for (p = cline; isspace(*p); ++p)
|
|
|
|
continue;
|
1994-12-29 22:02:25 +00:00
|
|
|
if (*p == 0)
|
|
|
|
continue;
|
2000-08-03 15:19:27 +00:00
|
|
|
if (*p == '#') {
|
1994-12-29 22:02:25 +00:00
|
|
|
p++;
|
2000-07-24 14:10:17 +00:00
|
|
|
if (*p != '!' && *p != '+' && *p != '-')
|
1994-12-29 22:02:25 +00:00
|
|
|
continue;
|
|
|
|
}
|
2000-07-24 14:10:17 +00:00
|
|
|
if (*p == '+' || *p == '-') {
|
|
|
|
host[0] = *p++;
|
|
|
|
while (isspace(*p)) p++;
|
|
|
|
if ((!*p) || (*p == '*')) {
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(host, "*", sizeof(host));
|
2000-07-24 14:10:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*p == '@')
|
|
|
|
p = LocalHostName;
|
2001-03-09 21:24:13 +00:00
|
|
|
for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
|
2000-07-24 14:10:17 +00:00
|
|
|
if (!isalnum(*p) && *p != '.' && *p != '-')
|
|
|
|
break;
|
|
|
|
host[i] = *p++;
|
|
|
|
}
|
|
|
|
host[i] = '\0';
|
|
|
|
continue;
|
|
|
|
}
|
2000-08-03 15:19:27 +00:00
|
|
|
if (*p == '!') {
|
1994-12-29 22:02:25 +00:00
|
|
|
p++;
|
2000-08-03 15:19:27 +00:00
|
|
|
while (isspace(*p)) p++;
|
|
|
|
if ((!*p) || (*p == '*')) {
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(prog, "*", sizeof(prog));
|
1994-12-29 22:02:25 +00:00
|
|
|
continue;
|
|
|
|
}
|
2000-08-03 15:19:27 +00:00
|
|
|
for (i = 0; i < NAME_MAX; i++) {
|
|
|
|
if (!isalnum(p[i]))
|
1994-12-29 22:02:25 +00:00
|
|
|
break;
|
|
|
|
prog[i] = p[i];
|
|
|
|
}
|
|
|
|
prog[i] = 0;
|
1994-05-26 05:23:31 +00:00
|
|
|
continue;
|
1994-12-29 22:02:25 +00:00
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
for (p = strchr(cline, '\0'); isspace(*--p);)
|
|
|
|
continue;
|
|
|
|
*++p = '\0';
|
|
|
|
f = (struct filed *)calloc(1, sizeof(*f));
|
|
|
|
*nextp = f;
|
|
|
|
nextp = &f->f_next;
|
2000-07-24 14:10:17 +00:00
|
|
|
cfline(cline, f, prog, host);
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* close the configuration file */
|
|
|
|
(void)fclose(cf);
|
|
|
|
|
|
|
|
Initialized = 1;
|
|
|
|
|
|
|
|
if (Debug) {
|
|
|
|
for (f = Files; f; f = f->f_next) {
|
|
|
|
for (i = 0; i <= LOG_NFACILITIES; i++)
|
|
|
|
if (f->f_pmask[i] == INTERNAL_NOPRI)
|
|
|
|
printf("X ");
|
|
|
|
else
|
|
|
|
printf("%d ", f->f_pmask[i]);
|
|
|
|
printf("%s: ", TypeNames[f->f_type]);
|
|
|
|
switch (f->f_type) {
|
|
|
|
case F_FILE:
|
|
|
|
printf("%s", f->f_un.f_fname);
|
|
|
|
break;
|
|
|
|
|
1996-11-26 02:24:42 +00:00
|
|
|
case F_CONSOLE:
|
|
|
|
case F_TTY:
|
|
|
|
printf("%s%s", _PATH_DEV, f->f_un.f_fname);
|
|
|
|
break;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
case F_FORW:
|
|
|
|
printf("%s", f->f_un.f_forw.f_hname);
|
|
|
|
break;
|
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
case F_PIPE:
|
|
|
|
printf("%s", f->f_un.f_pipe.f_pname);
|
|
|
|
break;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
case F_USERS:
|
|
|
|
for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
|
|
|
|
printf("%s, ", f->f_un.f_uname[i]);
|
|
|
|
break;
|
|
|
|
}
|
2000-08-03 15:19:27 +00:00
|
|
|
if (f->f_program)
|
1994-12-29 22:02:25 +00:00
|
|
|
printf(" (%s)", f->f_program);
|
1994-05-26 05:23:31 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
|
|
|
|
dprintf("syslogd: restarted\n");
|
2001-08-27 21:37:15 +00:00
|
|
|
/*
|
|
|
|
* Log a change in hostname, but only on a restart.
|
|
|
|
*/
|
|
|
|
if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) {
|
|
|
|
(void)snprintf(hostMsg, sizeof(hostMsg),
|
|
|
|
"syslogd: hostname changed, \"%s\" to \"%s\"",
|
|
|
|
oldLocalHostName, LocalHostName);
|
|
|
|
logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE);
|
|
|
|
dprintf("%s\n", hostMsg);
|
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Crack a configuration file line
|
|
|
|
*/
|
|
|
|
void
|
2000-07-24 14:10:17 +00:00
|
|
|
cfline(line, f, prog, host)
|
1994-05-26 05:23:31 +00:00
|
|
|
char *line;
|
|
|
|
struct filed *f;
|
1994-12-29 22:02:25 +00:00
|
|
|
char *prog;
|
2000-07-24 14:10:17 +00:00
|
|
|
char *host;
|
1994-05-26 05:23:31 +00:00
|
|
|
{
|
2000-12-16 18:25:48 +00:00
|
|
|
struct addrinfo hints, *res;
|
|
|
|
int error, i, pri;
|
1994-05-26 05:23:31 +00:00
|
|
|
char *bp, *p, *q;
|
|
|
|
char buf[MAXLINE], ebuf[100];
|
|
|
|
|
2000-07-24 14:10:17 +00:00
|
|
|
dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
errno = 0; /* keep strerror() stuff out of logerror messages */
|
|
|
|
|
|
|
|
/* clear out file entry */
|
|
|
|
memset(f, 0, sizeof(*f));
|
|
|
|
for (i = 0; i <= LOG_NFACILITIES; i++)
|
|
|
|
f->f_pmask[i] = INTERNAL_NOPRI;
|
|
|
|
|
2000-07-24 14:10:17 +00:00
|
|
|
/* save hostname if any */
|
2000-08-03 15:19:27 +00:00
|
|
|
if (host && *host == '*')
|
|
|
|
host = NULL;
|
2000-08-03 15:12:06 +00:00
|
|
|
if (host)
|
|
|
|
f->f_host = strdup(host);
|
2000-07-24 14:10:17 +00:00
|
|
|
|
1994-12-29 22:02:25 +00:00
|
|
|
/* save program name if any */
|
2000-08-03 15:19:27 +00:00
|
|
|
if (prog && *prog == '*')
|
|
|
|
prog = NULL;
|
|
|
|
if (prog)
|
|
|
|
f->f_program = strdup(prog);
|
1994-12-29 22:02:25 +00:00
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/* scan through the list of selectors */
|
1998-12-04 06:49:20 +00:00
|
|
|
for (p = line; *p && *p != '\t' && *p != ' ';) {
|
1998-07-22 06:15:19 +00:00
|
|
|
int pri_done;
|
|
|
|
int pri_cmp;
|
1994-05-26 05:23:31 +00:00
|
|
|
|
|
|
|
/* find the end of this facility name list */
|
1998-12-04 06:49:20 +00:00
|
|
|
for (q = p; *q && *q != '\t' && *q != ' ' && *q++ != '.'; )
|
1994-05-26 05:23:31 +00:00
|
|
|
continue;
|
|
|
|
|
1998-07-22 06:15:19 +00:00
|
|
|
/* get the priority comparison */
|
|
|
|
pri_cmp = 0;
|
|
|
|
pri_done = 0;
|
|
|
|
while (!pri_done) {
|
|
|
|
switch (*q) {
|
|
|
|
case '<':
|
|
|
|
pri_cmp |= PRI_LT;
|
|
|
|
q++;
|
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
pri_cmp |= PRI_EQ;
|
|
|
|
q++;
|
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
pri_cmp |= PRI_GT;
|
|
|
|
q++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pri_done++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!pri_cmp)
|
|
|
|
pri_cmp = (UniquePriority)
|
|
|
|
? (PRI_EQ)
|
|
|
|
: (PRI_EQ | PRI_GT)
|
|
|
|
;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
/* collect priority name */
|
1998-12-04 06:49:20 +00:00
|
|
|
for (bp = buf; *q && !strchr("\t,; ", *q); )
|
1994-05-26 05:23:31 +00:00
|
|
|
*bp++ = *q++;
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
|
|
/* skip cruft */
|
1998-12-04 06:49:20 +00:00
|
|
|
while (strchr(",;", *q))
|
1994-05-26 05:23:31 +00:00
|
|
|
q++;
|
|
|
|
|
|
|
|
/* decode priority name */
|
|
|
|
if (*buf == '*')
|
|
|
|
pri = LOG_PRIMASK + 1;
|
|
|
|
else {
|
|
|
|
pri = decode(buf, prioritynames);
|
|
|
|
if (pri < 0) {
|
1997-09-19 22:22:04 +00:00
|
|
|
(void)snprintf(ebuf, sizeof ebuf,
|
1994-05-26 05:23:31 +00:00
|
|
|
"unknown priority name \"%s\"", buf);
|
|
|
|
logerror(ebuf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* scan facilities */
|
1998-12-04 06:49:20 +00:00
|
|
|
while (*p && !strchr("\t.; ", *p)) {
|
|
|
|
for (bp = buf; *p && !strchr("\t,;. ", *p); )
|
1994-05-26 05:23:31 +00:00
|
|
|
*bp++ = *p++;
|
|
|
|
*bp = '\0';
|
1998-07-22 06:15:19 +00:00
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
if (*buf == '*')
|
1998-07-22 06:15:19 +00:00
|
|
|
for (i = 0; i < LOG_NFACILITIES; i++) {
|
1994-05-26 05:23:31 +00:00
|
|
|
f->f_pmask[i] = pri;
|
1998-07-22 06:15:19 +00:00
|
|
|
f->f_pcmp[i] = pri_cmp;
|
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
else {
|
|
|
|
i = decode(buf, facilitynames);
|
|
|
|
if (i < 0) {
|
1997-09-19 22:22:04 +00:00
|
|
|
(void)snprintf(ebuf, sizeof ebuf,
|
1994-05-26 05:23:31 +00:00
|
|
|
"unknown facility name \"%s\"",
|
|
|
|
buf);
|
|
|
|
logerror(ebuf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
f->f_pmask[i >> 3] = pri;
|
1998-07-22 06:15:19 +00:00
|
|
|
f->f_pcmp[i >> 3] = pri_cmp;
|
1994-05-26 05:23:31 +00:00
|
|
|
}
|
|
|
|
while (*p == ',' || *p == ' ')
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = q;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip to action part */
|
1998-12-04 06:49:20 +00:00
|
|
|
while (*p == '\t' || *p == ' ')
|
1994-05-26 05:23:31 +00:00
|
|
|
p++;
|
|
|
|
|
|
|
|
switch (*p)
|
|
|
|
{
|
|
|
|
case '@':
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_un.f_forw.f_hname, ++p,
|
|
|
|
sizeof(f->f_un.f_forw.f_hname));
|
2000-12-16 18:25:48 +00:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = family;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
error = getaddrinfo(f->f_un.f_forw.f_hname, "syslog", &hints,
|
|
|
|
&res);
|
|
|
|
if (error) {
|
|
|
|
logerror(gai_strerror(error));
|
1994-05-26 05:23:31 +00:00
|
|
|
break;
|
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
f->f_un.f_forw.f_addr = res;
|
1994-05-26 05:23:31 +00:00
|
|
|
f->f_type = F_FORW;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '/':
|
|
|
|
if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
|
1996-12-10 17:52:23 +00:00
|
|
|
f->f_type = F_UNUSED;
|
1994-05-26 05:23:31 +00:00
|
|
|
logerror(p);
|
|
|
|
break;
|
|
|
|
}
|
1996-11-26 02:24:42 +00:00
|
|
|
if (isatty(f->f_file)) {
|
|
|
|
if (strcmp(p, ctty) == 0)
|
|
|
|
f->f_type = F_CONSOLE;
|
|
|
|
else
|
|
|
|
f->f_type = F_TTY;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_un.f_fname, p + sizeof(_PATH_DEV - 1),
|
|
|
|
sizeof(f->f_un.f_fname));
|
1996-11-26 02:24:42 +00:00
|
|
|
} else {
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
|
1994-05-26 05:23:31 +00:00
|
|
|
f->f_type = F_FILE;
|
1996-11-26 02:24:42 +00:00
|
|
|
}
|
1994-05-26 05:23:31 +00:00
|
|
|
break;
|
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
case '|':
|
|
|
|
f->f_un.f_pipe.f_pid = 0;
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(f->f_un.f_fname, p + 1, sizeof(f->f_un.f_fname));
|
1997-02-22 12:59:36 +00:00
|
|
|
f->f_type = F_PIPE;
|
|
|
|
break;
|
|
|
|
|
1994-05-26 05:23:31 +00:00
|
|
|
case '*':
|
|
|
|
f->f_type = F_WALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
for (i = 0; i < MAXUNAMES && *p; i++) {
|
|
|
|
for (q = p; *q && *q != ','; )
|
|
|
|
q++;
|
|
|
|
(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
|
|
|
|
if ((q - p) > UT_NAMESIZE)
|
|
|
|
f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
|
|
|
|
else
|
|
|
|
f->f_un.f_uname[i][q - p] = '\0';
|
|
|
|
while (*q == ',' || *q == ' ')
|
|
|
|
q++;
|
|
|
|
p = q;
|
|
|
|
}
|
|
|
|
f->f_type = F_USERS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a symbolic name to a numeric value
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
decode(name, codetab)
|
|
|
|
const char *name;
|
|
|
|
CODE *codetab;
|
|
|
|
{
|
|
|
|
CODE *c;
|
|
|
|
char *p, buf[40];
|
|
|
|
|
|
|
|
if (isdigit(*name))
|
|
|
|
return (atoi(name));
|
|
|
|
|
|
|
|
for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
|
|
|
|
if (isupper(*name))
|
|
|
|
*p = tolower(*name);
|
|
|
|
else
|
|
|
|
*p = *name;
|
|
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
for (c = codetab; c->c_name; c++)
|
|
|
|
if (!strcmp(buf, c->c_name))
|
|
|
|
return (c->c_val);
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
1996-10-05 15:20:51 +00:00
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
void
|
|
|
|
markit(void)
|
|
|
|
{
|
|
|
|
struct filed *f;
|
|
|
|
dq_t q;
|
|
|
|
|
|
|
|
now = time((time_t *)NULL);
|
|
|
|
MarkSeq += TIMERINTVL;
|
|
|
|
if (MarkSeq >= MarkInterval) {
|
|
|
|
logmsg(LOG_INFO, "-- MARK --",
|
|
|
|
LocalHostName, ADDDATE|MARK);
|
|
|
|
MarkSeq = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (f = Files; f; f = f->f_next) {
|
|
|
|
if (f->f_prevcount && now >= REPEATTIME(f)) {
|
|
|
|
dprintf("flush %s: repeated %d times, %d sec.\n",
|
|
|
|
TypeNames[f->f_type], f->f_prevcount,
|
|
|
|
repeatinterval[f->f_repeatcount]);
|
|
|
|
fprintlog(f, 0, (char *)NULL);
|
|
|
|
BACKOFF(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Walk the dead queue, and see if we should signal somebody. */
|
|
|
|
for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
|
|
|
|
switch (q->dq_timeout) {
|
|
|
|
case 0:
|
|
|
|
/* Already signalled once, try harder now. */
|
|
|
|
if (kill(q->dq_pid, SIGKILL) != 0)
|
|
|
|
(void)deadq_remove(q->dq_pid);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
/*
|
|
|
|
* Timed out on dead queue, send terminate
|
|
|
|
* signal. Note that we leave the removal
|
|
|
|
* from the dead queue to reapchild(), which
|
|
|
|
* will also log the event (unless the process
|
|
|
|
* didn't even really exist, in case we simply
|
|
|
|
* drop it from the dead queue).
|
|
|
|
*/
|
|
|
|
if (kill(q->dq_pid, SIGTERM) != 0)
|
|
|
|
(void)deadq_remove(q->dq_pid);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
default:
|
|
|
|
q->dq_timeout--;
|
|
|
|
}
|
|
|
|
MarkSet = 0;
|
|
|
|
(void)alarm(TIMERINTVL);
|
|
|
|
}
|
|
|
|
|
1996-11-18 21:48:29 +00:00
|
|
|
/*
|
|
|
|
* fork off and become a daemon, but wait for the child to come online
|
|
|
|
* before returing to the parent, or we get disk thrashing at boot etc.
|
|
|
|
* Set a timer so we don't hang forever if it wedges.
|
|
|
|
*/
|
1996-10-05 15:20:51 +00:00
|
|
|
int
|
|
|
|
waitdaemon(nochdir, noclose, maxwait)
|
|
|
|
int nochdir, noclose, maxwait;
|
|
|
|
{
|
|
|
|
int fd;
|
1996-11-18 21:48:29 +00:00
|
|
|
int status;
|
|
|
|
pid_t pid, childpid;
|
1996-10-05 15:20:51 +00:00
|
|
|
|
1996-11-18 21:48:29 +00:00
|
|
|
switch (childpid = fork()) {
|
1996-10-05 15:20:51 +00:00
|
|
|
case -1:
|
|
|
|
return (-1);
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
1996-11-18 21:48:29 +00:00
|
|
|
signal(SIGALRM, timedout);
|
1996-10-05 15:20:51 +00:00
|
|
|
alarm(maxwait);
|
1996-11-18 21:48:29 +00:00
|
|
|
while ((pid = wait3(&status, 0, NULL)) != -1) {
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
errx(1, "child pid %d exited with return code %d",
|
|
|
|
pid, WEXITSTATUS(status));
|
|
|
|
if (WIFSIGNALED(status))
|
|
|
|
errx(1, "child pid %d exited on signal %d%s",
|
|
|
|
pid, WTERMSIG(status),
|
|
|
|
WCOREDUMP(status) ? " (core dumped)" :
|
|
|
|
"");
|
|
|
|
if (pid == childpid) /* it's gone... */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exit(0);
|
1996-10-05 15:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (setsid() == -1)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if (!nochdir)
|
|
|
|
(void)chdir("/");
|
|
|
|
|
|
|
|
if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
|
|
|
|
(void)dup2(fd, STDIN_FILENO);
|
|
|
|
(void)dup2(fd, STDOUT_FILENO);
|
|
|
|
(void)dup2(fd, STDERR_FILENO);
|
|
|
|
if (fd > 2)
|
|
|
|
(void)close (fd);
|
|
|
|
}
|
|
|
|
return (getppid());
|
|
|
|
}
|
1996-11-18 21:48:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We get a SIGALRM from the child when it's running and finished doing it's
|
|
|
|
* fsync()'s or O_SYNC writes for all the boot messages.
|
|
|
|
*
|
|
|
|
* We also get a signal from the kernel if the timer expires, so check to
|
|
|
|
* see what happened.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
timedout(sig)
|
|
|
|
int sig __unused;
|
|
|
|
{
|
|
|
|
int left;
|
|
|
|
left = alarm(0);
|
|
|
|
signal(SIGALRM, SIG_DFL);
|
|
|
|
if (left == 0)
|
|
|
|
errx(1, "timed out waiting for child");
|
|
|
|
else
|
2001-09-01 07:57:35 +00:00
|
|
|
_exit(0);
|
1996-11-18 21:48:29 +00:00
|
|
|
}
|
1997-02-22 12:59:36 +00:00
|
|
|
|
1997-05-03 22:17:43 +00:00
|
|
|
/*
|
|
|
|
* Add `s' to the list of allowable peer addresses to accept messages
|
|
|
|
* from.
|
|
|
|
*
|
|
|
|
* `s' is a string in the form:
|
|
|
|
*
|
|
|
|
* [*]domainname[:{servicename|portnumber|*}]
|
|
|
|
*
|
|
|
|
* or
|
|
|
|
*
|
|
|
|
* netaddr/maskbits[:{servicename|portnumber|*}]
|
|
|
|
*
|
|
|
|
* Returns -1 on error, 0 if the argument was valid.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
allowaddr(s)
|
|
|
|
char *s;
|
|
|
|
{
|
|
|
|
char *cp1, *cp2;
|
|
|
|
struct allowedpeer ap;
|
|
|
|
struct servent *se;
|
2001-07-19 22:04:09 +00:00
|
|
|
int masklen = -1, i;
|
2000-12-16 18:25:48 +00:00
|
|
|
struct addrinfo hints, *res;
|
|
|
|
struct in_addr *addrp, *maskp;
|
2001-07-19 22:04:09 +00:00
|
|
|
u_int32_t *addr6p, *mask6p;
|
2000-12-16 18:25:48 +00:00
|
|
|
char ip[NI_MAXHOST];
|
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL)
|
|
|
|
#endif
|
|
|
|
cp1 = s;
|
|
|
|
if ((cp1 = strrchr(cp1, ':'))) {
|
1997-05-03 22:17:43 +00:00
|
|
|
/* service/port provided */
|
|
|
|
*cp1++ = '\0';
|
|
|
|
if (strlen(cp1) == 1 && *cp1 == '*')
|
|
|
|
/* any port allowed */
|
2000-12-16 18:25:48 +00:00
|
|
|
ap.port = 0;
|
1997-05-03 22:17:43 +00:00
|
|
|
else if ((se = getservbyname(cp1, "udp")))
|
2000-12-16 18:25:48 +00:00
|
|
|
ap.port = ntohs(se->s_port);
|
1997-05-03 22:17:43 +00:00
|
|
|
else {
|
2000-12-16 18:25:48 +00:00
|
|
|
ap.port = strtol(cp1, &cp2, 0);
|
1997-05-03 22:17:43 +00:00
|
|
|
if (*cp2 != '\0')
|
|
|
|
return -1; /* port not numeric */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((se = getservbyname("syslog", "udp")))
|
2000-12-16 18:25:48 +00:00
|
|
|
ap.port = ntohs(se->s_port);
|
1997-05-03 22:17:43 +00:00
|
|
|
else
|
|
|
|
/* sanity, should not happen */
|
2000-12-16 18:25:48 +00:00
|
|
|
ap.port = 514;
|
1997-05-03 22:17:43 +00:00
|
|
|
}
|
|
|
|
|
2000-12-16 18:25:48 +00:00
|
|
|
if ((cp1 = strchr(s, '/')) != NULL &&
|
|
|
|
strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) {
|
|
|
|
*cp1 = '\0';
|
|
|
|
if ((masklen = atoi(cp1 + 1)) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef INET6
|
|
|
|
if (*s == '[') {
|
|
|
|
cp2 = s + strlen(s) - 1;
|
|
|
|
if (*cp2 == ']') {
|
|
|
|
++s;
|
|
|
|
*cp2 = '\0';
|
|
|
|
} else
|
|
|
|
cp2 = NULL;
|
|
|
|
} else
|
|
|
|
cp2 = NULL;
|
|
|
|
#endif
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = PF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
|
|
|
if (getaddrinfo(s, NULL, &hints, &res) == 0) {
|
1997-05-03 22:17:43 +00:00
|
|
|
ap.isnumeric = 1;
|
2000-12-16 18:25:48 +00:00
|
|
|
memcpy(&ap.a_addr, res->ai_addr, res->ai_addrlen);
|
|
|
|
memset(&ap.a_mask, 0, sizeof(ap.a_mask));
|
|
|
|
ap.a_mask.ss_family = res->ai_family;
|
|
|
|
if (res->ai_family == AF_INET) {
|
|
|
|
ap.a_mask.ss_len = sizeof(struct sockaddr_in);
|
|
|
|
maskp = &((struct sockaddr_in *)&ap.a_mask)->sin_addr;
|
2001-07-02 15:26:47 +00:00
|
|
|
addrp = &((struct sockaddr_in *)&ap.a_addr)->sin_addr;
|
2000-12-16 18:25:48 +00:00
|
|
|
if (masklen < 0) {
|
|
|
|
/* use default netmask */
|
|
|
|
if (IN_CLASSA(ntohl(addrp->s_addr)))
|
|
|
|
maskp->s_addr = htonl(IN_CLASSA_NET);
|
|
|
|
else if (IN_CLASSB(ntohl(addrp->s_addr)))
|
|
|
|
maskp->s_addr = htonl(IN_CLASSB_NET);
|
|
|
|
else
|
|
|
|
maskp->s_addr = htonl(IN_CLASSC_NET);
|
|
|
|
} else if (masklen <= 32) {
|
|
|
|
/* convert masklen to netmask */
|
|
|
|
maskp->s_addr = htonl(~((1 << (32 - masklen)) - 1));
|
|
|
|
} else {
|
|
|
|
freeaddrinfo(res);
|
1997-05-03 22:17:43 +00:00
|
|
|
return -1;
|
2000-12-16 18:25:48 +00:00
|
|
|
}
|
2001-07-02 15:26:47 +00:00
|
|
|
/* Lose any host bits in the network number. */
|
|
|
|
addrp->s_addr &= maskp->s_addr;
|
2000-12-16 18:25:48 +00:00
|
|
|
}
|
|
|
|
#ifdef INET6
|
|
|
|
else if (res->ai_family == AF_INET6 && masklen <= 128) {
|
|
|
|
ap.a_mask.ss_len = sizeof(struct sockaddr_in6);
|
|
|
|
if (masklen < 0)
|
|
|
|
masklen = 128;
|
|
|
|
mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
|
1997-05-03 22:17:43 +00:00
|
|
|
/* convert masklen to netmask */
|
2000-12-16 18:25:48 +00:00
|
|
|
while (masklen > 0) {
|
|
|
|
if (masklen < 32) {
|
|
|
|
*mask6p = htonl(~(0xffffffff >> masklen));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*mask6p++ = 0xffffffff;
|
|
|
|
masklen -= 32;
|
|
|
|
}
|
2001-07-19 22:04:09 +00:00
|
|
|
/* Lose any host bits in the network number. */
|
|
|
|
mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
|
|
|
|
addr6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_addr)->sin6_addr;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
addr6p[i] &= mask6p[i];
|
1997-05-03 22:17:43 +00:00
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
#endif
|
|
|
|
else {
|
|
|
|
freeaddrinfo(res);
|
1997-05-03 22:17:43 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
freeaddrinfo(res);
|
1997-05-03 22:17:43 +00:00
|
|
|
} else {
|
|
|
|
/* arg `s' is domain name */
|
|
|
|
ap.isnumeric = 0;
|
|
|
|
ap.a_name = s;
|
2000-12-16 18:25:48 +00:00
|
|
|
if (cp1)
|
|
|
|
*cp1 = '/';
|
|
|
|
#ifdef INET6
|
|
|
|
if (cp2) {
|
|
|
|
*cp2 = ']';
|
|
|
|
--s;
|
|
|
|
}
|
|
|
|
#endif
|
1997-05-03 22:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Debug) {
|
|
|
|
printf("allowaddr: rule %d: ", NumAllowed);
|
|
|
|
if (ap.isnumeric) {
|
|
|
|
printf("numeric, ");
|
2000-12-16 18:25:48 +00:00
|
|
|
getnameinfo((struct sockaddr *)&ap.a_addr,
|
|
|
|
((struct sockaddr *)&ap.a_addr)->sa_len,
|
|
|
|
ip, sizeof ip, NULL, 0,
|
|
|
|
NI_NUMERICHOST | withscopeid);
|
|
|
|
printf("addr = %s, ", ip);
|
|
|
|
getnameinfo((struct sockaddr *)&ap.a_mask,
|
|
|
|
((struct sockaddr *)&ap.a_mask)->sa_len,
|
|
|
|
ip, sizeof ip, NULL, 0,
|
|
|
|
NI_NUMERICHOST | withscopeid);
|
|
|
|
printf("mask = %s; ", ip);
|
1997-05-03 22:17:43 +00:00
|
|
|
} else
|
|
|
|
printf("domainname = %s; ", ap.a_name);
|
2000-12-16 18:25:48 +00:00
|
|
|
printf("port = %d\n", ap.port);
|
1997-05-03 22:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((AllowedPeers = realloc(AllowedPeers,
|
|
|
|
++NumAllowed * sizeof(struct allowedpeer)))
|
|
|
|
== NULL) {
|
|
|
|
fprintf(stderr, "Out of memory!\n");
|
|
|
|
exit(EX_OSERR);
|
|
|
|
}
|
|
|
|
memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate that the remote peer has permission to log to us.
|
|
|
|
*/
|
|
|
|
int
|
2000-12-16 18:25:48 +00:00
|
|
|
validate(sa, hname)
|
|
|
|
struct sockaddr *sa;
|
1997-05-03 22:17:43 +00:00
|
|
|
const char *hname;
|
|
|
|
{
|
2000-12-16 18:25:48 +00:00
|
|
|
int i, j, reject;
|
1997-05-03 22:17:43 +00:00
|
|
|
size_t l1, l2;
|
2000-12-16 18:25:48 +00:00
|
|
|
char *cp, name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV];
|
1997-05-03 22:17:43 +00:00
|
|
|
struct allowedpeer *ap;
|
2000-12-16 18:25:48 +00:00
|
|
|
struct sockaddr_in *sin, *a4p = NULL, *m4p = NULL;
|
|
|
|
struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL;
|
|
|
|
struct addrinfo hints, *res;
|
|
|
|
u_short sport;
|
1997-05-03 22:17:43 +00:00
|
|
|
|
|
|
|
if (NumAllowed == 0)
|
|
|
|
/* traditional behaviour, allow everything */
|
|
|
|
return 1;
|
|
|
|
|
2001-09-01 07:57:35 +00:00
|
|
|
(void)strlcpy(name, hname, sizeof(name));
|
2000-12-16 18:25:48 +00:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = PF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
|
|
|
if (getaddrinfo(name, NULL, &hints, &res) == 0)
|
|
|
|
freeaddrinfo(res);
|
|
|
|
else if (strchr(name, '.') == NULL) {
|
2000-06-08 03:54:45 +00:00
|
|
|
strlcat(name, ".", sizeof name);
|
|
|
|
strlcat(name, LocalDomain, sizeof name);
|
1997-05-03 22:17:43 +00:00
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
if (getnameinfo(sa, sa->sa_len, ip, sizeof ip, port, sizeof port,
|
|
|
|
NI_NUMERICHOST | withscopeid | NI_NUMERICSERV) != 0)
|
|
|
|
return 0; /* for safety, should not occur */
|
|
|
|
dprintf("validate: dgram from IP %s, port %s, name %s;\n",
|
|
|
|
ip, port, name);
|
|
|
|
sport = atoi(port);
|
1997-05-03 22:17:43 +00:00
|
|
|
|
|
|
|
/* now, walk down the list */
|
|
|
|
for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
|
2000-12-16 18:25:48 +00:00
|
|
|
if (ap->port != 0 && ap->port != sport) {
|
1997-05-03 22:17:43 +00:00
|
|
|
dprintf("rejected in rule %d due to port mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ap->isnumeric) {
|
2000-12-16 18:25:48 +00:00
|
|
|
if (ap->a_addr.ss_family != sa->sa_family) {
|
|
|
|
dprintf("rejected in rule %d due to address family mismatch.\n", i);
|
1997-05-03 22:17:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
if (ap->a_addr.ss_family == AF_INET) {
|
|
|
|
sin = (struct sockaddr_in *)sa;
|
|
|
|
a4p = (struct sockaddr_in *)&ap->a_addr;
|
|
|
|
m4p = (struct sockaddr_in *)&ap->a_mask;
|
|
|
|
if ((sin->sin_addr.s_addr & m4p->sin_addr.s_addr)
|
|
|
|
!= a4p->sin_addr.s_addr) {
|
|
|
|
dprintf("rejected in rule %d due to IP mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef INET6
|
|
|
|
else if (ap->a_addr.ss_family == AF_INET6) {
|
|
|
|
sin6 = (struct sockaddr_in6 *)sa;
|
|
|
|
a6p = (struct sockaddr_in6 *)&ap->a_addr;
|
|
|
|
m6p = (struct sockaddr_in6 *)&ap->a_mask;
|
|
|
|
#ifdef NI_WITHSCOPEID
|
|
|
|
if (a6p->sin6_scope_id != 0 &&
|
|
|
|
sin6->sin6_scope_id != a6p->sin6_scope_id) {
|
|
|
|
dprintf("rejected in rule %d due to scope mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
reject = 0;
|
|
|
|
for (j = 0; j < 16; j += 4) {
|
2001-07-19 22:04:09 +00:00
|
|
|
if ((*(u_int32_t *)&sin6->sin6_addr.s6_addr[j] & *(u_int32_t *)&m6p->sin6_addr.s6_addr[j])
|
|
|
|
!= *(u_int32_t *)&a6p->sin6_addr.s6_addr[j]) {
|
2000-12-16 18:25:48 +00:00
|
|
|
++reject;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (reject) {
|
|
|
|
dprintf("rejected in rule %d due to IP mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
continue;
|
1997-05-03 22:17:43 +00:00
|
|
|
} else {
|
|
|
|
cp = ap->a_name;
|
|
|
|
l1 = strlen(name);
|
|
|
|
if (*cp == '*') {
|
|
|
|
/* allow wildmatch */
|
|
|
|
cp++;
|
|
|
|
l2 = strlen(cp);
|
|
|
|
if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) {
|
|
|
|
dprintf("rejected in rule %d due to name mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* exact match */
|
|
|
|
l2 = strlen(cp);
|
|
|
|
if (l2 != l1 || memcmp(cp, name, l1) != 0) {
|
|
|
|
dprintf("rejected in rule %d due to name mismatch.\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dprintf("accepted in rule %d.\n", i);
|
|
|
|
return 1; /* hooray! */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1997-02-22 12:59:36 +00:00
|
|
|
/*
|
|
|
|
* Fairly similar to popen(3), but returns an open descriptor, as
|
|
|
|
* opposed to a FILE *.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
p_open(prog, pid)
|
|
|
|
char *prog;
|
|
|
|
pid_t *pid;
|
|
|
|
{
|
|
|
|
int pfd[2], nulldesc, i;
|
|
|
|
sigset_t omask, mask;
|
|
|
|
char *argv[4]; /* sh -c cmd NULL */
|
|
|
|
char errmsg[200];
|
|
|
|
|
|
|
|
if (pipe(pfd) == -1)
|
|
|
|
return -1;
|
|
|
|
if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1)
|
|
|
|
/* we are royally screwed anyway */
|
|
|
|
return -1;
|
|
|
|
|
1998-02-28 15:14:00 +00:00
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGALRM);
|
|
|
|
sigaddset(&mask, SIGHUP);
|
|
|
|
sigprocmask(SIG_BLOCK, &mask, &omask);
|
1997-02-22 12:59:36 +00:00
|
|
|
switch ((*pid = fork())) {
|
|
|
|
case -1:
|
1998-02-28 15:14:00 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &omask, 0);
|
1997-02-22 12:59:36 +00:00
|
|
|
close(nulldesc);
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
argv[0] = "sh";
|
|
|
|
argv[1] = "-c";
|
|
|
|
argv[2] = prog;
|
|
|
|
argv[3] = NULL;
|
|
|
|
|
|
|
|
alarm(0);
|
|
|
|
(void)setsid(); /* Avoid catching SIGHUPs. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Throw away pending signals, and reset signal
|
|
|
|
* behaviour to standard values.
|
|
|
|
*/
|
|
|
|
signal(SIGALRM, SIG_IGN);
|
|
|
|
signal(SIGHUP, SIG_IGN);
|
1998-02-28 15:14:00 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &omask, 0);
|
1997-02-22 12:59:36 +00:00
|
|
|
signal(SIGPIPE, SIG_DFL);
|
|
|
|
signal(SIGQUIT, SIG_DFL);
|
|
|
|
signal(SIGALRM, SIG_DFL);
|
|
|
|
signal(SIGHUP, SIG_DFL);
|
|
|
|
|
|
|
|
dup2(pfd[0], STDIN_FILENO);
|
|
|
|
dup2(nulldesc, STDOUT_FILENO);
|
|
|
|
dup2(nulldesc, STDERR_FILENO);
|
|
|
|
for (i = getdtablesize(); i > 2; i--)
|
|
|
|
(void) close(i);
|
|
|
|
|
|
|
|
(void) execvp(_PATH_BSHELL, argv);
|
|
|
|
_exit(255);
|
|
|
|
}
|
|
|
|
|
1998-02-28 15:14:00 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &omask, 0);
|
1997-02-22 12:59:36 +00:00
|
|
|
close(nulldesc);
|
|
|
|
close(pfd[0]);
|
|
|
|
/*
|
|
|
|
* Avoid blocking on a hung pipe. With O_NONBLOCK, we are
|
|
|
|
* supposed to get an EWOULDBLOCK on writev(2), which is
|
|
|
|
* caught by the logic above anyway, which will in turn close
|
|
|
|
* the pipe, and fork a new logging subprocess if necessary.
|
|
|
|
* The stale subprocess will be killed some time later unless
|
|
|
|
* it terminated itself due to closing its input pipe (so we
|
|
|
|
* get rid of really dead puppies).
|
|
|
|
*/
|
|
|
|
if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
|
|
|
|
/* This is bad. */
|
|
|
|
(void)snprintf(errmsg, sizeof errmsg,
|
|
|
|
"Warning: cannot change pipe to PID %d to "
|
|
|
|
"non-blocking behaviour.",
|
|
|
|
(int)*pid);
|
|
|
|
logerror(errmsg);
|
|
|
|
}
|
|
|
|
return pfd[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
deadq_enter(pid, name)
|
1997-02-22 12:59:36 +00:00
|
|
|
pid_t pid;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
const char *name;
|
1997-02-22 12:59:36 +00:00
|
|
|
{
|
|
|
|
dq_t p;
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
int status;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Be paranoid, if we can't signal the process, don't enter it
|
|
|
|
* into the dead queue (perhaps it's already dead). If possible,
|
|
|
|
* we try to fetch and log the child's status.
|
|
|
|
*/
|
|
|
|
if (kill(pid, 0) != 0) {
|
|
|
|
if (waitpid(pid, &status, WNOHANG) > 0)
|
|
|
|
log_deadchild(pid, status, name);
|
|
|
|
return;
|
|
|
|
}
|
1997-02-22 12:59:36 +00:00
|
|
|
|
|
|
|
p = malloc(sizeof(struct deadq_entry));
|
|
|
|
if (p == 0) {
|
|
|
|
errno = 0;
|
|
|
|
logerror("panic: out of virtual memory!");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
p->dq_pid = pid;
|
|
|
|
p->dq_timeout = DQ_TIMO_INIT;
|
|
|
|
TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
|
|
|
|
}
|
Fix a serious bug in syslogd regarding the handling of pipes. The bug
would cause syslogd to eventually kill innocent processes in the
system over time (note: not `could' but `would'). Many thanks to my
colleague Mirko for digging into the kernel structures and providing
me with the debugging framework to find out about the nature of this
bug (and to isolate that syslogd was the culprit) in a rather large
set of distributed machines at client sites where this happened
occasionally.
Whenever a child process was no longer responsive, or when syslogd
receives a SIGHUP so it closes all its logging file descriptors, for
any descriptor that refers to a pipe syslogd enters the data about the
old logging child process into a `dead queue', where it is being
removed from (and the status of the dead kitten being fetched) upon
receipt of a SIGCHLD. However, there's a high probability that the
SIGCHLD already arrives before the child's data are actually entered
into the dead queue inside the SIGHUP handler, so the SIGCHLD handler
has nothing to fetch and remove and simply continues. Whenever this
happens, the process'es data remain on the dead queue forever, and
since domark() tried to get rid of totally unresponsive children by
first sending a SIGTERM and later a SIGKILL, it was only a matter of
time until the system had recycled enough PIDs so an innocent process
got shot to death.
Fix the race by masking SIGHUP and SIGCHLD from both handlers mutually.
Add additional bandaids ``just in case'', i. e. don't enter a process
into the dead queue if we can't signal it (this should only happen in
case it is already dead by that time so we can fetch the status
immediately instead of deferring this to the SIGCHLD handler); for the
kill(2) inside domark(), check for an error status (/* Can't happen */
:) and remove it from the dead queue in this case (which if it would
have been there in the first place would have reduced the problem to a
statistically minimal likelihood so i certainly would never have
noticed the bug at all :).
Mirko also reviewed the fix in priciple (mutual blocking of both
signals inside the handlers), but not the actual code.
Reviewed by: Mirko Kaffka <mirko@interface-business.de>
Approved by: jkh
2000-02-28 17:49:43 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
deadq_remove(pid)
|
|
|
|
pid_t pid;
|
|
|
|
{
|
|
|
|
dq_t q;
|
|
|
|
|
|
|
|
for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
|
|
|
|
if (q->dq_pid == pid) {
|
|
|
|
TAILQ_REMOVE(&deadq_head, q, dq_entries);
|
|
|
|
free(q);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
log_deadchild(pid, status, name)
|
|
|
|
pid_t pid;
|
|
|
|
int status;
|
|
|
|
const char *name;
|
|
|
|
{
|
|
|
|
int code;
|
|
|
|
char buf[256];
|
|
|
|
const char *reason;
|
|
|
|
|
|
|
|
errno = 0; /* Keep strerror() stuff out of logerror messages. */
|
|
|
|
if (WIFSIGNALED(status)) {
|
|
|
|
reason = "due to signal";
|
|
|
|
code = WTERMSIG(status);
|
|
|
|
} else {
|
|
|
|
reason = "with status";
|
|
|
|
code = WEXITSTATUS(status);
|
|
|
|
if (code == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
(void)snprintf(buf, sizeof buf,
|
|
|
|
"Logging subprocess %d (%s) exited %s %d.",
|
|
|
|
pid, name, reason, code);
|
|
|
|
logerror(buf);
|
|
|
|
}
|
2000-12-16 18:25:48 +00:00
|
|
|
|
|
|
|
int *
|
|
|
|
socksetup(af)
|
|
|
|
int af;
|
|
|
|
{
|
|
|
|
struct addrinfo hints, *res, *r;
|
|
|
|
int error, maxs, *s, *socks;
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
hints.ai_family = af;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
error = getaddrinfo(NULL, "syslog", &hints, &res);
|
|
|
|
if (error) {
|
|
|
|
logerror(gai_strerror(error));
|
|
|
|
errno = 0;
|
|
|
|
die(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Count max number of sockets we may open */
|
|
|
|
for (maxs = 0, r = res; r; r = r->ai_next, maxs++);
|
|
|
|
socks = malloc((maxs+1) * sizeof(int));
|
|
|
|
if (!socks) {
|
|
|
|
logerror("couldn't allocate memory for sockets");
|
|
|
|
die(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
*socks = 0; /* num of sockets counter at start of array */
|
|
|
|
s = socks + 1;
|
|
|
|
for (r = res; r; r = r->ai_next) {
|
|
|
|
*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
|
|
|
|
if (*s < 0) {
|
|
|
|
logerror("socket");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#ifdef IPV6_BINDV6ONLY
|
|
|
|
if (r->ai_family == AF_INET6) {
|
|
|
|
int on = 1;
|
|
|
|
if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
|
|
|
|
(char *)&on, sizeof (on)) < 0) {
|
|
|
|
logerror("setsockopt");
|
|
|
|
close(*s);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
|
|
|
|
close(*s);
|
|
|
|
logerror("bind");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*socks)++;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*socks == 0) {
|
|
|
|
free(socks);
|
|
|
|
if (Debug)
|
|
|
|
return(NULL);
|
|
|
|
else
|
|
|
|
die(0);
|
|
|
|
}
|
|
|
|
if (res)
|
|
|
|
freeaddrinfo(res);
|
|
|
|
|
|
|
|
return(socks);
|
|
|
|
}
|