5bb4699e9c
has always held an open file descriptor. This allowed logging to spare virtual consoles and being able to switch to them. My previous change removed this since all writes were done with ttymsg() which opens it's own fd, and hence syslogd didn't need it's own fd to send messages on... but this caused an unexpected behavior change. This should close PR#2176
1301 lines
29 KiB
C
1301 lines
29 KiB
C
/*
|
|
* 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
|
|
static const char copyright[] =
|
|
"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
/*
|
|
static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
|
|
*/
|
|
static const char rcsid[] =
|
|
"$Id: syslogd.c,v 1.15 1996/11/26 02:35:08 peter Exp $";
|
|
#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)
|
|
* Extension to log by program name as well as facility and priority
|
|
* by Peter da Silva.
|
|
*/
|
|
|
|
#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 */
|
|
#define TTYMSGTIME 1 /* timed out passed to ttymsg */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/msgbuf.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/un.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/syslimits.h>
|
|
#include <paths.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <utmp.h>
|
|
#include "pathnames.h"
|
|
|
|
#define SYSLOG_NAMES
|
|
#include <sys/syslog.h>
|
|
|
|
const char *LogName = _PATH_LOG;
|
|
const char *ConfFile = _PATH_LOGCONF;
|
|
const char *PidFile = _PATH_LOGPID;
|
|
const char ctty[] = _PATH_CONSOLE;
|
|
|
|
#define FDMASK(fd) (1 << (fd))
|
|
|
|
#define dprintf if (Debug) printf
|
|
|
|
#define MAXUNAMES 20 /* maximum number of user names */
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
/*
|
|
* 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 */
|
|
u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
|
|
char *f_program; /* program this applies to */
|
|
union {
|
|
char f_uname[MAXUNAMES][UT_NAMESIZE+1];
|
|
struct {
|
|
char f_hname[MAXHOSTNAMELEN+1];
|
|
struct sockaddr_in f_addr;
|
|
} f_forw; /* forwarding address */
|
|
char f_fname[MAXPATHLEN];
|
|
} f_un;
|
|
char f_prevline[MAXSVLINE]; /* last message logged */
|
|
char f_lasttime[16]; /* time of last occurrence */
|
|
char f_prevhost[MAXHOSTNAMELEN+1]; /* host from which recd. */
|
|
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 */
|
|
};
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
char *TypeNames[7] = {
|
|
"UNUSED", "FILE", "TTY", "CONSOLE",
|
|
"FORW", "USERS", "WALL"
|
|
};
|
|
|
|
struct filed *Files;
|
|
struct filed consfile;
|
|
|
|
int Debug; /* debug flag */
|
|
char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
|
|
char *LocalDomain; /* our local domain name */
|
|
int InetInuse = 0; /* non-zero if INET sockets are being used */
|
|
int finet; /* Internet datagram socket */
|
|
int LogPort; /* port number for INET connections */
|
|
int Initialized = 0; /* set when we have initialized ourselves */
|
|
int MarkInterval = 20 * 60; /* interval between marks in seconds */
|
|
int MarkSeq = 0; /* mark sequence number */
|
|
int SecureMode = 0; /* when true, speak only unix domain socks */
|
|
|
|
int created_lsock = 0; /* Flag if local socket created */
|
|
char bootfile[MAXLINE+1]; /* booted kernel file */
|
|
|
|
void cfline __P((char *, struct filed *, char *));
|
|
char *cvthname __P((struct sockaddr_in *));
|
|
int decode __P((const char *, CODE *));
|
|
void die __P((int));
|
|
void domark __P((int));
|
|
void fprintlog __P((struct filed *, int, char *));
|
|
void init __P((int));
|
|
void logerror __P((const char *));
|
|
void logmsg __P((int, char *, char *, int));
|
|
void printline __P((char *, char *));
|
|
void printsys __P((char *));
|
|
void reapchild __P((int));
|
|
char *ttymsg __P((struct iovec *, int, char *, int));
|
|
void usage __P((void));
|
|
void wallmsg __P((struct filed *, struct iovec *));
|
|
int waitdaemon __P((int, int, int));
|
|
void timedout __P((int));
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int ch, funix, i, inetm, fklog, klogm, len;
|
|
struct sockaddr_un sunx, fromunix;
|
|
struct sockaddr_in sin, frominet;
|
|
FILE *fp;
|
|
char *p, line[MSG_BSIZE + 1];
|
|
struct timeval tv, *tvp;
|
|
pid_t ppid;
|
|
|
|
while ((ch = getopt(argc, argv, "dsf:Im:p:")) != EOF)
|
|
switch(ch) {
|
|
case 'd': /* debug */
|
|
Debug++;
|
|
break;
|
|
case 'f': /* configuration file */
|
|
ConfFile = optarg;
|
|
break;
|
|
case 'm': /* mark interval */
|
|
MarkInterval = atoi(optarg) * 60;
|
|
break;
|
|
case 'p': /* path */
|
|
LogName = optarg;
|
|
break;
|
|
case 'I': /* backwards compatible w/FreeBSD */
|
|
case 's': /* no network mode */
|
|
SecureMode++;
|
|
break;
|
|
case '?':
|
|
default:
|
|
usage();
|
|
}
|
|
if ((argc -= optind) != 0)
|
|
usage();
|
|
|
|
if (!Debug) {
|
|
ppid = waitdaemon(0, 0, 30);
|
|
if (ppid < 0)
|
|
err(1, "could not become daemon");
|
|
} else
|
|
setlinebuf(stdout);
|
|
|
|
consfile.f_type = F_CONSOLE;
|
|
(void)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1);
|
|
(void)gethostname(LocalHostName, sizeof(LocalHostName));
|
|
if ((p = strchr(LocalHostName, '.')) != NULL) {
|
|
*p++ = '\0';
|
|
LocalDomain = p;
|
|
} else
|
|
LocalDomain = "";
|
|
(void)strcpy(bootfile, getbootfile());
|
|
(void)signal(SIGTERM, die);
|
|
(void)signal(SIGINT, Debug ? die : SIG_IGN);
|
|
(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
|
|
(void)signal(SIGCHLD, reapchild);
|
|
(void)signal(SIGALRM, domark);
|
|
(void)alarm(TIMERINTVL);
|
|
|
|
#ifndef SUN_LEN
|
|
#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
|
|
#endif
|
|
memset(&sunx, 0, sizeof(sunx));
|
|
sunx.sun_family = AF_UNIX;
|
|
(void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
|
|
funix = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
if (funix < 0 ||
|
|
bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
|
|
chmod(LogName, 0666) < 0) {
|
|
(void) sprintf(line, "cannot create %s", LogName);
|
|
logerror(line);
|
|
dprintf("cannot create %s (%d)\n", LogName, errno);
|
|
die(0);
|
|
} else
|
|
created_lsock = 1;
|
|
|
|
if (!SecureMode)
|
|
finet = socket(AF_INET, SOCK_DGRAM, 0);
|
|
else
|
|
finet = -1;
|
|
|
|
inetm = 0;
|
|
if (finet >= 0) {
|
|
struct servent *sp;
|
|
|
|
sp = getservbyname("syslog", "udp");
|
|
if (sp == NULL) {
|
|
errno = 0;
|
|
logerror("syslog/udp: unknown service");
|
|
die(0);
|
|
}
|
|
memset(&sin, 0, sizeof(sin));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = LogPort = sp->s_port;
|
|
if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
|
logerror("bind");
|
|
if (!Debug)
|
|
die(0);
|
|
} else {
|
|
inetm = FDMASK(finet);
|
|
InetInuse = 1;
|
|
}
|
|
}
|
|
if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
|
|
klogm = FDMASK(fklog);
|
|
else {
|
|
dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
|
|
klogm = 0;
|
|
}
|
|
|
|
/* 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);
|
|
(void)signal(SIGHUP, init);
|
|
|
|
tvp = &tv;
|
|
tv.tv_sec = tv.tv_usec = 0;
|
|
|
|
for (;;) {
|
|
int nfds, readfds = FDMASK(funix) | inetm | klogm;
|
|
|
|
dprintf("readfds = %#x\n", readfds);
|
|
nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
|
|
(fd_set *)NULL, tvp);
|
|
if (nfds == 0) {
|
|
if (tvp) {
|
|
tvp = NULL;
|
|
if (ppid != 1)
|
|
kill(ppid, SIGALRM);
|
|
}
|
|
continue;
|
|
}
|
|
if (nfds < 0) {
|
|
if (errno != EINTR)
|
|
logerror("select");
|
|
continue;
|
|
}
|
|
dprintf("got a message (%d, %#x)\n", nfds, readfds);
|
|
if (readfds & klogm) {
|
|
i = read(fklog, line, sizeof(line) - 1);
|
|
if (i > 0) {
|
|
line[i] = '\0';
|
|
printsys(line);
|
|
} else if (i < 0 && errno != EINTR) {
|
|
logerror("klog");
|
|
fklog = -1;
|
|
klogm = 0;
|
|
}
|
|
}
|
|
if (readfds & FDMASK(funix)) {
|
|
len = sizeof(fromunix);
|
|
i = recvfrom(funix, line, MAXLINE, 0,
|
|
(struct sockaddr *)&fromunix, &len);
|
|
if (i > 0) {
|
|
line[i] = '\0';
|
|
printline(LocalHostName, line);
|
|
} else if (i < 0 && errno != EINTR)
|
|
logerror("recvfrom unix");
|
|
}
|
|
if (readfds & inetm) {
|
|
len = sizeof(frominet);
|
|
i = recvfrom(finet, line, MAXLINE, 0,
|
|
(struct sockaddr *)&frominet, &len);
|
|
if (i > 0) {
|
|
line[i] = '\0';
|
|
printline(cvthname(&frominet), line);
|
|
} else if (i < 0 && errno != EINTR)
|
|
logerror("recvfrom inet");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
usage()
|
|
{
|
|
|
|
fprintf(stderr,
|
|
"usage: syslogd [-ds] [-f conffile] [-m markinterval]"
|
|
" [-p logpath]\n");
|
|
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 */
|
|
if (LOG_FAC(pri) == LOG_KERN)
|
|
pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
|
|
|
|
q = line;
|
|
|
|
while ((c = *p++ & 0177) != '\0' &&
|
|
q < &line[sizeof(line) - 1])
|
|
if (iscntrl(c))
|
|
if (c == '\n')
|
|
*q++ = ' ';
|
|
else if (c == '\t')
|
|
*q++ = '\t';
|
|
else {
|
|
*q++ = '^';
|
|
*q++ = c ^ 0100;
|
|
}
|
|
else
|
|
*q++ = c;
|
|
*q = '\0';
|
|
|
|
logmsg(pri, line, hname, 0);
|
|
}
|
|
|
|
/*
|
|
* Take a raw input line from /dev/klog, split and format similar to syslog().
|
|
*/
|
|
void
|
|
printsys(msg)
|
|
char *msg;
|
|
{
|
|
int c, pri, flags;
|
|
char *lp, *p, *q, line[MAXLINE + 1];
|
|
|
|
(void)strcpy(line, bootfile);
|
|
(void)strcat(line, ": ");
|
|
lp = line + strlen(line);
|
|
for (p = msg; *p != '\0'; ) {
|
|
flags = SYNC_FILE | ADDDATE; /* fsync file after write */
|
|
pri = DEFSPRI;
|
|
if (*p == '<') {
|
|
pri = 0;
|
|
while (isdigit(*++p))
|
|
pri = 10 * pri + (*p - '0');
|
|
if (*p == '>')
|
|
++p;
|
|
} else {
|
|
/* kernel printf's come out on console */
|
|
flags |= IGN_CONS;
|
|
}
|
|
if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
|
|
pri = DEFSPRI;
|
|
q = lp;
|
|
while (*p != '\0' && (c = *p++) != '\n' &&
|
|
q < &line[MAXLINE])
|
|
*q++ = c;
|
|
*q = '\0';
|
|
logmsg(pri, line, LocalHostName, flags);
|
|
}
|
|
}
|
|
|
|
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;
|
|
int fac, msglen, omask, prilev;
|
|
char *timestamp;
|
|
char prog[NAME_MAX+1];
|
|
int i;
|
|
|
|
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;
|
|
}
|
|
|
|
/* skip leading blanks */
|
|
while(isspace(*msg)) {
|
|
msg++;
|
|
msglen--;
|
|
}
|
|
|
|
/* extract facility and priority level */
|
|
if (flags & MARK)
|
|
fac = LOG_NFACILITIES;
|
|
else
|
|
fac = LOG_FAC(pri);
|
|
prilev = LOG_PRI(pri);
|
|
|
|
/* extract program name */
|
|
for(i = 0; i < NAME_MAX; i++) {
|
|
if(!isalnum(msg[i]))
|
|
break;
|
|
prog[i] = msg[i];
|
|
}
|
|
prog[i] = 0;
|
|
|
|
/* 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 */
|
|
if (f->f_pmask[fac] < prilev ||
|
|
f->f_pmask[fac] == INTERNAL_NOPRI)
|
|
continue;
|
|
/* skip messages with the incorrect program name */
|
|
if(f->f_program)
|
|
if(strcmp(prog, f->f_program) != 0)
|
|
continue;
|
|
|
|
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) &&
|
|
!strcmp(from, f->f_prevhost)) {
|
|
(void)strncpy(f->f_lasttime, timestamp, 15);
|
|
f->f_prevcount++;
|
|
dprintf("msg repeated %d times, %ld sec of %d\n",
|
|
f->f_prevcount, now - f->f_time,
|
|
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;
|
|
f->f_prevpri = pri;
|
|
(void)strncpy(f->f_lasttime, timestamp, 15);
|
|
(void)strncpy(f->f_prevhost, from,
|
|
sizeof(f->f_prevhost));
|
|
if (msglen < MAXSVLINE) {
|
|
f->f_prevlen = msglen;
|
|
(void)strcpy(f->f_prevline, msg);
|
|
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;
|
|
{
|
|
struct iovec iov[6];
|
|
struct iovec *v;
|
|
int l;
|
|
char line[MAXLINE + 1], repbuf[80], greetings[200];
|
|
char *msgret;
|
|
|
|
v = iov;
|
|
if (f->f_type == F_WALL) {
|
|
v->iov_base = greetings;
|
|
v->iov_len = sprintf(greetings,
|
|
"\r\n\7Message from syslogd@%s at %.24s ...\r\n",
|
|
f->f_prevhost, ctime(&now));
|
|
v++;
|
|
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++;
|
|
}
|
|
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;
|
|
v->iov_len = sprintf(repbuf, "last message repeated %d times",
|
|
f->f_prevcount);
|
|
} 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);
|
|
l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
|
|
iov[0].iov_base, iov[4].iov_base);
|
|
if (l > MAXLINE)
|
|
l = MAXLINE;
|
|
if ((finet >= 0) &&
|
|
(sendto(finet, line, l, 0,
|
|
(struct sockaddr *)&f->f_un.f_forw.f_addr,
|
|
sizeof(f->f_un.f_forw.f_addr)) != l)) {
|
|
int e = errno;
|
|
(void)close(f->f_file);
|
|
f->f_type = F_UNUSED;
|
|
errno = e;
|
|
logerror("sendto");
|
|
}
|
|
break;
|
|
|
|
case F_FILE:
|
|
dprintf(" %s\n", f->f_un.f_fname);
|
|
v->iov_base = "\n";
|
|
v->iov_len = 1;
|
|
if (writev(f->f_file, iov, 6) < 0) {
|
|
int e = errno;
|
|
(void)close(f->f_file);
|
|
f->f_type = F_UNUSED;
|
|
errno = e;
|
|
logerror(f->f_un.f_fname);
|
|
} else if (flags & SYNC_FILE)
|
|
(void)fsync(f->f_file);
|
|
break;
|
|
|
|
case F_CONSOLE:
|
|
if (flags & IGN_CONS) {
|
|
dprintf(" (ignored)\n");
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
|
|
case F_TTY:
|
|
dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
|
|
v->iov_base = "\r\n";
|
|
v->iov_len = 2;
|
|
|
|
errno = 0; /* ttymsg() only sometimes returns an errno */
|
|
if ((msgret = ttymsg(iov, 6, f->f_un.f_fname, 10))) {
|
|
f->f_type = F_UNUSED;
|
|
logerror(msgret);
|
|
}
|
|
break;
|
|
|
|
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;
|
|
strncpy(line, ut.ut_line, sizeof(ut.ut_line));
|
|
line[sizeof(ut.ut_line)] = '\0';
|
|
if (f->f_type == F_WALL) {
|
|
if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
|
|
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)) {
|
|
if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
|
|
!= NULL) {
|
|
errno = 0; /* already in msg */
|
|
logerror(p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
(void)fclose(uf);
|
|
reenter = 0;
|
|
}
|
|
|
|
void
|
|
reapchild(signo)
|
|
int signo;
|
|
{
|
|
int status;
|
|
|
|
while (wait3(&status, WNOHANG, (struct rusage *)NULL) > 0)
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Return a printable representation of a host address.
|
|
*/
|
|
char *
|
|
cvthname(f)
|
|
struct sockaddr_in *f;
|
|
{
|
|
struct hostent *hp;
|
|
char *p;
|
|
|
|
dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
|
|
|
|
if (f->sin_family != AF_INET) {
|
|
dprintf("Malformed from address\n");
|
|
return ("???");
|
|
}
|
|
hp = gethostbyaddr((char *)&f->sin_addr,
|
|
sizeof(struct in_addr), f->sin_family);
|
|
if (hp == 0) {
|
|
dprintf("Host name for your address (%s) unknown\n",
|
|
inet_ntoa(f->sin_addr));
|
|
return (inet_ntoa(f->sin_addr));
|
|
}
|
|
if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
|
|
*p = '\0';
|
|
return (hp->h_name);
|
|
}
|
|
|
|
void
|
|
domark(signo)
|
|
int signo;
|
|
{
|
|
struct filed *f;
|
|
|
|
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);
|
|
}
|
|
}
|
|
(void)alarm(TIMERINTVL);
|
|
}
|
|
|
|
/*
|
|
* Print syslogd errors some place.
|
|
*/
|
|
void
|
|
logerror(type)
|
|
const char *type;
|
|
{
|
|
char buf[100];
|
|
|
|
if (errno)
|
|
(void)snprintf(buf,
|
|
sizeof(buf), "syslogd: %s: %s", type, strerror(errno));
|
|
else
|
|
(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
|
|
errno = 0;
|
|
dprintf("%s\n", buf);
|
|
logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
|
|
}
|
|
|
|
void
|
|
die(signo)
|
|
int signo;
|
|
{
|
|
struct filed *f;
|
|
char buf[100];
|
|
|
|
for (f = Files; f != NULL; f = f->f_next) {
|
|
/* flush any pending output */
|
|
if (f->f_prevcount)
|
|
fprintlog(f, 0, (char *)NULL);
|
|
}
|
|
if (signo) {
|
|
dprintf("syslogd: exiting on signal %d\n", signo);
|
|
(void)sprintf(buf, "exiting on signal %d", signo);
|
|
errno = 0;
|
|
logerror(buf);
|
|
}
|
|
if (created_lsock)
|
|
(void)unlink(LogName);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* 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];
|
|
char prog[NAME_MAX+1];
|
|
|
|
dprintf("init\n");
|
|
|
|
/*
|
|
* 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:
|
|
case F_CONSOLE:
|
|
case F_TTY:
|
|
(void)close(f->f_file);
|
|
break;
|
|
}
|
|
next = f->f_next;
|
|
if(f->f_program) free(f->f_program);
|
|
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));
|
|
cfline("*.ERR\t/dev/console", *nextp, "*");
|
|
(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
|
|
cfline("*.PANIC\t*", (*nextp)->f_next, "*");
|
|
Initialized = 1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Foreach line in the conf table, open that file.
|
|
*/
|
|
f = NULL;
|
|
strcpy(prog, "*");
|
|
while (fgets(cline, sizeof(cline), cf) != NULL) {
|
|
/*
|
|
* check for end-of-section, comments, strip off trailing
|
|
* spaces and newline character. #!prog is treated specially:
|
|
* following lines apply only to that program.
|
|
*/
|
|
for (p = cline; isspace(*p); ++p)
|
|
continue;
|
|
if (*p == 0)
|
|
continue;
|
|
if(*p == '#') {
|
|
p++;
|
|
if(*p!='!')
|
|
continue;
|
|
}
|
|
if(*p=='!') {
|
|
p++;
|
|
while(isspace(*p)) p++;
|
|
if(!*p) {
|
|
strcpy(prog, "*");
|
|
continue;
|
|
}
|
|
for(i = 0; i < NAME_MAX; i++) {
|
|
if(!isalnum(p[i]))
|
|
break;
|
|
prog[i] = p[i];
|
|
}
|
|
prog[i] = 0;
|
|
continue;
|
|
}
|
|
for (p = strchr(cline, '\0'); isspace(*--p);)
|
|
continue;
|
|
*++p = '\0';
|
|
f = (struct filed *)calloc(1, sizeof(*f));
|
|
*nextp = f;
|
|
nextp = &f->f_next;
|
|
cfline(cline, f, prog);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
case F_CONSOLE:
|
|
case F_TTY:
|
|
printf("%s%s", _PATH_DEV, f->f_un.f_fname);
|
|
break;
|
|
|
|
case F_FORW:
|
|
printf("%s", f->f_un.f_forw.f_hname);
|
|
break;
|
|
|
|
case F_USERS:
|
|
for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
|
|
printf("%s, ", f->f_un.f_uname[i]);
|
|
break;
|
|
}
|
|
if(f->f_program) {
|
|
printf(" (%s)", f->f_program);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
|
|
dprintf("syslogd: restarted\n");
|
|
}
|
|
|
|
/*
|
|
* Crack a configuration file line
|
|
*/
|
|
void
|
|
cfline(line, f, prog)
|
|
char *line;
|
|
struct filed *f;
|
|
char *prog;
|
|
{
|
|
struct hostent *hp;
|
|
int i, pri;
|
|
char *bp, *p, *q;
|
|
char buf[MAXLINE], ebuf[100];
|
|
|
|
dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
|
|
|
|
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;
|
|
|
|
/* save program name if any */
|
|
if(prog && *prog=='*') prog = NULL;
|
|
if(prog) {
|
|
f->f_program = calloc(1, strlen(prog)+1);
|
|
if(f->f_program) {
|
|
strcpy(f->f_program, prog);
|
|
}
|
|
}
|
|
|
|
/* scan through the list of selectors */
|
|
for (p = line; *p && *p != '\t';) {
|
|
|
|
/* find the end of this facility name list */
|
|
for (q = p; *q && *q != '\t' && *q++ != '.'; )
|
|
continue;
|
|
|
|
/* collect priority name */
|
|
for (bp = buf; *q && !strchr("\t,;", *q); )
|
|
*bp++ = *q++;
|
|
*bp = '\0';
|
|
|
|
/* skip cruft */
|
|
while (strchr(", ;", *q))
|
|
q++;
|
|
|
|
/* decode priority name */
|
|
if (*buf == '*')
|
|
pri = LOG_PRIMASK + 1;
|
|
else {
|
|
pri = decode(buf, prioritynames);
|
|
if (pri < 0) {
|
|
(void)sprintf(ebuf,
|
|
"unknown priority name \"%s\"", buf);
|
|
logerror(ebuf);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* scan facilities */
|
|
while (*p && !strchr("\t.;", *p)) {
|
|
for (bp = buf; *p && !strchr("\t,;.", *p); )
|
|
*bp++ = *p++;
|
|
*bp = '\0';
|
|
if (*buf == '*')
|
|
for (i = 0; i < LOG_NFACILITIES; i++)
|
|
f->f_pmask[i] = pri;
|
|
else {
|
|
i = decode(buf, facilitynames);
|
|
if (i < 0) {
|
|
(void)sprintf(ebuf,
|
|
"unknown facility name \"%s\"",
|
|
buf);
|
|
logerror(ebuf);
|
|
return;
|
|
}
|
|
f->f_pmask[i >> 3] = pri;
|
|
}
|
|
while (*p == ',' || *p == ' ')
|
|
p++;
|
|
}
|
|
|
|
p = q;
|
|
}
|
|
|
|
/* skip to action part */
|
|
while (*p == '\t')
|
|
p++;
|
|
|
|
switch (*p)
|
|
{
|
|
case '@':
|
|
if (!InetInuse)
|
|
break;
|
|
(void)strcpy(f->f_un.f_forw.f_hname, ++p);
|
|
hp = gethostbyname(p);
|
|
if (hp == NULL) {
|
|
extern int h_errno;
|
|
|
|
logerror(hstrerror(h_errno));
|
|
break;
|
|
}
|
|
memset(&f->f_un.f_forw.f_addr, 0,
|
|
sizeof(f->f_un.f_forw.f_addr));
|
|
f->f_un.f_forw.f_addr.sin_family = AF_INET;
|
|
f->f_un.f_forw.f_addr.sin_port = LogPort;
|
|
memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
|
|
f->f_type = F_FORW;
|
|
break;
|
|
|
|
case '/':
|
|
if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
|
|
f->f_type = F_UNUSED;
|
|
logerror(p);
|
|
break;
|
|
}
|
|
if (isatty(f->f_file)) {
|
|
if (strcmp(p, ctty) == 0)
|
|
f->f_type = F_CONSOLE;
|
|
else
|
|
f->f_type = F_TTY;
|
|
(void)strcpy(f->f_un.f_fname, p + sizeof _PATH_DEV - 1);
|
|
} else {
|
|
(void)strcpy(f->f_un.f_fname, p);
|
|
f->f_type = F_FILE;
|
|
}
|
|
break;
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
int
|
|
waitdaemon(nochdir, noclose, maxwait)
|
|
int nochdir, noclose, maxwait;
|
|
{
|
|
int fd;
|
|
int status;
|
|
pid_t pid, childpid;
|
|
|
|
switch (childpid = fork()) {
|
|
case -1:
|
|
return (-1);
|
|
case 0:
|
|
break;
|
|
default:
|
|
signal(SIGALRM, timedout);
|
|
alarm(maxwait);
|
|
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);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
exit(0);
|
|
}
|