| /* |
| * Copyright (c) 1983, 1988 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 |
| char copyright[] = |
| "@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\ |
| All rights reserved.\n"; |
| #endif /* not lint */ |
| |
| #ifndef lint |
| static char sccsid[] = "@(#)syslogd.c 5.45 (Berkeley) 3/2/91"; |
| #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) |
| * |
| * Modified, Sun Mar 7 15:21:13 1993, faith@cs.unc.edu for Linux: |
| * |
| * SUN_LEN_MISSING: struct sockaddr does not have sun_len |
| * SYSLOG_STREAM: syslog is implemented using stream sockets |
| * SYSLOG_INET: support inet logging |
| * KLOG_STREAM: kernel logging uses a stream device |
| * STATUS_BROKEN: use "int status" instead of "union wait status" |
| * RE_INSTALL_SIGNAL: signal() does *not* remain installed |
| * SYS_MSGBUF_H_MISSING: sys/msgbuf.h is missing |
| * FSYNC_MISSING: fsync() is missing |
| * KERNEL_NAME: what the kernel is usually called |
| * |
| * Original Linux version by Rik Faith <faith@cs.unc.edu> |
| * with changes by Rick Sladkey <jrs@world.std.com> and |
| * Rick <pclink@qus102.qld.npb.telecom.com.au>. Anyone else |
| * named Rick who wants to chip in? :-) |
| */ |
| |
| #ifdef __linux__ |
| #define SUN_LEN_MISSING |
| #define SYSLOG_STREAM |
| #define SYSLOG_INET |
| #define KLOG_STREAM |
| #undef STATUS_BROKEN |
| #define RE_INSTALL_SIGNAL |
| #define SYS_MSGBUF_H_MISSING |
| #undef FSYNC_MISSING |
| #define KERNEL_NAME "linux" |
| #endif |
| |
| #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 */ |
| |
| #include <sys/param.h> |
| #include <sys/errno.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| #include <sys/socket.h> |
| #include <sys/file.h> |
| #ifdef SYS_MSGBUF_H_MISSING |
| #define MSG_BSIZE (MAXLINE*2) |
| #else |
| #include <sys/msgbuf.h> |
| #endif |
| #include <sys/uio.h> |
| #include <sys/un.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <sys/signal.h> |
| |
| #include <netinet/in.h> |
| #include <netdb.h> |
| |
| #include <utmp.h> |
| #include <setjmp.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <paths.h> |
| |
| #define SYSLOG_NAMES |
| #include <sys/syslog.h> |
| |
| char *LogName = _PATH_LOG; |
| char *ConfFile = _PATH_LOGCONF; |
| char *PidFile = _PATH_LOGPID; |
| 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 */ |
| 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 */ |
| #ifdef SYSLOG_INET |
| int finet; /* Internet datagram socket */ |
| #endif /* SYSLOG_INET */ |
| 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 */ |
| |
| extern int errno; |
| extern char *ctime(), *index(), *calloc(); |
| |
| main(argc, argv) |
| int argc; |
| char **argv; |
| { |
| register int i; |
| register char *p; |
| int funix, fklog, len; |
| struct sockaddr_un sunx, fromunix; |
| struct sockaddr_in sin, frominet; |
| FILE *fp; |
| int ch; |
| char line[MSG_BSIZE + 1]; |
| extern int optind; |
| extern char *optarg; |
| void die(), domark(), init(), reapchild(); |
| #ifdef SYSLOG_STREAM |
| fd_set unixm; |
| int fd; |
| #endif /* SYSLOG_STREAM */ |
| |
| while ((ch = getopt(argc, argv, "df:m:p:")) != EOF) |
| switch((char)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 '?': |
| default: |
| usage(); |
| } |
| if (argc -= optind) |
| usage(); |
| |
| if (!Debug) |
| daemon(0, 0); |
| else |
| setlinebuf(stdout); |
| |
| consfile.f_type = F_CONSOLE; |
| (void) strcpy(consfile.f_un.f_fname, ctty); |
| (void) gethostname(LocalHostName, sizeof LocalHostName); |
| if (p = index(LocalHostName, '.')) { |
| *p++ = '\0'; |
| LocalDomain = p; |
| } |
| else |
| LocalDomain = ""; |
| (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); |
| (void) unlink(LogName); |
| |
| bzero((char *)&sunx, sizeof(sunx)); |
| sunx.sun_family = AF_UNIX; |
| (void) strncpy(sunx.sun_path, LogName, sizeof sunx.sun_path); |
| #ifdef SYSLOG_STREAM |
| funix = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (funix < 0 || bind(funix, (struct sockaddr *) &sunx, |
| sizeof(sunx)) < 0 || listen(funix, 5) < 0 || |
| #else /* !SYSLOG_STREAM */ |
| funix = socket(AF_UNIX, SOCK_DGRAM, 0); |
| if (funix < 0 || bind(funix, (struct sockaddr *) &sunx, |
| sizeof(sunx.sun_family)+sizeof(sunx.sun_len)+ |
| strlen(sunx.sun_path)) < 0 || |
| #endif /* !SYSLOG_STREAM */ |
| chmod(LogName, 0666) < 0) { |
| (void) sprintf(line, "cannot create %s", LogName); |
| logerror(line); |
| dprintf("cannot create %s (%d)\n", LogName, errno); |
| die(0); |
| } |
| #ifdef SYSLOG_INET |
| finet = socket(AF_INET, SOCK_DGRAM, 0); |
| if (finet >= 0) { |
| struct servent *sp; |
| |
| sp = getservbyname("syslog", "udp"); |
| if (sp == NULL) { |
| errno = 0; |
| logerror("syslog/udp: unknown service"); |
| } |
| else { |
| bzero((char *) &sin, 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"); |
| else { |
| finet = -1; |
| InetInuse = 1; |
| } |
| } |
| } |
| #endif /* SYSLOG_INET */ |
| if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0) |
| 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(); |
| (void) signal(SIGHUP, init); |
| #ifdef SYSLOG_STREAM |
| FD_ZERO(&unixm); |
| #endif /* SYSLOG_STREAM */ |
| |
| for (;;) { |
| int nfds; |
| fd_set readfds; |
| |
| #ifdef SYSLOG_STREAM |
| readfds = unixm; |
| #else /* !SYSLOG_STREAM */ |
| FD_ZERO(&readfds); |
| #endif /* !SYSLOG_STREAM */ |
| if (funix >= 0) |
| FD_SET(funix, &readfds); |
| if (fklog >= 0) |
| FD_SET(fklog, &readfds); |
| #ifdef SYSLOG_INET |
| if (finet >= 0) |
| FD_SET(finet, &readfds); |
| #endif /* SYSLOG_INET */ |
| |
| errno = 0; |
| dprintf("readfds = %#x\n", *((long *)&readfds)); |
| nfds = select(FD_SETSIZE, &readfds, (fd_set *) NULL, |
| (fd_set *) NULL, (struct timeval *) NULL); |
| if (nfds == 0) |
| continue; |
| if (nfds < 0) { |
| if (errno != EINTR) |
| logerror("select"); |
| continue; |
| } |
| dprintf("got a message (%d, %#x)\n", nfds, *((long *)&readfds)); |
| |
| #ifdef KLOG_STREAM |
| if (FD_ISSET(fklog, &readfds)) { |
| nfds--; |
| for (;;) { |
| i = klogread(fklog, line, sizeof(line) - 1); |
| if (i > 0) { |
| line[i] = '\0'; |
| printsys(line); |
| } |
| else { |
| if (i < 0 && errno != EINTR) { |
| logerror("klog"); |
| fklog = -1; |
| } |
| break; |
| } |
| } |
| } |
| #else /* !KLOG_STREAM */ |
| if (FD_ISSET(fklog, &readfds)) { |
| nfds--; |
| 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; |
| } |
| } |
| #endif /* !KLOG_STREAM */ |
| |
| #ifdef SYSLOG_STREAM |
| /* Accept a new unix connection. */ |
| if (FD_ISSET(funix, &readfds)) { |
| nfds--; |
| len = sizeof fromunix; |
| if ((fd = accept(funix, (struct sockaddr *) &fromunix, |
| &len)) >= 0) { |
| FD_SET(fd, &unixm); |
| dprintf("new stream connect (%d)\n", fd); |
| FD_SET(fd, &readfds); |
| nfds++; |
| } |
| } |
| /* Recv from existing connections. */ |
| for (fd = 0; nfds > 0 && fd < FD_SETSIZE; fd++) { |
| if (FD_ISSET(fd, &unixm) && FD_ISSET(fd, &readfds)) { |
| nfds--; |
| dprintf("message from stream (%d)\n", fd); |
| i = read(fd, line, MAXLINE); |
| if (i > 0) { |
| line[i] = '\0'; |
| printline(LocalHostName, line); |
| } |
| else if (i == 0) { |
| dprintf("stream closed (%d)\n", fd); |
| close(fd); |
| FD_CLR(fd, &unixm); |
| } |
| else if (i < 0 && errno != EINTR) { |
| logerror("recv stream"); |
| close(fd); |
| FD_CLR(fd, &unixm); |
| } |
| } |
| } |
| #else /* !SYSLOG_STREAM */ |
| if (FD_ISSET(funix, &readfds)) { |
| nfds--; |
| 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"); |
| } |
| #endif /* !SYSLOG_STREAM */ |
| |
| #ifdef SYSLOG_INET |
| if (FD_ISSET(finet, &readfds)) { |
| nfds--; |
| len = sizeof frominet; |
| i = recvfrom(finet, line, MAXLINE, 0, |
| (struct sockaddr *) &frominet, &len); |
| if (i > 0) { |
| extern char *cvthname(); |
| |
| line[i] = '\0'; |
| printline(cvthname(&frominet), line); |
| } else if (i < 0 && errno != EINTR) |
| logerror("recvfrom inet"); |
| } |
| #endif /* SYSLOG_INET */ |
| if (nfds != 0) |
| logerror("loose cannon"); |
| } |
| } |
| |
| usage() |
| { |
| (void) fprintf(stderr, |
| "usage: syslogd [-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. |
| */ |
| |
| printline(hname, msg) |
| char *hname; |
| char *msg; |
| { |
| register char *p, *q; |
| register int c; |
| char line[MAXLINE + 1]; |
| int pri; |
| |
| /* 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 == '\r') |
| *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(). |
| */ |
| |
| printsys(msg) |
| char *msg; |
| { |
| register char *p, *q; |
| register int c; |
| char line[MAXLINE + 1]; |
| int pri, flags; |
| char *lp; |
| |
| #ifdef KERNEL_NAME |
| (void) sprintf(line, "%s: ", KERNEL_NAME); |
| #else /* !KERNEL_NAME */ |
| (void) strcpy(line, "vmunix: "); |
| #endif /* !KERNEL_NAME */ |
| |
| 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]) |
| if (iscntrl(c)) { |
| if (c == '\n') |
| *q++ = ' '; |
| else if (c == '\r') |
| *q++ = ' '; |
| else if (c == '\t') |
| *q++ = '\t'; |
| else { |
| *q++ = '^'; |
| *q++ = c ^ 0100; |
| } |
| } |
| else |
| *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. |
| */ |
| |
| logmsg(pri, msg, from, flags) |
| int pri; |
| char *msg, *from; |
| int flags; |
| { |
| register struct filed *f; |
| int fac, prilev; |
| int omask, msglen; |
| char *timestamp; |
| time_t time(); |
| |
| 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; |
| } |
| |
| /* extract facility and priority level */ |
| if (flags & MARK) |
| fac = LOG_NFACILITIES; |
| else |
| fac = LOG_FAC(pri); |
| prilev = LOG_PRI(pri); |
| |
| /* log the message to the particular outputs */ |
| if (!Initialized) { |
| f = &consfile; |
| #ifdef O_NOCTTY |
| f->f_file = open(ctty, O_WRONLY|O_NOCTTY, 0); |
| #else /* !O_NOCTTY */ |
| f->f_file = open(ctty, O_WRONLY, 0); |
| #endif /* !O_NOCTTY */ |
| |
| 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; |
| |
| 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; |
| (void) strncpy(f->f_lasttime, timestamp, 15); |
| (void) strncpy(f->f_prevhost, from, |
| sizeof(f->f_prevhost)); |
| if (msglen < MAXSVLINE) { |
| f->f_prevlen = msglen; |
| f->f_prevpri = pri; |
| (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); |
| } |
| |
| fprintlog(f, flags, msg) |
| register struct filed *f; |
| int flags; |
| char *msg; |
| { |
| struct iovec iov[6]; |
| register struct iovec *v; |
| register int l; |
| char line[MAXLINE + 1], repbuf[80], greetings[200]; |
| |
| 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 (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_CONSOLE: |
| if (flags & IGN_CONS) { |
| dprintf(" (ignored)\n"); |
| break; |
| } |
| /* FALLTHROUGH */ |
| |
| case F_TTY: |
| case F_FILE: |
| dprintf(" %s\n", f->f_un.f_fname); |
| if (f->f_type != F_FILE) { |
| v->iov_base = "\r\n"; |
| v->iov_len = 2; |
| } else { |
| v->iov_base = "\n"; |
| v->iov_len = 1; |
| } |
| again: |
| if (writev(f->f_file, iov, 6) < 0) { |
| int e = errno; |
| (void) close(f->f_file); |
| /* |
| * Check for errors on TTY's due to loss of tty |
| */ |
| if ((e == EIO || e == EBADF) && f->f_type != F_FILE) { |
| f->f_file = open(f->f_un.f_fname, |
| O_WRONLY|O_APPEND, 0); |
| if (f->f_file < 0) { |
| f->f_type = F_UNUSED; |
| logerror(f->f_un.f_fname); |
| } else |
| goto again; |
| } else { |
| f->f_type = F_UNUSED; |
| errno = e; |
| logerror(f->f_un.f_fname); |
| } |
| } |
| #ifdef FSYNC_MISSING |
| else if (flags & SYNC_FILE) |
| (void) sync(); |
| #else |
| else if (flags & SYNC_FILE) |
| (void) fsync(f->f_file); |
| #endif |
| 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. |
| */ |
| |
| wallmsg(f, iov) |
| register struct filed *f; |
| struct iovec *iov; |
| { |
| static int reenter; /* avoid calling ourselves */ |
| register FILE *uf; |
| register int i; |
| struct utmp ut; |
| char *p, *ttymsg(); |
| |
| 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; |
| #ifdef USER_PROCESS |
| if (ut.ut_type != USER_PROCESS) |
| continue; |
| #endif |
| if (f->f_type == F_WALL) { |
| if (p = ttymsg(iov, 6, ut.ut_line, 1)) { |
| 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, ut.ut_line, 1)) { |
| errno = 0; /* already in msg */ |
| logerror(p); |
| } |
| break; |
| } |
| } |
| } |
| (void) fclose(uf); |
| reenter = 0; |
| } |
| |
| void |
| reapchild() |
| { |
| #ifdef STATUS_BROKEN |
| int status; |
| #else |
| union wait status; |
| #endif |
| |
| while (wait3((int *)&status, WNOHANG, (struct rusage *) NULL) > 0) |
| ; |
| } |
| |
| /* |
| * Return a printable representation of a host address. |
| */ |
| char * |
| cvthname(f) |
| struct sockaddr_in *f; |
| { |
| struct hostent *hp; |
| register char *p; |
| extern char *inet_ntoa(); |
| |
| 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 = index(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0) |
| *p = '\0'; |
| return ((char *) hp->h_name); |
| } |
| |
| void |
| domark() |
| { |
| register struct filed *f; |
| time_t time(); |
| |
| 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); |
| } |
| } |
| #ifdef RE_INSTALL_SIGNAL |
| (void) signal(SIGALRM, domark); |
| #endif |
| (void) alarm(TIMERINTVL); |
| } |
| |
| /* |
| * Print syslogd errors some place. |
| */ |
| logerror(type) |
| char *type; |
| { |
| char buf[100], *strerror(); |
| |
| if (errno) |
| (void) sprintf(buf, "syslogd: %s: %s", type, strerror(errno)); |
| else |
| (void) sprintf(buf, "syslogd: %s", type); |
| errno = 0; |
| dprintf("%s\n", buf); |
| logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE); |
| } |
| |
| void |
| die(sig) |
| { |
| register 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 (sig) { |
| dprintf("syslogd: exiting on signal %d\n", sig); |
| (void) sprintf(buf, "exiting on signal %d", sig); |
| errno = 0; |
| logerror(buf); |
| } |
| (void) unlink(LogName); |
| exit(0); |
| } |
| |
| /* |
| * INIT -- Initialize syslogd from configuration table |
| */ |
| |
| void |
| init() |
| { |
| register int i; |
| register FILE *cf; |
| register struct filed *f, *next, **nextp; |
| register char *p; |
| char cline[BUFSIZ]; |
| |
| 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_TTY: |
| case F_CONSOLE: |
| case F_FORW: |
| (void) close(f->f_file); |
| break; |
| } |
| next = f->f_next; |
| 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; |
| while (fgets(cline, sizeof cline, cf) != NULL) { |
| /* |
| * check for end-of-section, comments, strip off trailing |
| * spaces and newline character. |
| */ |
| for (p = cline; isspace(*p); ++p); |
| if (*p == '\0' || *p == '#') |
| continue; |
| for (p = index(cline, '\0'); isspace(*--p);); |
| *++p = '\0'; |
| f = (struct filed *)calloc(1, sizeof(*f)); |
| *nextp = f; |
| nextp = &f->f_next; |
| cfline(cline, f); |
| } |
| |
| /* 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: |
| case F_TTY: |
| case F_CONSOLE: |
| printf("%s", 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; |
| } |
| printf("\n"); |
| } |
| } |
| |
| logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE); |
| dprintf("syslogd: restarted\n"); |
| } |
| |
| /* |
| * Crack a configuration file line |
| */ |
| |
| cfline(line, f) |
| char *line; |
| register struct filed *f; |
| { |
| register char *p; |
| register char *q; |
| register int i; |
| char *bp; |
| int pri; |
| struct hostent *hp; |
| char buf[MAXLINE], ebuf[100]; |
| |
| dprintf("cfline(%s)\n", line); |
| |
| errno = 0; /* keep strerror() stuff out of logerror messages */ |
| |
| /* clear out file entry */ |
| bzero((char *) f, sizeof *f); |
| for (i = 0; i <= LOG_NFACILITIES; i++) |
| f->f_pmask[i] = INTERNAL_NOPRI; |
| |
| /* 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 && !index("\t,;", *q); ) |
| *bp++ = *q++; |
| *bp = '\0'; |
| |
| /* skip cruft */ |
| while (index(", ;", *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 && !index("\t.;", *p)) { |
| for (bp = buf; *p && !index("\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, h_nerr; |
| extern char **h_errlist; |
| |
| logerror((u_int)h_errno < h_nerr ? |
| h_errlist[h_errno] : "Unknown error"); |
| break; |
| } |
| 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_type = F_FORW; |
| break; |
| |
| case '/': |
| (void) strcpy(f->f_un.f_fname, p); |
| #ifdef O_NOCTTY |
| if ((f->f_file = open(p, O_WRONLY|O_APPEND|O_NOCTTY, 0)) < 0) { |
| #else /* !O_NOCTTY */ |
| if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) { |
| #endif /* !O_NOCTTY */ |
| f->f_file = F_UNUSED; |
| logerror(p); |
| break; |
| } |
| if (isatty(f->f_file)) |
| f->f_type = F_TTY; |
| else |
| f->f_type = F_FILE; |
| if (strcmp(p, ctty) == 0) |
| f->f_type = F_CONSOLE; |
| 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 |
| */ |
| |
| decode(name, codetab) |
| char *name; |
| CODE *codetab; |
| { |
| register CODE *c; |
| register char *p; |
| char buf[40]; |
| |
| if (isdigit(*name)) |
| return (atoi(name)); |
| |
| (void) strcpy(buf, name); |
| for (p = buf; *p; p++) |
| if (isupper(*p)) |
| *p = tolower(*p); |
| for (c = codetab; c->c_name; c++) |
| if (!strcmp(buf, c->c_name)) |
| return (c->c_val); |
| |
| return (-1); |
| } |
| |
| #ifdef KLOG_STREAM |
| |
| int klogread(fd, buf, size) |
| int fd; |
| char *buf; |
| int size; |
| { |
| static char line[MAXLINE]; |
| static char *pos = line; |
| |
| int i; |
| char *obuf = buf; |
| char *s; |
| char *end; |
| *pos = '\0'; |
| if (!(end = strchr(line, '\n'))) { |
| struct timeval tv; |
| fd_set readfds; |
| |
| tv.tv_sec = tv.tv_usec = 0; |
| FD_ZERO(&readfds); |
| FD_SET(fd, &readfds); |
| i = select(fd + 1, (fd_set *) &readfds, (fd_set *) NULL, |
| (fd_set *) NULL, &tv); |
| if (i <= 0) |
| return i; |
| i = read(fd, pos, sizeof(line) - 1 - (pos - line)); |
| if (i <= 0) |
| return i; |
| pos += i; |
| *pos = '\0'; |
| if (!(end = strchr(line, '\n'))) |
| return 0; |
| } |
| for (s = line; s < end; s++) |
| if (*s != '\r') |
| *buf++ = *s; |
| end++; |
| for (s = line; end < pos; s++) |
| *s = *end++; |
| pos = s; |
| return (buf - obuf); |
| } |
| |
| #endif /* KLOG_STREAM */ |