diff options
Diffstat (limited to 'syslogd.c')
-rw-r--r-- | syslogd.c | 2803 |
1 files changed, 2803 insertions, 0 deletions
diff --git a/syslogd.c b/syslogd.c new file mode 100644 index 00000000..4c2014f6 --- /dev/null +++ b/syslogd.c @@ -0,0 +1,2803 @@ +/* + * Copyright (c) 1983, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if !defined(lint) && !defined(NO_SCCS) +char copyright2[] = +"@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#if !defined(lint) && !defined(NO_SCCS) +static char sccsid[] = "@(#)syslogd.c 5.27 (Berkeley) 10/10/88"; +#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 maximum 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) + * + * Steve Lord: Fix UNIX domain socket code, added linux kernel logging + * change defines to + * SYSLOG_INET - listen on a UDP socket + * SYSLOG_UNIXAF - listen on unix domain socket + * SYSLOG_KERNEL - listen to linux kernel + * + * Mon Feb 22 09:55:42 CST 1993: Dr. Wettstein + * Additional modifications to the source. Changed priority scheme + * to increase the level of configurability. In its stock configuration + * syslogd no longer logs all messages of a certain priority and above + * to a log file. The * wildcard is supported to specify all priorities. + * Note that this is a departure from the BSD standard. + * + * Syslogd will now listen to both the inetd and the unixd socket. The + * strategy is to allow all local programs to direct their output to + * syslogd through the unixd socket while the program listens to the + * inetd socket to get messages forwarded from other hosts. + * + * Fri Mar 12 16:55:33 CST 1993: Dr. Wettstein + * Thanks to Stephen Tweedie (dcs.ed.ac.uk!sct) for helpful bug-fixes + * and an enlightened commentary on the prioritization problem. + * + * Changed the priority scheme so that the default behavior mimics the + * standard BSD. In this scenario all messages of a specified priority + * and above are logged. + * + * Add the ability to specify a wildcard (=) as the first character + * of the priority name. Doing this specifies that ONLY messages with + * this level of priority are to be logged. For example: + * + * *.=debug /usr/adm/debug + * + * Would log only messages with a priority of debug to the /usr/adm/debug + * file. + * + * Providing an * as the priority specifies that all messages are to be + * logged. Note that this case is degenerate with specifying a priority + * level of debug. The wildcard * was retained because I believe that + * this is more intuitive. + * + * Thu Jun 24 11:34:13 CDT 1993: Dr. Wettstein + * Modified sources to incorporate changes in libc4.4. Messages from + * syslog are now null-terminated, syslogd code now parses messages + * based on this termination scheme. Linux as of libc4.4 supports the + * fsync system call. Modified code to fsync after all writes to + * log files. + * + * Sat Dec 11 11:59:43 CST 1993: Dr. Wettstein + * Extensive changes to the source code to allow compilation with no + * complaints with -Wall. + * + * Reorganized the facility and priority name arrays so that they + * compatible with the syslog.h source found in /usr/include/syslog.h. + * NOTE that this should really be changed. The reason I do not + * allow the use of the values defined in syslog.h is on account of + * the extensions made to allow the wildcard character in the + * priority field. To fix this properly one should malloc an array, + * copy the contents of the array defined by syslog.h and then + * make whatever modifications that are desired. Next round. + * + * Thu Jan 6 12:07:36 CST 1994: Dr. Wettstein + * Added support for proper decomposition and re-assembly of + * fragment messages on UNIX domain sockets. Lack of this capability + * was causing 'partial' messages to be output. Since facility and + * priority information is encoded as a leader on the messages this + * was causing lines to be placed in erroneous files. + * + * Also added a patch from Shane Alderton (shane@ion.apana.org.au) to + * correct a problem with syslogd dumping core when an attempt was made + * to write log messages to a logged-on user. Thank you. + * + * Many thanks to Juha Virtanen (jiivee@hut.fi) for a series of + * interchanges which lead to the fixing of problems with messages set + * to priorities of none and emerg. Also thanks to Juha for a patch + * to exclude users with a class of LOGIN from receiving messages. + * + * Shane Alderton provided an additional patch to fix zombies which + * were conceived when messages were written to multiple users. + * + * Mon Feb 6 09:57:10 CST 1995: Dr. Wettstein + * Patch to properly reset the single priority message flag. Thanks + * to Christopher Gori for spotting this bug and forwarding a patch. + * + * Wed Feb 22 15:38:31 CST 1995: Dr. Wettstein + * Added version information to startup messages. + * + * Added defines so that paths to important files are taken from + * the definitions in paths.h. Hopefully this will insure that + * everything follows the FSSTND standards. Thanks to Chris Metcalf + * for a set of patches to provide this functionality. Also thanks + * Elias Levy for prompting me to get these into the sources. + * + * Wed Jul 26 18:57:23 MET DST 1995: Martin Schulze + * Linux' gethostname only returns the hostname and not the fqdn as + * expected in the code. But if you call hostname with an fqdn then + * gethostname will return an fqdn, so we have to mention that. This + * has been changed. + * + * The 'LocalDomain' and the hostname of a remote machine is + * converted to lower case, because the original caused some + * inconsistency, because the (at least my) nameserver did respond an + * fqdn containing of upper- _and_ lowercase letters while + * 'LocalDomain' consisted only of lowercase letters and that didn't + * match. + * + * Sat Aug 5 18:59:15 MET DST 1995: Martin Schulze + * Now no messages that were received from any remote host are sent + * out to another. At my domain this missing feature caused ugly + * syslog-loops, sometimes. + * + * Remember that no message is sent out. I can't figure out any + * scenario where it might be useful to change this behavior and to + * send out messages to other hosts than the one from which we + * received the message, but I might be shortsighted. :-/ + * + * Thu Aug 10 19:01:08 MET DST 1995: Martin Schulze + * Added my pidfile.[ch] to it to perform a better handling with + * pidfiles. Now both, syslogd and klogd, can only be started + * once. They check the pidfile. + * + * Sun Aug 13 19:01:41 MET DST 1995: Martin Schulze + * Add an addition to syslog.conf's interpretation. If a priority + * begins with an exclamation mark ('!') the normal interpretation + * of the priority is inverted: ".!*" is the same as ".none", ".!=info" + * don't logs the info priority, ".!crit" won't log any message with + * the priority crit or higher. For example: + * + * mail.*;mail.!=info /usr/adm/mail + * + * Would log all messages of the facility mail except those with + * the priority info to /usr/adm/mail. This makes the syslogd + * much more flexible. + * + * Defined TABLE_ALLPRI=255 and changed some occurrences. + * + * Sat Aug 19 21:40:13 MET DST 1995: Martin Schulze + * Making the table of facilities and priorities while in debug + * mode more readable. + * + * If debugging is turned on, printing the whole table of + * facilities and priorities every hexadecimal or 'X' entry is + * now 2 characters wide. + * + * The number of the entry is prepended to each line of + * facilities and priorities, and F_UNUSED lines are not shown + * anymore. + * + * Corrected some #ifdef SYSV's. + * + * Mon Aug 21 22:10:35 MET DST 1995: Martin Schulze + * Corrected a strange behavior during parsing of configuration + * file. The original BSD syslogd doesn't understand spaces as + * separators between specifier and action. This syslogd now + * understands them. The old behavior caused some confusion over + * the Linux community. + * + * Thu Oct 19 00:02:07 MET 1995: Martin Schulze + * The default behavior has changed for security reasons. The + * syslogd will not receive any remote message unless you turn + * reception on with the "-r" option. + * + * Not defining SYSLOG_INET will result in not doing any network + * activity, i.e. not sending or receiving messages. I changed + * this because the old idea is implemented with the "-r" option + * and the old thing didn't work anyway. + * + * Thu Oct 26 13:14:06 MET 1995: Martin Schulze + * Added another logfile type F_FORW_UNKN. The problem I ran into + * was a name server that runs on my machine and a forwarder of + * kern.crit to another host. The hosts address can only be + * fetched using the nameserver. But named is started after + * syslogd, so syslogd complained. + * + * This logfile type will retry to get the address of the + * hostname ten times and then complain. This should be enough to + * get the named up and running during boot sequence. + * + * Fri Oct 27 14:08:15 1995: Dr. Wettstein + * Changed static array of logfiles to a dynamic array. This + * can grow during process. + * + * Fri Nov 10 23:08:18 1995: Martin Schulze + * Inserted a new tabular sys_h_errlist that contains plain text + * for error codes that are returned from the net subsystem and + * stored in h_errno. I have also changed some wrong lookups to + * sys_errlist. + * + * Wed Nov 22 22:32:55 1995: Martin Schulze + * Added the fabulous strip-domain feature that allows us to + * strip off (several) domain names from the fqdn and only log + * the simple hostname. This is useful if you're in a LAN that + * has a central log server and also different domains. + * + * I have also also added the -l switch do define hosts as + * local. These will get logged with their simple hostname, too. + * + * Thu Nov 23 19:02:56 MET DST 1995: Martin Schulze + * Added the possibility to omit fsyncing of logfiles after every + * write. This will give some performance back if you have + * programs that log in a very verbose manner (like innd or + * smartlist). Thanks to Stephen R. van den Berg <srb@cuci.nl> + * for the idea. + * + * Thu Jan 18 11:14:36 CST 1996: Dr. Wettstein + * Added patche from beta-testers to stop compile error. Also + * added removal of pid file as part of termination cleanup. + * + * Wed Feb 14 12:42:09 CST 1996: Dr. Wettstein + * Allowed forwarding of messages received from remote hosts to + * be controlled by a command-line switch. Specifying -h allows + * forwarding. The default behavior is to disable forwarding of + * messages which were received from a remote host. + * + * Parent process of syslogd does not exit until child process has + * finished initialization process. This allows rc.* startup to + * pause until syslogd facility is up and operating. + * + * Re-arranged the select code to move UNIX domain socket accepts + * to be processed later. This was a contributed change which + * has been proposed to correct the delays sometimes encountered + * when syslogd starts up. + * + * Minor code cleanups. + * + * Thu May 2 15:15:33 CDT 1996: Dr. Wettstein + * Fixed bug in init function which resulted in file descripters + * being orphaned when syslogd process was re-initialized with SIGHUP + * signal. Thanks to Edvard Tuinder + * (Edvard.Tuinder@praseodymium.cistron.nl) for putting me on the + * trail of this bug. I am amazed that we didn't catch this one + * before now. + * + * Tue May 14 00:03:35 MET DST 1996: Martin Schulze + * Corrected a mistake that causes the syslogd to stop logging at + * some virtual consoles under Linux. This was caused by checking + * the wrong error code. Thanks to Michael Nonweiler + * <mrn20@hermes.cam.ac.uk> for sending me a patch. + * + * Mon May 20 13:29:32 MET DST 1996: Miquel van Smoorenburg <miquels@cistron.nl> + * Added continuation line supported and fixed a bug in + * the init() code. + * + * Tue May 28 00:58:45 MET DST 1996: Martin Schulze + * Corrected behaviour of blocking pipes - i.e. the whole system + * hung. Michael Nonweiler <mrn20@hermes.cam.ac.uk> has sent us + * a patch to correct this. A new logfile type F_PIPE has been + * introduced. + * + * Mon Feb 3 10:12:15 MET DST 1997: Martin Schulze + * Corrected behaviour of logfiles if the file can't be opened. + * There was a bug that causes syslogd to try to log into non + * existing files which ate cpu power. + * + * Sun Feb 9 03:22:12 MET DST 1997: Martin Schulze + * Modified syslogd.c to not kill itself which confuses bash 2.0. + * + * Mon Feb 10 00:09:11 MET DST 1997: Martin Schulze + * Improved debug code to decode the numeric facility/priority + * pair into textual information. + * + * Tue Jun 10 12:35:10 MET DST 1997: Martin Schulze + * Corrected freeing of logfiles. Thanks to Jos Vos <jos@xos.nl> + * for reporting the bug and sending an idea to fix the problem. + * + * Tue Jun 10 12:51:41 MET DST 1997: Martin Schulze + * Removed sleep(10) from parent process. This has caused a slow + * startup in former times - and I don't see any reason for this. + * + * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman + * Some more glibc patches made by <mdorman@debian.org>. + * + * Thu Jan 1 16:04:52 CET 1998: Martin Schulze <joey@infodrom.north.de + * Applied patch from Herbert Thielen <Herbert.Thielen@lpr.e-technik.tu-muenchen.de>. + * This included some balance parentheses for emacs and a bug in + * the exclamation mark handling. + * + * Fixed small bug which caused syslogd to write messages to the + * wrong logfile under some very rare conditions. Thanks to + * Herbert Xu <herbert@gondor.apana.org.au> for fiddling this out. + * + * Thu Jan 8 22:46:35 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Reworked one line of the above patch as it prevented syslogd + * from binding the socket with the result that no messages were + * forwarded to other hosts. + * + * Sat Jan 10 01:33:06 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Fixed small bugs in F_FORW_UNKN meachanism. Thanks to Torsten + * Neumann <torsten@londo.rhein-main.de> for pointing me to it. + * + * Mon Jan 12 19:50:58 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Modified debug output concerning remote receiption. + * + * Mon Feb 23 23:32:35 CET 1998: Topi Miettinen <Topi.Miettinen@ml.tele.fi> + * Re-worked handling of Unix and UDP sockets to support closing / + * opening of them in order to have it open only if it is needed + * either for forwarding to a remote host or by receiption from + * the network. + * + * Wed Feb 25 10:54:09 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Fixed little comparison mistake that prevented the MARK + * feature to work properly. + * + * Wed Feb 25 13:21:44 CET 1998: Martin Schulze <joey@infodrom.north.de> + * Corrected Topi's patch as it prevented forwarding during + * startup due to an unknown LogPort. + * + * Sat Oct 10 20:01:48 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Added support for TESTING define which will turn syslogd into + * stdio-mode used for debugging. + * + * Sun Oct 11 20:16:59 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Reworked the initialization/fork code. Now the parent + * process activates a signal handler which the daughter process + * will raise if it is initialized. Only after that one the + * parent process may exit. Otherwise klogd might try to flush + * its log cache while syslogd can't receive the messages yet. + * + * Mon Oct 12 13:30:35 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Redirected some error output with regard to argument parsing to + * stderr. + * + * Mon Oct 12 14:02:51 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Applied patch provided vom Topi Miettinen with regard to the + * people from OpenBSD. This provides the additional '-a' + * argument used for specifying additional UNIX domain sockets to + * listen to. This is been used with chroot()'ed named's for + * example. See for http://www.psionic.com/papers/dns.html + * + * Mon Oct 12 18:29:44 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Added `ftp' facility which was introduced in glibc version 2. + * It's #ifdef'ed so won't harm with older libraries. + * + * Mon Oct 12 19:59:21 MET DST 1998: Martin Schulze <joey@infodrom.north.de> + * Code cleanups with regard to bsd -> posix transition and + * stronger security (buffer length checking). Thanks to Topi + * Miettinen <tom@medialab.sonera.net> + * . index() --> strchr() + * . sprintf() --> snprintf() + * . bcopy() --> memcpy() + * . bzero() --> memset() + * . UNAMESZ --> UT_NAMESIZE + * . sys_errlist --> strerror() + * + * Mon Oct 12 20:22:59 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Added support for setutent()/getutent()/endutend() instead of + * binary reading the UTMP file. This is the the most portable + * way. This allows /var/run/utmp format to change, even to a + * real database or utmp daemon. Also if utmp file locking is + * implemented in libc, syslog will use it immediately. Thanks + * to Topi Miettinen <tom@medialab.sonera.net>. + * + * Mon Oct 12 20:49:18 MET DST 1998: Martin Schulze <joey@infodrom.north.de> + * Avoid logging of SIGCHLD when syslogd is in the process of + * exiting and closing its files. Again thanks to Topi. + * + * Mon Oct 12 22:18:34 CEST 1998: Martin Schulze <joey@infodrom.north.de> + * Modified printline() to support 8bit characters - such as + * russion letters. Thanks to Vladas Lapinskas <lapinskas@mail.iae.lt>. + * + * Sat Nov 14 02:29:37 CET 1998: Martin Schulze <joey@infodrom.north.de> + * ``-m 0'' now turns of MARK logging entirely. + * + * Tue Jan 19 01:04:18 MET 1999: Martin Schulze <joey@infodrom.north.de> + * Finally fixed an error with `-a' processing, thanks to Topi + * Miettinen <tom@medialab.sonera.net>. + * + * Sun May 23 10:08:53 CEST 1999: Martin Schulze <joey@infodrom.north.de> + * Removed superflous call to utmpname(). The path to the utmp + * file is defined in the used libc and should not be hardcoded + * into the syslogd binary referring the system it was compiled on. + * + * Sun Sep 17 20:45:33 CEST 2000: Martin Schulze <joey@infodrom.ffis.de> + * Fixed some bugs in printline() code that did not escape + * control characters '\177' through '\237' and contained a + * single-byte buffer overflow. Thanks to Solar Designer + * <solar@false.com>. + * + * Sun Sep 17 21:26:16 CEST 2000: Martin Schulze <joey@infodrom.ffis.de> + * Don't close open sockets upon reload. Thanks to Bill + * Nottingham. + * + * Mon Sep 18 09:10:47 CEST 2000: Martin Schulze <joey@infodrom.ffis.de> + * Fixed bug in printchopped() that caused syslogd to emit + * kern.emerg messages when splitting long lines. Thanks to + * Daniel Jacobowitz <dan@debian.org> for the fix. + * + * Mon Sep 18 15:33:26 CEST 2000: Martin Schulze <joey@infodrom.ffis.de> + * Removed unixm/unix domain sockets and switch to Datagram Unix + * Sockets. This should remove one possibility to play DoS with + * syslogd. Thanks to Olaf Kirch <okir@caldera.de> for the patch. + * + * Sun Mar 11 20:23:44 CET 2001: Martin Schulze <joey@infodrom.ffis.de> + * Don't return a closed fd if `-a' is called with a wrong path. + * Thanks to Bill Nottingham <notting@redhat.com> for providing + * a patch. + */ + + +#define MAXLINE 1024 /* maximum line length */ +#define MAXSVLINE 240 /* 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 CONT_LINE 1 /* Allow continuation lines */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#ifdef SYSV +#include <sys/types.h> +#endif +#include <utmp.h> +#include <ctype.h> +#include <string.h> +#include <setjmp.h> +#include <stdarg.h> +#include <time.h> + +#define SYSLOG_NAMES +#include <sys/syslog.h> +#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 SYSV +#include <fcntl.h> +#else +#include <sys/msgbuf.h> +#endif +#include <sys/uio.h> +#include <sys/un.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <signal.h> + +#include <netinet/in.h> +#include <netdb.h> +#include <syscall.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <resolv.h> +#ifndef TESTING +#include "pidfile.h" +#endif +#include "version.h" + +#if defined(__linux__) +#include <paths.h> +#endif + +#ifndef UTMP_FILE +#ifdef UTMP_FILENAME +#define UTMP_FILE UTMP_FILENAME +#else +#ifdef _PATH_UTMP +#define UTMP_FILE _PATH_UTMP +#else +#define UTMP_FILE "/etc/utmp" +#endif +#endif +#endif + +#ifndef _PATH_LOGCONF +#define _PATH_LOGCONF "/etc/syslog.conf" +#endif + +#if defined(SYSLOGD_PIDNAME) +#undef _PATH_LOGPID +#if defined(FSSTND) +#define _PATH_LOGPID _PATH_VARRUN SYSLOGD_PIDNAME +#else +#define _PATH_LOGPID "/etc/" SYSLOGD_PIDNAME +#endif +#else +#ifndef _PATH_LOGPID +#if defined(FSSTND) +#define _PATH_LOGPID _PATH_VARRUN "syslogd.pid" +#else +#define _PATH_LOGPID "/etc/syslogd.pid" +#endif +#endif +#endif + +#ifndef _PATH_DEV +#define _PATH_DEV "/dev/" +#endif + +#ifndef _PATH_CONSOLE +#define _PATH_CONSOLE "/dev/console" +#endif + +#ifndef _PATH_TTY +#define _PATH_TTY "/dev/tty" +#endif + +#ifndef _PATH_LOG +#define _PATH_LOG "/dev/log" +#endif + +char *ConfFile = _PATH_LOGCONF; +char *PidFile = _PATH_LOGPID; +char ctty[] = _PATH_CONSOLE; + +char **parts; + +int inetm = 0; +static int debugging_on = 0; +static int nlogs = -1; +static int restart = 0; + +#define MAXFUNIX 20 + +int nfunix = 1; +char *funixn[MAXFUNIX] = { _PATH_LOG }; +int funix[MAXFUNIX] = { -1, }; + +#ifdef UT_NAMESIZE +# define UNAMESZ UT_NAMESIZE /* length of a login name */ +#else +# define UNAMESZ 8 /* length of a login name */ +#endif +#define MAXUNAMES 20 /* maximum number of user names */ +#define MAXFNAME 200 /* max file pathname length */ + +#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ +#define TABLE_NOPRI 0 /* Value to indicate no priority in f_pmask */ +#define TABLE_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */ +#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */ + +/* + * 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 table contains plain text for h_errno errors used by the + * net subsystem. + */ +const char *sys_h_errlist[] = { + "No problem", /* NETDB_SUCCESS */ + "Authoritative answer: host not found", /* HOST_NOT_FOUND */ + "Non-authoritative answer: host not found, or serverfail", /* TRY_AGAIN */ + "Non recoverable errors", /* NO_RECOVERY */ + "Valid name, no data record of requested type", /* NO_DATA */ + "no address, look for MX record" /* NO_ADDRESS */ + }; + +/* + * This structure represents the files that will have log + * copies printed. + */ + +struct filed { +#ifndef SYSV + struct filed *f_next; /* next in linked list */ +#endif + 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][UNAMESZ+1]; + struct { + char f_hname[MAXHOSTNAMELEN+1]; + struct sockaddr_in f_addr; + } f_forw; /* forwarding address */ + char f_fname[MAXFNAME]; + } 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 */ + int f_flags; /* store some additional flags */ +}; + +/* + * 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, 60 }; /* # 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; \ + } +#ifdef SYSLOG_INET +#define INET_SUSPEND_TIME 180 /* equal to 3 minutes */ +#define INET_RETRY_MAX 10 /* maximum of retries for gethostbyname() */ +#endif + +#define LIST_DELIMITER ':' /* delimiter between two hosts */ + +/* 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 */ +#define F_FORW_SUSP 7 /* suspended host forwarding */ +#define F_FORW_UNKN 8 /* unknown host forwarding */ +#define F_PIPE 9 /* named pipe */ +char *TypeNames[] = { + "UNUSED", "FILE", "TTY", "CONSOLE", + "FORW", "USERS", "WALL", "FORW(SUSPENDED)", + "FORW(UNKNOWN)", "PIPE" +}; + +struct filed *Files = (struct filed *) 0; +struct filed consfile; + +struct code { + char *c_name; + int c_val; +}; + +struct code PriNames[] = { + {"alert", LOG_ALERT}, + {"crit", LOG_CRIT}, + {"debug", LOG_DEBUG}, + {"emerg", LOG_EMERG}, + {"err", LOG_ERR}, + {"error", LOG_ERR}, /* DEPRECATED */ + {"info", LOG_INFO}, + {"none", INTERNAL_NOPRI}, /* INTERNAL */ + {"notice", LOG_NOTICE}, + {"panic", LOG_EMERG}, /* DEPRECATED */ + {"warn", LOG_WARNING}, /* DEPRECATED */ + {"warning", LOG_WARNING}, + {"*", TABLE_ALLPRI}, + {NULL, -1} +}; + +struct code FacNames[] = { + {"auth", LOG_AUTH}, + {"authpriv", LOG_AUTHPRIV}, + {"cron", LOG_CRON}, + {"daemon", LOG_DAEMON}, + {"kern", LOG_KERN}, + {"lpr", LOG_LPR}, + {"mail", LOG_MAIL}, + {"mark", LOG_MARK}, /* INTERNAL */ + {"news", LOG_NEWS}, + {"security", LOG_AUTH}, /* DEPRECATED */ + {"syslog", LOG_SYSLOG}, + {"user", LOG_USER}, + {"uucp", LOG_UUCP}, +#if defined(LOG_FTP) + {"ftp", LOG_FTP}, +#endif + {"local0", LOG_LOCAL0}, + {"local1", LOG_LOCAL1}, + {"local2", LOG_LOCAL2}, + {"local3", LOG_LOCAL3}, + {"local4", LOG_LOCAL4}, + {"local5", LOG_LOCAL5}, + {"local6", LOG_LOCAL6}, + {"local7", LOG_LOCAL7}, + {NULL, -1}, +}; + +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 = -1; /* 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 NoFork = 0; /* don't fork - don't run in daemon mode */ +int AcceptRemote = 0; /* receive messages that come via UDP */ +char **StripDomains = NULL; /* these domains may be stripped before writing logs */ +char **LocalHosts = NULL; /* these hosts are logged with their hostname */ +int NoHops = 1; /* Can we bounce syslog messages through an + intermediate host. */ + +extern int errno; + +/* Function prototypes. */ +int main(int argc, char **argv); +char **crunch_list(char *list); +int usage(void); +void untty(void); +void printchopped(const char *hname, char *msg, int len, int fd); +void printline(const char *hname, char *msg); +void printsys(char *msg); +void logmsg(int pri, char *msg, const char *from, int flags); +void fprintlog(register struct filed *f, char *from, int flags, char *msg); +void endtty(); +void wallmsg(register struct filed *f, struct iovec *iov); +void reapchild(); +const char *cvthname(struct sockaddr_in *f); +void domark(); +void debug_switch(); +void logerror(char *type); +void die(int sig); +#ifndef TESTING +void doexit(int sig); +#endif +void init(); +void cfline(char *line, register struct filed *f); +int decode(char *name, struct code *codetab); +#if defined(__GLIBC__) +#define dprintf mydprintf +#endif /* __GLIBC__ */ +static void dprintf(char *, ...); +static void allocate_log(void); +void sighup_handler(); + +#ifdef SYSLOG_UNIXAF +static int create_unix_socket(const char *path); +#endif +#ifdef SYSLOG_INET +static int create_inet_socket(); +#endif + +int main(argc, argv) + int argc; + char **argv; +{ + register int i; + register char *p; +#if !defined(__GLIBC__) + int len, num_fds; +#else /* __GLIBC__ */ +#ifndef TESTING + size_t len; +#endif + int num_fds; +#endif /* __GLIBC__ */ + /* + * It took me quite some time to figure out how this is + * supposed to work so I guess I should better write it down. + * unixm is a list of file descriptors from which one can + * read(). This is in contrary to readfds which is a list of + * file descriptors where activity is monitored by select() + * and from which one cannot read(). -Joey + * + * Changed: unixm is gone, since we now use datagram unix sockets. + * Hence we recv() from unix sockets directly (rather than + * first accept()ing connections on them), so there's no need + * for separate book-keeping. --okir + */ + fd_set readfds; + +#ifndef TESTING + int fd; +#ifdef SYSLOG_INET + struct sockaddr_in frominet; + char *from; +#endif + pid_t ppid = getpid(); +#endif + int ch; + struct hostent *hent; + + char line[MAXLINE +1]; + extern int optind; + extern char *optarg; + int maxfds; + +#ifndef TESTING + chdir ("/"); +#endif + for (i = 1; i < MAXFUNIX; i++) { + funixn[i] = ""; + funix[i] = -1; + } + + while ((ch = getopt(argc, argv, "a:dhf:l:m:np:rs:v")) != EOF) + switch((char)ch) { + case 'a': + if (nfunix < MAXFUNIX) + funixn[nfunix++] = optarg; + else + fprintf(stderr, "Out of descriptors, ignoring %s\n", optarg); + break; + case 'd': /* debug */ + Debug = 1; + break; + case 'f': /* configuration file */ + ConfFile = optarg; + break; + case 'h': + NoHops = 0; + break; + case 'l': + if (LocalHosts) { + fprintf (stderr, "Only one -l argument allowed," \ + "the first one is taken.\n"); + break; + } + LocalHosts = crunch_list(optarg); + break; + case 'm': /* mark interval */ + MarkInterval = atoi(optarg) * 60; + break; + case 'n': /* don't fork */ + NoFork = 1; + break; + case 'p': /* path to regular log socket */ + funixn[0] = optarg; + break; + case 'r': /* accept remote messages */ + AcceptRemote = 1; + break; + case 's': + if (StripDomains) { + fprintf (stderr, "Only one -s argument allowed," \ + "the first one is taken.\n"); + break; + } + StripDomains = crunch_list(optarg); + break; + case 'v': + printf("syslogd %s.%s\n", VERSION, PATCHLEVEL); + exit (0); + case '?': + default: + usage(); + } + if ((argc -= optind)) + usage(); + +#ifndef TESTING + if ( !(Debug || NoFork) ) + { + dprintf("Checking pidfile.\n"); + if (!check_pid(PidFile)) + { + if (fork()) { + /* + * Parent process + */ + signal (SIGTERM, doexit); + sleep(300); + /* + * Not reached unless something major went wrong. 5 + * minutes should be a fair amount of time to wait. + * Please note that this procedure is important since + * the father must not exit before syslogd isn't + * initialized or the klogd won't be able to flush its + * logs. -Joey + */ + exit(1); + } + num_fds = getdtablesize(); + for (i= 0; i < num_fds; i++) + (void) close(i); + untty(); + } + else + { + fputs("syslogd: Already running.\n", stderr); + exit(1); + } + } + else +#endif + debugging_on = 1; +#ifndef SYSV + else + setlinebuf(stdout); +#endif + +#ifndef TESTING + /* tuck my process id away */ + if ( !Debug ) + { + dprintf("Writing pidfile.\n"); + if (!check_pid(PidFile)) + { + if (!write_pid(PidFile)) + { + dprintf("Can't write pid.\n"); + exit(1); + } + } + else + { + dprintf("Pidfile (and pid) already exist.\n"); + exit(1); + } + } /* if ( !Debug ) */ +#endif + + consfile.f_type = F_CONSOLE; + (void) strcpy(consfile.f_un.f_fname, ctty); + (void) gethostname(LocalHostName, sizeof(LocalHostName)); + if ( (p = strchr(LocalHostName, '.')) ) { + *p++ = '\0'; + LocalDomain = p; + } + else + { + LocalDomain = ""; + + /* + * It's not clearly defined whether gethostname() + * should return the simple hostname or the fqdn. A + * good piece of software should be aware of both and + * we want to distribute good software. Joey + * + * Good software also always checks its return values... + * If syslogd starts up before DNS is up & /etc/hosts + * doesn't have LocalHostName listed, gethostbyname will + * return NULL. + */ + hent = gethostbyname(LocalHostName); + if ( hent ) + snprintf(LocalHostName, sizeof(LocalHostName), "%s", hent->h_name); + + if ( (p = strchr(LocalHostName, '.')) ) + { + *p++ = '\0'; + LocalDomain = p; + } + } + + /* + * Convert to lower case to recognize the correct domain laterly + */ + for (p = (char *)LocalDomain; *p ; p++) + if (isupper(*p)) + *p = tolower(*p); + + (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) signal(SIGUSR1, Debug ? debug_switch : SIG_IGN); + (void) alarm(TIMERINTVL); + + /* Create a partial message table for all file descriptors. */ + num_fds = getdtablesize(); + dprintf("Allocated parts table for %d file descriptors.\n", num_fds); + if ( (parts = (char **) malloc(num_fds * sizeof(char *))) == \ + (char **) 0 ) + { + logerror("Cannot allocate memory for message parts table."); + die(0); + } + for(i= 0; i < num_fds; ++i) + parts[i] = (char *) 0; + + dprintf("Starting.\n"); + init(); +#ifndef TESTING + if ( Debug ) + { + dprintf("Debugging disabled, SIGUSR1 to turn on debugging.\n"); + debugging_on = 0; + } + /* + * Send a signal to the parent to it can terminate. + */ + if (getpid() != ppid) + kill (ppid, SIGTERM); +#endif + + /* Main loop begins here. */ + for (;;) { + int nfds; + errno = 0; + FD_ZERO(&readfds); + maxfds = 0; +#ifdef SYSLOG_UNIXAF +#ifndef TESTING + /* + * Add the Unix Domain Sockets to the list of read + * descriptors. + */ + /* Copy master connections */ + for (i = 0; i < nfunix; i++) { + if (funix[i] != -1) { + FD_SET(funix[i], &readfds); + if (funix[i]>maxfds) maxfds=funix[i]; + } + } +#endif +#endif +#ifdef SYSLOG_INET +#ifndef TESTING + /* + * Add the Internet Domain Socket to the list of read + * descriptors. + */ + if ( InetInuse && AcceptRemote ) { + FD_SET(inetm, &readfds); + if (inetm>maxfds) maxfds=inetm; + dprintf("Listening on syslog UDP port.\n"); + } +#endif +#endif +#ifdef TESTING + FD_SET(fileno(stdin), &readfds); + if (fileno(stdin) > maxfds) maxfds = fileno(stdin); + + dprintf("Listening on stdin. Press Ctrl-C to interrupt.\n"); +#endif + + if ( debugging_on ) + { + dprintf("Calling select, active file descriptors (max %d): ", maxfds); + for (nfds= 0; nfds <= maxfds; ++nfds) + if ( FD_ISSET(nfds, &readfds) ) + dprintf("%d ", nfds); + dprintf("\n"); + } + nfds = select(maxfds+1, (fd_set *) &readfds, (fd_set *) NULL, + (fd_set *) NULL, (struct timeval *) NULL); + if ( restart ) + { + dprintf("\nReceived SIGHUP, reloading syslogd.\n"); + init(); + restart = 0; + continue; + } + if (nfds == 0) { + dprintf("No select activity.\n"); + continue; + } + if (nfds < 0) { + if (errno != EINTR) + logerror("select"); + dprintf("Select interrupted.\n"); + continue; + } + + if ( debugging_on ) + { + dprintf("\nSuccessful select, descriptor count = %d, " \ + "Activity on: ", nfds); + for (nfds= 0; nfds <= maxfds; ++nfds) + if ( FD_ISSET(nfds, &readfds) ) + dprintf("%d ", nfds); + dprintf(("\n")); + } + +#ifndef TESTING +#ifdef SYSLOG_UNIXAF + for (i = 0; i < nfunix; i++) { + if ((fd = funix[i]) != -1 && FD_ISSET(fd, &readfds)) { + memset(line, '\0', sizeof(line)); + i = recv(fd, line, MAXLINE - 2, 0); + dprintf("Message from UNIX socket: #%d\n", fd); + if (i > 0) { + line[i] = line[i+1] = '\0'; + printchopped(LocalHostName, line, i + 2, fd); + } else if (i < 0 && errno != EINTR) { + dprintf("UNIX socket error: %d = %s.\n", \ + errno, strerror(errno)); + logerror("recvfrom UNIX"); + } + } + } +#endif + +#ifdef SYSLOG_INET + if (InetInuse && AcceptRemote && FD_ISSET(inetm, &readfds)) { + len = sizeof(frominet); + memset(line, '\0', sizeof(line)); + i = recvfrom(finet, line, MAXLINE - 2, 0, \ + (struct sockaddr *) &frominet, &len); + dprintf("Message from inetd socket: #%d, host: %s\n", + inetm, inet_ntoa(frominet.sin_addr)); + if (i > 0) { + line[i] = line[i+1] = '\0'; + from = (char *)cvthname(&frominet); + /* + * Here we could check if the host is permitted + * to send us syslog messages. We just have to + * catch the result of cvthname, look for a dot + * and if that doesn't exist, replace the first + * '\0' with '.' and we have the fqdn in lowercase + * letters so we could match them against whatever. + * -Joey + */ + printchopped(from, line, \ + i + 2, finet); + } else if (i < 0 && errno != EINTR) { + dprintf("INET socket error: %d = %s.\n", \ + errno, strerror(errno)); + logerror("recvfrom inet"); + /* should be harmless now that we set + * BSDCOMPAT on the socket */ + sleep(10); + } + } +#endif +#else + if ( FD_ISSET(fileno(stdin), &readfds) ) { + dprintf("Message from stdin.\n"); + memset(line, '\0', sizeof(line)); + line[0] = '.'; + parts[fileno(stdin)] = (char *) 0; + i = read(fileno(stdin), line, MAXLINE); + if (i > 0) { + printchopped(LocalHostName, line, i+1, fileno(stdin)); + } else if (i < 0) { + if (errno != EINTR) { + logerror("stdin"); + } + } + FD_CLR(fileno(stdin), &readfds); + } + +#endif + } +} + +int usage() +{ + fprintf(stderr, "usage: syslogd [-drvh] [-l hostlist] [-m markinterval] [-n] [-p path]\n" \ + " [-s domainlist] [-f conffile]\n"); + exit(1); +} + +#ifdef SYSLOG_UNIXAF +static int create_unix_socket(const char *path) +{ + struct sockaddr_un sunx; + int fd; + char line[MAXLINE +1]; + + if (path[0] == '\0') + return -1; + + (void) unlink(path); + + memset(&sunx, 0, sizeof(sunx)); + sunx.sun_family = AF_UNIX; + (void) strncpy(sunx.sun_path, path, sizeof(sunx.sun_path)); + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0 || bind(fd, (struct sockaddr *) &sunx, + sizeof(sunx.sun_family)+strlen(sunx.sun_path)) < 0 || + chmod(path, 0666) < 0) { + (void) snprintf(line, sizeof(line), "cannot create %s", path); + logerror(line); + dprintf("cannot create %s (%d).\n", path, errno); + close(fd); +#ifndef SYSV + die(0); +#endif + return -1; + } + return fd; +} +#endif + +#ifdef SYSLOG_INET +static int create_inet_socket() +{ + int fd, on = 1; + struct sockaddr_in sin; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + logerror("syslog: Unknown protocol, suspending inet service."); + return fd; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = LogPort; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, \ + (char *) &on, sizeof(on)) < 0 ) { + logerror("setsockopt(REUSEADDR), suspending inet"); + close(fd); + return -1; + } + /* We need to enable BSD compatibility. Otherwise an attacker + * could flood our log files by sending us tons of ICMP errors. + */ + if (setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, \ + (char *) &on, sizeof(on)) < 0) { + logerror("setsockopt(BSDCOMPAT), suspending inet"); + close(fd); + return -1; + } + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + logerror("bind, suspending inet"); + close(fd); + return -1; + } + return fd; +} +#endif + +char ** +crunch_list(list) + char *list; +{ + int count, i; + char *p, *q; + char **result = NULL; + + p = list; + + /* strip off trailing delimiters */ + while (p[strlen(p)-1] == LIST_DELIMITER) { + count--; + p[strlen(p)-1] = '\0'; + } + /* cut off leading delimiters */ + while (p[0] == LIST_DELIMITER) { + count--; + p++; + } + + /* count delimiters to calculate elements */ + for (count=i=0; p[i]; i++) + if (p[i] == LIST_DELIMITER) count++; + + if ((result = (char **)malloc(sizeof(char *) * count+2)) == NULL) { + printf ("Sorry, can't get enough memory, exiting.\n"); + exit(0); + } + + /* + * We now can assume that the first and last + * characters are different from any delimiters, + * so we don't have to care about this. + */ + count = 0; + while ((q=strchr(p, LIST_DELIMITER))) { + result[count] = (char *) malloc((q - p + 1) * sizeof(char)); + if (result[count] == NULL) { + printf ("Sorry, can't get enough memory, exiting.\n"); + exit(0); + } + strncpy(result[count], p, q - p); + result[count][q - p] = '\0'; + p = q; p++; + count++; + } + if ((result[count] = \ + (char *)malloc(sizeof(char) * strlen(p) + 1)) == NULL) { + printf ("Sorry, can't get enough memory, exiting.\n"); + exit(0); + } + strcpy(result[count],p); + result[++count] = NULL; + +#if 0 + count=0; + while (result[count]) + dprintf ("#%d: %s\n", count, StripDomains[count++]); +#endif + return result; +} + + +void untty() +#ifdef SYSV +{ + if ( !Debug ) { + setsid(); + } + return; +} + +#else +{ + int i; + + if ( !Debug ) { + i = open(_PATH_TTY, O_RDWR); + if (i >= 0) { + (void) ioctl(i, (int) TIOCNOTTY, (char *)0); + (void) close(i); + } + } +} +#endif + + +/* + * Parse the line to make sure that the msg is not a composite of more + * than one message. + */ + +void printchopped(hname, msg, len, fd) + const char *hname; + char *msg; + int len; + int fd; +{ + auto int ptlngth; + + auto char *start = msg, + *p, + *end, + tmpline[MAXLINE + 1]; + + dprintf("Message length: %d, File descriptor: %d.\n", len, fd); + tmpline[0] = '\0'; + if ( parts[fd] != (char *) 0 ) + { + dprintf("Including part from messages.\n"); + strcpy(tmpline, parts[fd]); + free(parts[fd]); + parts[fd] = (char *) 0; + if ( (strlen(msg) + strlen(tmpline)) > MAXLINE ) + { + logerror("Cannot glue message parts together"); + printline(hname, tmpline); + start = msg; + } + else + { + dprintf("Previous: %s\n", tmpline); + dprintf("Next: %s\n", msg); + strcat(tmpline, msg); /* length checked above */ + printline(hname, tmpline); + if ( (strlen(msg) + 1) == len ) + return; + else + start = strchr(msg, '\0') + 1; + } + } + + if ( msg[len-1] != '\0' ) + { + msg[len] = '\0'; + for(p= msg+len-1; *p != '\0' && p > msg; ) + --p; + if(*p == '\0') p++; + ptlngth = strlen(p); + if ( (parts[fd] = malloc(ptlngth + 1)) == (char *) 0 ) + logerror("Cannot allocate memory for message part."); + else + { + strcpy(parts[fd], p); + dprintf("Saving partial msg: %s\n", parts[fd]); + memset(p, '\0', ptlngth); + } + } + + do { + end = strchr(start + 1, '\0'); + printline(hname, start); + start = end + 1; + } while ( *start != '\0' ); + + return; +} + + + +/* + * Take a raw input line, decode the message, and print the message + * on the appropriate log files. + */ + +void printline(hname, msg) + const char *hname; + char *msg; +{ + register char *p, *q; + register unsigned char 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; + + memset (line, 0, sizeof(line)); + q = line; + while ((c = *p++) && q < &line[sizeof(line) - 4]) { + if (c == '\n') + *q++ = ' '; + else if (c < 040) { + *q++ = '^'; + *q++ = c ^ 0100; + } else if (c == 0177 || (c & 0177) < 040) { + *q++ = '\\'; + *q++ = '0' + ((c & 0300) >> 6); + *q++ = '0' + ((c & 0070) >> 3); + *q++ = '0' + (c & 0007); + } else + *q++ = c; + } + *q = '\0'; + + logmsg(pri, line, hname, SYNC_FILE); + return; +} + + + +/* + * Take a raw input line from /dev/klog, split and format similar to syslog(). + */ + +void printsys(msg) + char *msg; +{ + register char *p, *q; + register int c; + char line[MAXLINE + 1]; + int pri, flags; + char *lp; + + (void) snprintf(line, sizeof(line), "vmunix: "); + lp = line + strlen(line); + for (p = msg; *p != '\0'; ) { + flags = ADDDATE; + 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); + } + return; +} + +/* + * Decode a priority into textual information like auth.emerg. + */ +char *textpri(pri) + int pri; +{ + static char res[20]; + CODE *c_pri, *c_fac; + + for (c_fac = facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++); + for (c_pri = prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++); + + snprintf (res, sizeof(res), "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri); + + return res; +} + +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; + const char *from; + int flags; +{ + register struct filed *f; + int fac, prilev, lognum; + int msglen; + char *timestamp; + + dprintf("logmsg: %s, flags %x, from %s, msg %s\n", textpri(pri), flags, from, msg); + +#ifndef SYSV + omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM)); +#endif + + /* + * 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; + f->f_file = open(ctty, O_WRONLY|O_NOCTTY); + + if (f->f_file >= 0) { + untty(); + fprintlog(f, (char *)from, flags, msg); + (void) close(f->f_file); + f->f_file = -1; + } +#ifndef SYSV + (void) sigsetmask(omask); +#endif + return; + } +#ifdef SYSV + for (lognum = 0; lognum <= nlogs; lognum++) { + f = &Files[lognum]; +#else + for (f = Files; f; f = f->f_next) { +#endif + + /* skip messages that are incorrect priority */ + if ( (f->f_pmask[fac] == TABLE_NOPRI) || \ + ((f->f_pmask[fac] & (1<<prilev)) == 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, (char *)from, flags, (char *)NULL); + BACKOFF(f); + } + } else { + /* new line, save it */ + if (f->f_prevcount) + fprintlog(f, (char *)from, 0, (char *)NULL); + f->f_prevpri = pri; + 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; + (void) strcpy(f->f_prevline, msg); + fprintlog(f, (char *)from, flags, (char *)NULL); + } else { + f->f_prevline[0] = 0; + f->f_prevlen = 0; + fprintlog(f, (char *)from, flags, msg); + } + } + } +#ifndef SYSV + (void) sigsetmask(omask); +#endif +} +#if FALSE +} /* balance parentheses for emacs */ +#endif + +void fprintlog(f, from, flags, msg) + register struct filed *f; + char *from; + int flags; + char *msg; +{ + struct iovec iov[6]; + register struct iovec *v = iov; + char repbuf[80]; +#ifdef SYSLOG_INET + register int l; + char line[MAXLINE + 1]; + time_t fwd_suspend; + struct hostent *hp; +#endif + + dprintf("Called fprintlog, "); + + 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) { + (void) snprintf(repbuf, sizeof(repbuf), "last message repeated %d times", + f->f_prevcount); + v->iov_base = repbuf; + v->iov_len = strlen(repbuf); + } else { + v->iov_base = f->f_prevline; + v->iov_len = f->f_prevlen; + } + v++; + + dprintf("logging to %s", TypeNames[f->f_type]); + + switch (f->f_type) { + case F_UNUSED: + f->f_time = now; + dprintf("\n"); + break; + +#ifdef SYSLOG_INET + case F_FORW_SUSP: + fwd_suspend = time((time_t *) 0) - f->f_time; + if ( fwd_suspend >= INET_SUSPEND_TIME ) { + dprintf("\nForwarding suspension over, " \ + "retrying FORW "); + f->f_type = F_FORW; + goto f_forw; + } + else { + dprintf(" %s\n", f->f_un.f_forw.f_hname); + dprintf("Forwarding suspension not over, time " \ + "left: %d.\n", INET_SUSPEND_TIME - \ + fwd_suspend); + } + break; + + /* + * The trick is to wait some time, then retry to get the + * address. If that fails retry x times and then give up. + * + * You'll run into this problem mostly if the name server you + * need for resolving the address is on the same machine, but + * is started after syslogd. + */ + case F_FORW_UNKN: + dprintf(" %s\n", f->f_un.f_forw.f_hname); + fwd_suspend = time((time_t *) 0) - f->f_time; + if ( fwd_suspend >= INET_SUSPEND_TIME ) { + dprintf("Forwarding suspension to unknown over, retrying\n"); + if ( (hp = gethostbyname(f->f_un.f_forw.f_hname)) == NULL ) { + dprintf("Failure: %s\n", sys_h_errlist[h_errno]); + dprintf("Retries: %d\n", f->f_prevcount); + if ( --f->f_prevcount < 0 ) { + dprintf("Giving up.\n"); + f->f_type = F_UNUSED; + } + else + dprintf("Left retries: %d\n", f->f_prevcount); + } + else { + dprintf("%s found, resuming.\n", f->f_un.f_forw.f_hname); + memcpy((char *) &f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length); + f->f_prevcount = 0; + f->f_type = F_FORW; + goto f_forw; + } + } + else + dprintf("Forwarding suspension not over, time " \ + "left: %d\n", INET_SUSPEND_TIME - fwd_suspend); + break; + + case F_FORW: + /* + * Don't send any message to a remote host if it + * already comes from one. (we don't care 'bout who + * sent the message, we don't send it anyway) -Joey + */ + f_forw: + dprintf(" %s\n", f->f_un.f_forw.f_hname); + if ( strcmp(from, LocalHostName) && NoHops ) + dprintf("Not sending message to remote.\n"); + else { + f->f_time = now; + (void) snprintf(line, sizeof(line), "<%d>%s\n", f->f_prevpri, \ + (char *) iov[4].iov_base); + l = strlen(line); + 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; + dprintf("INET sendto error: %d = %s.\n", + e, strerror(e)); + f->f_type = F_FORW_SUSP; + errno = e; + logerror("sendto"); + } + } + break; +#endif + + case F_CONSOLE: + f->f_time = now; +#ifdef UNIXPC + if (1) { +#else + if (flags & IGN_CONS) { +#endif + dprintf(" (ignored).\n"); + break; + } + /* FALLTHROUGH */ + + case F_TTY: + case F_FILE: + case F_PIPE: + f->f_time = now; + dprintf(" %s\n", f->f_un.f_fname); + if (f->f_type == F_TTY || f->f_type == F_CONSOLE) { + v->iov_base = "\r\n"; + v->iov_len = 2; + } else { + v->iov_base = "\n"; + v->iov_len = 1; + } + again: + /* f->f_file == -1 is an indicator that the we couldn't + open the file at startup. */ + if (f->f_file == -1) + break; + + if (writev(f->f_file, iov, 6) < 0) { + int e = errno; + + /* If a named pipe is full, just ignore it for now + - mrn 24 May 96 */ + if (f->f_type == F_PIPE && e == EAGAIN) + break; + + (void) close(f->f_file); + /* + * Check for EBADF on TTY's due to vhangup() XXX + * Linux uses EIO instead (mrn 12 May 96) + */ + if ((f->f_type == F_TTY || f->f_type == F_CONSOLE) +#ifdef linux + && e == EIO) { +#else + && e == EBADF) { +#endif + f->f_file = open(f->f_un.f_fname, O_WRONLY|O_APPEND|O_NOCTTY); + if (f->f_file < 0) { + f->f_type = F_UNUSED; + logerror(f->f_un.f_fname); + } else { + untty(); + goto again; + } + } else { + f->f_type = F_UNUSED; + errno = e; + logerror(f->f_un.f_fname); + } + } else if (f->f_flags & SYNC_FILE) + (void) fsync(f->f_file); + break; + + case F_USERS: + case F_WALL: + f->f_time = now; + dprintf("\n"); + v->iov_base = "\r\n"; + v->iov_len = 2; + wallmsg(f, iov); + break; + } /* switch */ + if (f->f_type != F_FORW_UNKN) + f->f_prevcount = 0; + return; +} +#if FALSE +}} /* balance parentheses for emacs */ +#endif + +jmp_buf ttybuf; + +void endtty() +{ + longjmp(ttybuf, 1); +} + +/* + * 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) + register struct filed *f; + struct iovec *iov; +{ + char p[6 + UNAMESZ]; + register int i; + int ttyf, len; + static int reenter = 0; + struct utmp ut; + struct utmp *uptr; + char greetings[200]; + + if (reenter++) + return; + + /* open the user login file */ + setutent(); + + + /* + * Might as well fork instead of using nonblocking I/O + * and doing notty(). + */ + if (fork() == 0) { + (void) signal(SIGTERM, SIG_DFL); + (void) alarm(0); + (void) signal(SIGALRM, endtty); +#ifndef SYSV + (void) signal(SIGTTOU, SIG_IGN); + (void) sigsetmask(0); +#endif + (void) snprintf(greetings, sizeof(greetings), + "\r\n\7Message from syslogd@%s at %.24s ...\r\n", + (char *) iov[2].iov_base, ctime(&now)); + len = strlen(greetings); + + /* scan the user login file */ + while ((uptr = getutent())) { + memcpy(&ut, uptr, sizeof(ut)); + /* is this slot used? */ + if (ut.ut_name[0] == '\0') + continue; + if (ut.ut_type == LOGIN_PROCESS) + continue; + if (!(strcmp (ut.ut_name,"LOGIN"))) /* paranoia */ + continue; + + /* 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]) { + i = MAXUNAMES; + break; + } + if (strncmp(f->f_un.f_uname[i], + ut.ut_name, UNAMESZ) == 0) + break; + } + if (i >= MAXUNAMES) + continue; + } + + /* compute the device name */ + strcpy(p, _PATH_DEV); + strncat(p, ut.ut_line, UNAMESZ); + + if (f->f_type == F_WALL) { + iov[0].iov_base = greetings; + iov[0].iov_len = len; + iov[1].iov_len = 0; + } + if (setjmp(ttybuf) == 0) { + (void) alarm(15); + /* open the terminal */ + ttyf = open(p, O_WRONLY|O_NOCTTY); + if (ttyf >= 0) { + struct stat statb; + + if (fstat(ttyf, &statb) == 0 && + (statb.st_mode & S_IWRITE)) + (void) writev(ttyf, iov, 6); + close(ttyf); + ttyf = -1; + } + } + (void) alarm(0); + } + exit(0); + } + /* close the user login file */ + endutent(); + reenter = 0; +} + +void reapchild() +{ + int saved_errno = errno; +#if defined(SYSV) && !defined(linux) + (void) signal(SIGCHLD, reapchild); /* reset signal handler -ASP */ + wait ((int *)0); +#else + union wait status; + + while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0) + ; +#endif +#ifdef linux + (void) signal(SIGCHLD, reapchild); /* reset signal handler -ASP */ +#endif + errno = saved_errno; +} + +/* + * Return a printable representation of a host address. + */ +const char *cvthname(f) + struct sockaddr_in *f; +{ + struct hostent *hp; + register char *p; + int count; + + 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)); + } + /* + * Convert to lower case, just like LocalDomain above + */ + for (p = (char *)hp->h_name; *p ; p++) + if (isupper(*p)) + *p = tolower(*p); + + /* + * Notice that the string still contains the fqdn, but your + * hostname and domain are separated by a '\0'. + */ + if ((p = strchr(hp->h_name, '.'))) { + if (strcmp(p + 1, LocalDomain) == 0) { + *p = '\0'; + return (hp->h_name); + } else { + if (StripDomains) { + count=0; + while (StripDomains[count]) { + if (strcmp(p + 1, StripDomains[count]) == 0) { + *p = '\0'; + return (hp->h_name); + } + count++; + } + } + if (LocalHosts) { + count=0; + while (LocalHosts[count]) { + if (!strcmp(hp->h_name, LocalHosts[count])) { + *p = '\0'; + return (hp->h_name); + } + count++; + } + } + } + } + + return (hp->h_name); +} + +void domark() +{ + register struct filed *f; +#ifdef SYSV + int lognum; +#endif + + if (MarkInterval > 0) { + now = time(0); + MarkSeq += TIMERINTVL; + if (MarkSeq >= MarkInterval) { + logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK); + MarkSeq = 0; + } + +#ifdef SYSV + for (lognum = 0; lognum <= nlogs; lognum++) { + f = &Files[lognum]; +#else + for (f = Files; f; f = f->f_next) { +#endif + 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, LocalHostName, 0, (char *)NULL); + BACKOFF(f); + } + } + } + (void) signal(SIGALRM, domark); + (void) alarm(TIMERINTVL); +} + +void debug_switch() + +{ + dprintf("Switching debugging_on to %s\n", (debugging_on == 0) ? "true" : "false"); + debugging_on = (debugging_on == 0) ? 1 : 0; + signal(SIGUSR1, debug_switch); +} + + +/* + * Print syslogd errors some place. + */ +void logerror(type) + char *type; +{ + char buf[100]; + + dprintf("Called logerr, msg: %s\n", type); + + if (errno == 0) + (void) snprintf(buf, sizeof(buf), "syslogd: %s", type); + else + (void) snprintf(buf, sizeof(buf), "syslogd: %s: %s", type, strerror(errno)); + errno = 0; + logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE); + return; +} + +void die(sig) + + int sig; + +{ + register struct filed *f; + char buf[100]; + int lognum; + int i; + int was_initialized = Initialized; + + Initialized = 0; /* Don't log SIGCHLDs in case we + receive one during exiting */ + + for (lognum = 0; lognum <= nlogs; lognum++) { + f = &Files[lognum]; + /* flush any pending output */ + if (f->f_prevcount) + fprintlog(f, LocalHostName, 0, (char *)NULL); + } + + Initialized = was_initialized; + if (sig) { + dprintf("syslogd: exiting on signal %d\n", sig); + (void) snprintf(buf, sizeof(buf), "exiting on signal %d", sig); + errno = 0; + logmsg(LOG_SYSLOG|LOG_INFO, buf, LocalHostName, ADDDATE); + } + + /* Close the UNIX sockets. */ + for (i = 0; i < nfunix; i++) + if (funix[i] != -1) + close(funix[i]); + /* Close the inet socket. */ + if (InetInuse) close(inetm); + + /* Clean-up files. */ + for (i = 0; i < nfunix; i++) + if (funixn[i] && funix[i] != -1) + (void)unlink(funixn[i]); +#ifndef TESTING + (void) remove_pid(PidFile); +#endif + exit(0); +} + +/* + * Signal handler to terminate the parent process. + */ +#ifndef TESTING +void doexit(sig) + int sig; +{ + exit (0); +} +#endif + +/* + * INIT -- Initialize syslogd from configuration table + */ + +void init() +{ + register int i, lognum; + register FILE *cf; + register struct filed *f; +#ifndef TESTING +#ifndef SYSV + register struct filed **nextp = (struct filed **) 0; +#endif +#endif + register char *p; + register unsigned int Forwarding = 0; +#ifdef CONT_LINE + char cbuf[BUFSIZ]; + char *cline; +#else + char cline[BUFSIZ]; +#endif + struct servent *sp; + + sp = getservbyname("syslog", "udp"); + if (sp == NULL) { + errno = 0; + logerror("network logging disabled (syslog/udp service unknown)."); + logerror("see syslogd(8) for details of whether and how to enable it."); + return; + } + LogPort = sp->s_port; + + /* + * Close all open log files and free log descriptor array. + */ + dprintf("Called init.\n"); + Initialized = 0; + if ( nlogs > -1 ) + { + dprintf("Initializing log structures.\n"); + + for (lognum = 0; lognum <= nlogs; lognum++ ) { + f = &Files[lognum]; + + /* flush any pending output */ + if (f->f_prevcount) + fprintlog(f, LocalHostName, 0, (char *)NULL); + + switch (f->f_type) { + case F_FILE: + case F_PIPE: + case F_TTY: + case F_CONSOLE: + (void) close(f->f_file); + break; + } + } + + /* + * This is needed especially when HUPing syslogd as the + * structure would grow infinitively. -Joey + */ + nlogs = -1; + free((void *) Files); + Files = (struct filed *) 0; + } + + +#ifdef SYSV + lognum = 0; +#else + f = NULL; +#endif + + /* open the configuration file */ + if ((cf = fopen(ConfFile, "r")) == NULL) { + dprintf("cannot open %s.\n", ConfFile); +#ifdef SYSV + allocate_log(); + f = &Files[lognum++]; +#ifndef TESTING + cfline("*.err\t" _PATH_CONSOLE, f); +#else + snprintf(cbuf,sizeof(cbuf), "*.*\t%s", ttyname(0)); + cfline(cbuf, f); +#endif +#else + *nextp = (struct filed *)calloc(1, sizeof(*f)); + cfline("*.ERR\t" _PATH_CONSOLE, *nextp); + (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f)) /* ASP */ + cfline("*.PANIC\t*", (*nextp)->f_next); +#endif + Initialized = 1; + return; + } + + /* + * Foreach line in the conf table, open that file. + */ +#if CONT_LINE + cline = cbuf; + while (fgets(cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) { +#else + while (fgets(cline, sizeof(cline), cf) != NULL) { +#endif + /* + * check for end-of-section, comments, strip off trailing + * spaces and newline character. + */ + for (p = cline; isspace(*p); ++p); + if (*p == '\0' || *p == '#') + continue; +#if CONT_LINE + strcpy(cline, p); +#endif + for (p = strchr(cline, '\0'); isspace(*--p);); +#if CONT_LINE + if (*p == '\\') { + if ((p - cbuf) > BUFSIZ - 30) { + /* Oops the buffer is full - what now? */ + cline = cbuf; + } else { + *p = 0; + cline = p; + continue; + } + } else + cline = cbuf; +#endif + *++p = '\0'; +#ifndef SYSV + f = (struct filed *)calloc(1, sizeof(*f)); + *nextp = f; + nextp = &f->f_next; +#endif + allocate_log(); + f = &Files[lognum++]; +#if CONT_LINE + cfline(cbuf, f); +#else + cfline(cline, f); +#endif + if (f->f_type == F_FORW || f->f_type == F_FORW_SUSP || f->f_type == F_FORW_UNKN) { + Forwarding++; + } + } + + /* close the configuration file */ + (void) fclose(cf); + +#ifdef SYSLOG_UNIXAF + for (i = 0; i < nfunix; i++) { + if (funix[i] != -1) + /* Don't close the socket, preserve it instead + close(funix[i]); + */ + continue; + if ((funix[i] = create_unix_socket(funixn[i])) != -1) + dprintf("Opened UNIX socket `%s'.\n", funixn[i]); + } +#endif + +#ifdef SYSLOG_INET + if (Forwarding || AcceptRemote) { + if (finet < 0) { + finet = create_inet_socket(); + if (finet >= 0) { + InetInuse = 1; + dprintf("Opened syslog UDP port.\n"); + } + } + } + else { + if (finet >= 0) + close(finet); + finet = -1; + InetInuse = 0; + } + inetm = finet; +#endif + + Initialized = 1; + + if ( Debug ) { +#ifdef SYSV + for (lognum = 0; lognum <= nlogs; lognum++) { + f = &Files[lognum]; + if (f->f_type != F_UNUSED) { + printf ("%2d: ", lognum); +#else + for (f = Files; f; f = f->f_next) { + if (f->f_type != F_UNUSED) { +#endif + for (i = 0; i <= LOG_NFACILITIES; i++) + if (f->f_pmask[i] == TABLE_NOPRI) + printf(" X "); + else + printf("%2X ", f->f_pmask[i]); + printf("%s: ", TypeNames[f->f_type]); + switch (f->f_type) { + case F_FILE: + case F_PIPE: + case F_TTY: + case F_CONSOLE: + printf("%s", f->f_un.f_fname); + if (f->f_file == -1) + printf(" (unused)"); + break; + + case F_FORW: + case F_FORW_SUSP: + case F_FORW_UNKN: + 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"); + } + } + } + + if ( AcceptRemote ) +#ifdef DEBRELEASE + logmsg(LOG_SYSLOG|LOG_INFO, "syslogd " VERSION "." PATCHLEVEL "#" DEBRELEASE \ + ": restart (remote reception)." , LocalHostName, \ + ADDDATE); +#else + logmsg(LOG_SYSLOG|LOG_INFO, "syslogd " VERSION "." PATCHLEVEL \ + ": restart (remote reception)." , LocalHostName, \ + ADDDATE); +#endif + else +#ifdef DEBRELEASE + logmsg(LOG_SYSLOG|LOG_INFO, "syslogd " VERSION "." PATCHLEVEL "#" DEBRELEASE \ + ": restart." , LocalHostName, ADDDATE); +#else + logmsg(LOG_SYSLOG|LOG_INFO, "syslogd " VERSION "." PATCHLEVEL \ + ": restart." , LocalHostName, ADDDATE); +#endif + (void) signal(SIGHUP, sighup_handler); + dprintf("syslogd: restarted.\n"); +} +#if FALSE + }}} /* balance parentheses for emacs */ +#endif + +/* + * Crack a configuration file line + */ + +void cfline(line, f) + char *line; + register struct filed *f; +{ + register char *p; + register char *q; + register int i, i2; + char *bp; + int pri; + int singlpri = 0; + int ignorepri = 0; + int syncfile; +#ifdef SYSLOG_INET + struct hostent *hp; +#endif + char buf[MAXLINE]; + char xbuf[200]; + + dprintf("cfline(%s)\n", line); + + errno = 0; /* keep strerror() stuff out of logerror messages */ + + /* clear out file entry */ +#ifndef SYSV + memset((char *) f, 0, sizeof(*f)); +#endif + for (i = 0; i <= LOG_NFACILITIES; i++) { + f->f_pmask[i] = TABLE_NOPRI; + f->f_flags = 0; + } + + /* scan through the list of selectors */ + for (p = line; *p && *p != '\t' && *p != ' ';) { + + /* 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 == '!' ) { + ignorepri = 1; + for (bp=buf; *(bp+1); bp++) + *bp=*(bp+1); + *bp='\0'; + } + else { + ignorepri = 0; + } + if ( *buf == '=' ) + { + singlpri = 1; + pri = decode(&buf[1], PriNames); + } + else { + singlpri = 0; + pri = decode(buf, PriNames); + } + + if (pri < 0) { + (void) snprintf(xbuf, sizeof(xbuf), "unknown priority name \"%s\"", buf); + logerror(xbuf); + 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++) { + if ( pri == INTERNAL_NOPRI ) { + if ( ignorepri ) + f->f_pmask[i] = TABLE_ALLPRI; + else + f->f_pmask[i] = TABLE_NOPRI; + } + else if ( singlpri ) { + if ( ignorepri ) + f->f_pmask[i] &= ~(1<<pri); + else + f->f_pmask[i] |= (1<<pri); + } + else + { + if ( pri == TABLE_ALLPRI ) { + if ( ignorepri ) + f->f_pmask[i] = TABLE_NOPRI; + else + f->f_pmask[i] = TABLE_ALLPRI; + } + else + { + if ( ignorepri ) + for (i2= 0; i2 <= pri; ++i2) + f->f_pmask[i] &= ~(1<<i2); + else + for (i2= 0; i2 <= pri; ++i2) + f->f_pmask[i] |= (1<<i2); + } + } + } + } else { + i = decode(buf, FacNames); + if (i < 0) { + + (void) snprintf(xbuf, sizeof(xbuf), "unknown facility name \"%s\"", buf); + logerror(xbuf); + return; + } + + if ( pri == INTERNAL_NOPRI ) { + if ( ignorepri ) + f->f_pmask[i >> 3] = TABLE_ALLPRI; + else + f->f_pmask[i >> 3] = TABLE_NOPRI; + } else if ( singlpri ) { + if ( ignorepri ) + f->f_pmask[i >> 3] &= ~(1<<pri); + else + f->f_pmask[i >> 3] |= (1<<pri); + } else { + if ( pri == TABLE_ALLPRI ) { + if ( ignorepri ) + f->f_pmask[i >> 3] = TABLE_NOPRI; + else + f->f_pmask[i >> 3] = TABLE_ALLPRI; + } else { + if ( ignorepri ) + for (i2= 0; i2 <= pri; ++i2) + f->f_pmask[i >> 3] &= ~(1<<i2); + else + for (i2= 0; i2 <= pri; ++i2) + f->f_pmask[i >> 3] |= (1<<i2); + } + } + } + while (*p == ',' || *p == ' ') + p++; + } + + p = q; + } + + /* skip to action part */ + while (*p == '\t' || *p == ' ') + p++; + + if (*p == '-') + { + syncfile = 0; + p++; + } else + syncfile = 1; + + dprintf("leading char in action: %c\n", *p); + switch (*p) + { + case '@': +#ifdef SYSLOG_INET + (void) strcpy(f->f_un.f_forw.f_hname, ++p); + dprintf("forwarding host: %s\n", p); /*ASP*/ + if ( (hp = gethostbyname(p)) == NULL ) { + f->f_type = F_FORW_UNKN; + f->f_prevcount = INET_RETRY_MAX; + f->f_time = time ( (time_t *)0 ); + } else { + f->f_type = F_FORW; + } + + memset((char *) &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; + if ( f->f_type == F_FORW ) + memcpy((char *) &f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length); + /* + * Otherwise the host might be unknown due to an + * inaccessible nameserver (perhaps on the same + * host). We try to get the ip number later, like + * FORW_SUSP. + */ +#endif + break; + + case '|': + case '/': + (void) strcpy(f->f_un.f_fname, p); + dprintf ("filename: %s\n", p); /*ASP*/ + if (syncfile) + f->f_flags |= SYNC_FILE; + if ( *p == '|' ) { + f->f_file = open(++p, O_RDWR|O_NONBLOCK); + f->f_type = F_PIPE; + } else { + f->f_file = open(p, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + 0644); + f->f_type = F_FILE; + } + + if ( f->f_file < 0 ){ + f->f_file = -1; + dprintf("Error opening log file: %s\n", p); + logerror(p); + break; + } + if (isatty(f->f_file)) { + f->f_type = F_TTY; + untty(); + } + if (strcmp(p, ctty) == 0) + f->f_type = F_CONSOLE; + break; + + case '*': + dprintf ("write-all\n"); + f->f_type = F_WALL; + break; + + default: + dprintf ("users: %s\n", p); /* ASP */ + for (i = 0; i < MAXUNAMES && *p; i++) { + for (q = p; *q && *q != ','; ) + q++; + (void) strncpy(f->f_un.f_uname[i], p, UNAMESZ); + if ((q - p) > UNAMESZ) + f->f_un.f_uname[i][UNAMESZ] = '\0'; + else + f->f_un.f_uname[i][q - p] = '\0'; + while (*q == ',' || *q == ' ') + q++; + p = q; + } + f->f_type = F_USERS; + break; + } + return; +} + + +/* + * Decode a symbolic name to a numeric value + */ + +int decode(name, codetab) + char *name; + struct code *codetab; +{ + register struct code *c; + register char *p; + char buf[80]; + + dprintf ("symbolic name: %s", name); + if (isdigit(*name)) + { + dprintf ("\n"); + return (atoi(name)); + } + (void) strncpy(buf, name, 79); + for (p = buf; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + for (c = codetab; c->c_name; c++) + if (!strcmp(buf, c->c_name)) + { + dprintf (" ==> %d\n", c->c_val); + return (c->c_val); + } + return (-1); +} + +static void dprintf(char *fmt, ...) + +{ + va_list ap; + + if ( !(Debug && debugging_on) ) + return; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + + fflush(stdout); + return; +} + + +/* + * The following function is responsible for allocating/reallocating the + * array which holds the structures which define the logging outputs. + */ +static void allocate_log() + +{ + dprintf("Called allocate_log, nlogs = %d.\n", nlogs); + + /* + * Decide whether the array needs to be initialized or needs to + * grow. + */ + if ( nlogs == -1 ) + { + Files = (struct filed *) malloc(sizeof(struct filed)); + if ( Files == (void *) 0 ) + { + dprintf("Cannot initialize log structure."); + logerror("Cannot initialize log structure."); + return; + } + } + else + { + /* Re-allocate the array. */ + Files = (struct filed *) realloc(Files, (nlogs+2) * \ + sizeof(struct filed)); + if ( Files == (struct filed *) 0 ) + { + dprintf("Cannot grow log structure."); + logerror("Cannot grow log structure."); + return; + } + } + + /* + * Initialize the array element, bump the number of elements in the + * the array and return. + */ + ++nlogs; + memset(&Files[nlogs], '\0', sizeof(struct filed)); + return; +} + + +/* + * The following function is resposible for handling a SIGHUP signal. Since + * we are now doing mallocs/free as part of init we had better not being + * doing this during a signal handler. Instead this function simply sets + * a flag variable which will tell the main loop to go through a restart. + */ +void sighup_handler() + +{ + restart = 1; + signal(SIGHUP, sighup_handler); + return; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ + |