* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)syslogd.c 5.16 (Berkeley) %G%";
* 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.
* MAXLINE -- the maximimum line length that can be handled.
* NLOGS -- the maximum number of simultaneous log files.
* DEFUPRI -- the default priority for user messages
* DEFSPRI -- the default priority for kernel messages
* extensive changes by Ralph Campbell
* more extensive changes by Eric Allman (again)
#define NLOGS 20 /* max number of log files */
#define MAXLINE 1024 /* maximum line length */
#define DEFUPRI (LOG_USER|LOG_NOTICE)
#define DEFSPRI (LOG_KERN|LOG_CRIT)
#define MARKCOUNT 10 /* ratio of minor to major marks */
#include <sys/resource.h>
char *LogName
= "/dev/log";
char *ConfFile
= "/etc/syslog.conf";
char *PidFile
= "/etc/syslog.pid";
char ctty
[] = "/dev/console";
#define FDMASK(fd) (1 << (fd))
#define dprintf if (Debug) printf
#define UNAMESZ 8 /* length of a login name */
#define MAXUNAMES 20 /* maximum number of user names */
#define MAXFNAME 200 /* max file pathname length */
#define NOPRI 0x10 /* the "no priority" priority */
#define LOG_MARK (LOG_NFACILITIES << 3) /* mark "facility" */
#define IGN_CONS 0x001 /* don't print on console */
#define SYNC_FILE 0x002 /* do fsync on file after printing */
#define NOCOPY 0x004 /* don't suppress duplicate messages */
#define ADDDATE 0x008 /* add a date to the message */
#define MARK 0x010 /* this message is a mark */
* This structure represents the files that will have log
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_uname
[MAXUNAMES
][UNAMESZ
+1];
char f_hname
[MAXHOSTNAMELEN
+1];
struct sockaddr_in f_addr
;
} f_forw
; /* forwarding address */
#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 */
"UNUSED", "FILE", "TTY", "CONSOLE",
struct filed Files
[NLOGS
];
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 LogPort
; /* port number for INET connections */
char PrevLine
[MAXLINE
+ 1]; /* copy of last line to supress repeats */
char PrevHost
[MAXHOSTNAMELEN
+1]; /* previous host */
int PrevCount
= 0; /* number of times seen */
int Initialized
= 0; /* set when we have initialized ourselves */
int MarkInterval
= 20; /* interval between marks in minutes */
int MarkSeq
= 0; /* mark sequence number */
extern int errno
, sys_nerr
;
extern char *sys_errlist
[];
extern char *ctime(), *index();
int funix
, finet
, inetm
, fklog
, klogm
, len
;
struct sockaddr_un sun
, fromunix
;
struct sockaddr_in sin
, frominet
;
char line
[MSG_BSIZE
+ 1];
extern int die(), domark(), reapchild();
case 'f': /* configuration file */
case 'm': /* mark interval */
MarkInterval
= atoi(&p
[2]);
(void) gethostname(LocalHostName
, sizeof LocalHostName
);
if (p
= index(LocalHostName
, '.')) {
(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(MarkInterval
* 60 / MARKCOUNT
);
sun
.sun_family
= AF_UNIX
;
(void) strncpy(sun
.sun_path
, LogName
, sizeof sun
.sun_path
);
funix
= socket(AF_UNIX
, SOCK_DGRAM
, 0);
if (funix
< 0 || bind(funix
, (struct sockaddr
*) &sun
,
sizeof(sun
.sun_family
)+strlen(sun
.sun_path
)) < 0 ||
chmod(LogName
, 0666) < 0) {
(void) sprintf(line
, "cannot create %s", LogName
);
dprintf("cannot create %s (%d)\n", LogName
, errno
);
finet
= socket(AF_INET
, SOCK_DGRAM
, 0);
sp
= getservbyname("syslog", "udp");
logerror("syslog/udp: unknown service");
sin
.sin_family
= AF_INET
;
sin
.sin_port
= LogPort
= sp
->s_port
;
if (bind(finet
, &sin
, sizeof(sin
)) < 0) {
if ((fklog
= open("/dev/klog", O_RDONLY
)) >= 0)
dprintf("can't open /dev/klog (%d)\n", errno
);
/* tuck my process id away */
fp
= fopen(PidFile
, "w");
fprintf(fp
, "%d\n", getpid());
dprintf("off & running....\n");
(void) signal(SIGHUP
, init
);
int nfds
, readfds
= FDMASK(funix
) | inetm
| klogm
;
dprintf("readfds = %#x\n", readfds
, funix
, finet
, fklog
);
nfds
= select(20, (fd_set
*) &readfds
, (fd_set
*) NULL
,
(fd_set
*) NULL
, (struct timeval
*) NULL
);
dprintf("got a message (%d, %#x)\n", nfds
, readfds
);
i
= read(fklog
, line
, sizeof(line
) - 1);
} else if (i
< 0 && errno
!= EINTR
) {
if (readfds
& FDMASK(funix
)) {
i
= recvfrom(funix
, line
, MAXLINE
, 0,
(struct sockaddr
*) &fromunix
, &len
);
printline(LocalHostName
, line
);
} else if (i
< 0 && errno
!= EINTR
)
logerror("recvfrom unix");
i
= recvfrom(finet
, line
, MAXLINE
, 0, &frominet
, &len
);
printline(cvthname(&frominet
), line
);
} else if (i
< 0 && errno
!= EINTR
)
logerror("recvfrom inet");
fprintf(stderr
, "usage: syslogd [-d] [-mmarkinterval] [-ppath] [-fconffile]\n");
i
= open("/dev/tty", O_RDWR
);
(void) ioctl(i
, (int) TIOCNOTTY
, (char *)0);
* Take a raw input line, decode the message, and print the message
* on the appropriate log files.
/* test for special codes */
pri
= 10 * pri
+ (*p
- '0');
if (pri
<= 0 || pri
>= (LOG_NFACILITIES
<< 3))
/* don't allow users to log kernel messages */
if ((pri
& LOG_PRIMASK
) == LOG_KERN
)
while ((c
= *p
++ & 0177) != '\0' && c
!= '\n' &&
q
< &line
[sizeof(line
) - 1]) {
logmsg(pri
, line
, hname
, 0);
* Take a raw input line from /dev/klog, split and format similar to syslog().
(void) sprintf(line
, "%.15s vmunix: ", ctime(&now
) + 4);
lp
= line
+ strlen(line
);
for (p
= msg
; *p
!= '\0'; ) {
flags
= SYNC_FILE
; /* fsync file after write */
pri
= 10 * pri
+ (*p
- '0');
if (pri
<= 0 || pri
>= (LOG_NFACILITIES
<< 3))
/* kernel printf's come out on console */
while (*p
!= '\0' && (c
= *p
++) != '\n' &&
logmsg(pri
, line
, LocalHostName
, flags
);
* Log a message to the appropriate log files, users, etc. based on
logmsg(pri
, msg
, from
, flags
)
register struct filed
*f
;
register struct iovec
*v
= iov
;
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.
if (strlen(msg
) < 16 || msg
[3] != ' ' || msg
[6] != ' ' ||
msg
[9] != ':' || msg
[12] != ':' || msg
[15] != ' ')
if (flags
& (ADDDATE
|MARK
))
else if (!strcmp(msg
+ 16, PrevLine
+ 16)) {
/* we found a match, update the time */
(void) strncpy(PrevLine
, msg
, 15);
(void) sigsetmask(omask
);
(void) strcpy(PrevLine
, msg
);
(void) strcpy(PrevHost
, from
);
v
->iov_base
= ctime(&now
) + 4;
v
->iov_len
= strlen(v
->iov_base
);
v
->iov_len
= strlen(v
->iov_base
);
/* extract facility and priority level */
fac
= (pri
& LOG_FACMASK
) >> 3;
prilev
= pri
& LOG_PRIMASK
;
/* log the message to the particular outputs */
int cfd
= open(ctty
, O_WRONLY
);
(void) writev(cfd
, iov
, 6);
(void) sigsetmask(omask
);
for (f
= Files
; f
< &Files
[NLOGS
]; f
++) {
/* skip messages that are incorrect priority */
if (f
->f_pmask
[fac
] < prilev
|| f
->f_pmask
[fac
] == NOPRI
)
/* don't output marks to recently written files */
if ((flags
& MARK
) && (now
- f
->f_time
) < (MarkInterval
* 60 / 2))
dprintf("Logging to %s", TypeNames
[f
->f_type
]);
dprintf(" %s\n", f
->f_un
.f_forw
.f_hname
);
(void) sprintf(line
, "<%d>%.15s %s", pri
,
iov
[0].iov_base
, iov
[4].iov_base
);
if (sendto(f
->f_file
, line
, l
, 0,
sizeof f
->f_un
.f_forw
.f_addr
) != l
) {
dprintf(" %s\n", f
->f_un
.f_fname
);
if (f
->f_type
!= F_FILE
) {
if (writev(f
->f_file
, iov
, 6) < 0) {
* Check for EBADF on TTY's due to vhangup() XXX
if (e
== EBADF
&& f
->f_type
!= F_FILE
) {
f
->f_file
= open(f
->f_un
.f_fname
, O_WRONLY
|O_APPEND
);
logerror(f
->f_un
.f_fname
);
logerror(f
->f_un
.f_fname
);
} else if (flags
& SYNC_FILE
)
(void) sigsetmask(omask
);
* WALLMSG -- Write a message to the world at large
* Write the specified message to either the entire
* world, or a list of approved users.
register struct filed
*f
;
/* open the user login file */
if ((uf
= fopen("/etc/utmp", "r")) == NULL
) {
(void) sprintf(greetings
,
"\r\n\7Message from syslogd@%s at %.24s ...\r\n",
iov
[2].iov_base
, ctime(&now
));
/* scan the user login file */
while (fread((char *) &ut
, sizeof ut
, 1, uf
) == 1) {
if (ut
.ut_name
[0] == '\0')
/* should we send the message to this user? */
if (f
->f_type
== F_USERS
) {
for (i
= 0; i
< MAXUNAMES
; i
++) {
if (!f
->f_un
.f_uname
[i
][0]) {
if (strncmp(f
->f_un
.f_uname
[i
], ut
.ut_name
,
/* compute the device name */
strcpyn(&p
[5], ut
.ut_line
, UNAMESZ
);
* Might as well fork instead of using nonblocking I/O
if (f
->f_type
== F_WALL
) {
iov
[0].iov_base
= greetings
;
(void) signal(SIGALRM
, SIG_DFL
);
ttyf
= open(p
, O_WRONLY
);
if (fstat(ttyf
, &statb
) == 0 &&
(statb
.st_mode
& S_IWRITE
))
(void) writev(ttyf
, iov
, 6);
/* close the user login file */
while (wait3(&status
, WNOHANG
, (struct rusage
*) NULL
) > 0)
* Return a printable representation of a host address.
extern char *inet_ntoa();
dprintf("cvthname(%s)\n", inet_ntoa(f
->sin_addr
));
if (f
->sin_family
!= AF_INET
) {
dprintf("Malformed from address\n");
hp
= gethostbyaddr(&f
->sin_addr
, sizeof(struct in_addr
), f
->sin_family
);
dprintf("Host name for your address (%s) unknown\n",
return (inet_ntoa(f
->sin_addr
));
if ((p
= index(hp
->h_name
, '.')) && strcmp(p
+ 1, LocalDomain
) == 0)
if ((++MarkSeq
% MARKCOUNT
) == 0)
logmsg(LOG_INFO
, "-- MARK --", LocalHostName
, ADDDATE
|MARK
);
alarm(MarkInterval
* 60 / MARKCOUNT
);
(void) sprintf(PrevLine
+16, "last message repeated %d times", PrevCount
);
logmsg(PrevPri
, PrevLine
, PrevHost
, PrevFlags
|NOCOPY
);
* Print syslogd errors some place.
(void) sprintf(buf
, "syslogd: %s", type
);
else if ((unsigned) errno
> sys_nerr
)
(void) sprintf(buf
, "syslogd: %s: error %d", type
, errno
);
(void) sprintf(buf
, "syslogd: %s: %s", type
, sys_errlist
[errno
]);
logmsg(LOG_SYSLOG
|LOG_ERR
, buf
, LocalHostName
, ADDDATE
);
dprintf("syslogd: going down on signal %d\n", sig
);
(void) sprintf(buf
, "going down on signal %d", sig
);
* INIT -- Initialize syslogd from configuration table
register struct filed
*f
;
/* flush any pending output */
* Close all open log files.
for (f
= Files
; f
< &Files
[NLOGS
]; f
++) {
/* open the configuration file */
if ((cf
= fopen(ConfFile
, "r")) == NULL
) {
dprintf("cannot open %s\n", ConfFile
);
cfline("*.ERR\t/dev/console", &Files
[0]);
cfline("*.PANIC\t*", &Files
[1]);
* Foreach line in the conf table, open that file.
while (fgets(cline
, sizeof cline
, cf
) != NULL
&& f
< &Files
[NLOGS
]) {
/* check for end-of-section */
if (cline
[0] == '\n' || cline
[0] == '#')
/* strip off newline character */
/* close the configuration file */
for (f
= Files
; f
< &Files
[NLOGS
]; f
++) {
for (i
= 0; i
<= LOG_NFACILITIES
; i
++)
if (f
->f_pmask
[i
] == NOPRI
)
printf("%d ", f
->f_pmask
[i
]);
printf("%s: ", TypeNames
[f
->f_type
]);
printf("%s", f
->f_un
.f_fname
);
printf("%s", f
->f_un
.f_forw
.f_hname
);
for (i
= 0; i
< MAXUNAMES
&& *f
->f_un
.f_uname
[i
]; i
++)
printf("%s, ", f
->f_un
.f_uname
[i
]);
logmsg(LOG_SYSLOG
|LOG_INFO
, "syslogd: restart", LocalHostName
, ADDDATE
);
dprintf("syslogd: restarted\n");
* Crack a configuration file line
struct code PriNames
[] = {
struct code FacNames
[] = {
register struct filed
*f
;
dprintf("cfline(%s)\n", line
);
/* clear out file entry */
bzero((char *) f
, sizeof *f
);
for (i
= 0; i
<= LOG_NFACILITIES
; i
++)
/* 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
++ != '.'; )
/* collect priority name */
for (bp
= buf
; *q
&& !index("\t,;", *q
); )
/* decode priority name */
pri
= decode(buf
, PriNames
);
(void) sprintf(xbuf
, "unknown priority name \"%s\"", buf
);
while (*p
&& !index("\t.;", *p
)) {
for (bp
= buf
; *p
&& !index("\t,;.", *p
); )
for (i
= 0; i
< LOG_NFACILITIES
; i
++)
i
= decode(buf
, FacNames
);
(void) sprintf(xbuf
, "unknown facility name \"%s\"", buf
);
f
->f_pmask
[i
>> 3] = pri
;
while (*p
== ',' || *p
== ' ')
/* skip to action part */
(void) strcpy(f
->f_un
.f_forw
.f_hname
, ++p
);
(void) sprintf(buf
, "unknown host %s", p
);
bzero((char *) &f
->f_un
.f_forw
.f_addr
,
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
;
bcopy(hp
->h_addr
, (char *) &f
->f_un
.f_forw
.f_addr
.sin_addr
, hp
->h_length
);
f
->f_file
= socket(AF_INET
, SOCK_DGRAM
, 0);
(void) strcpy(f
->f_un
.f_fname
, p
);
if ((f
->f_file
= open(p
, O_WRONLY
|O_APPEND
)) < 0) {
if (strcmp(p
, ctty
) == 0)
for (i
= 0; i
< MAXUNAMES
&& *p
; i
++) {
for (q
= p
; *q
&& *q
!= ','; )
(void) strncpy(f
->f_un
.f_uname
[i
], p
, UNAMESZ
);
f
->f_un
.f_uname
[i
][UNAMESZ
] = '\0';
f
->f_un
.f_uname
[i
][q
- p
] = '\0';
while (*q
== ',' || *q
== ' ')
* Decode a symbolic name to a numeric value
(void) strcpy(buf
, name
);
for (c
= codetab
; c
->c_name
; c
++)
if (!strcmp(buf
, c
->c_name
))