From 05f2f1839c2712ca77e86aa679dc909d051fc23b Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 8 Nov 2004 13:52:36 +0000 Subject: Initial revision --- klogd.c | 1191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1191 insertions(+) create mode 100644 klogd.c (limited to 'klogd.c') diff --git a/klogd.c b/klogd.c new file mode 100644 index 00000000..2c8e765e --- /dev/null +++ b/klogd.c @@ -0,0 +1,1191 @@ +/* + klogd.c - main program for Linux kernel log daemon. + Copyright (c) 1995 Dr. G.W. Wettstein + + This file is part of the sysklogd package, a kernel and system log daemon. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Steve Lord (lord@cray.com) 7th Nov 92 + * + * Modified to check for kernel info by Dr. G.W. Wettstein 02/17/93. + * + * Fri Mar 12 16:53:56 CST 1993: Dr. Wettstein + * Modified LogLine to use a newline as the line separator in + * the kernel message buffer. + * + * Added debugging code to dump the contents of the kernel message + * buffer at the start of the LogLine function. + * + * Thu Jul 29 11:40:32 CDT 1993: Dr. Wettstein + * Added syscalls to turn off logging of kernel messages to the + * console when klogd becomes responsible for kernel messages. + * + * klogd now catches SIGTERM and SIGKILL signals. Receipt of these + * signals cases the clean_up function to be called which shuts down + * kernel logging and re-enables logging of messages to the console. + * + * Sat Dec 11 11:54:22 CST 1993: Dr. Wettstein + * Added fixes to allow compilation with no complaints with -Wall. + * + * When the daemon catches a fatal signal (SIGTERM, SIGKILL) a + * message is output to the logfile advising that the daemon is + * going to terminate. + * + * Thu Jan 6 11:54:10 CST 1994: Dr. Wettstein + * Major re-write/re-organization of the code. + * + * Klogd now assigns kernel messages to priority levels when output + * to the syslog facility is requested. The priority level is + * determined by decoding the prioritization sequence which is + * tagged onto the start of the kernel messages. + * + * Added the following program options: -f arg -c arg -s -o -d + * + * The -f switch can be used to specify that output should + * be written to the named file. + * + * The -c switch is used to specify the level of kernel + * messages which are to be directed to the console. + * + * The -s switch causes the program to use the syscall + * interface to the kernel message facility. This can be + * used to override the presence of the /proc filesystem. + * + * The -o switch causes the program to operate in 'one-shot' + * mode. A single call will be made to read the complete + * kernel buffer. The contents of the buffer will be + * output and the program will terminate. + * + * The -d switch causes 'debug' mode to be activated. This + * will cause the daemon to generate LOTS of output to stderr. + * + * The buffer decomposition function (LogLine) was re-written to + * squash a bug which was causing only partial kernel messages to + * be written to the syslog facility. + * + * The signal handling code was modified to properly differentiate + * between the STOP and TSTP signals. + * + * Added pid saving when the daemon detaches into the background. Thank + * you to Juha Virtanen (jiivee@hut.fi) for providing this patch. + * + * Mon Feb 6 07:31:29 CST 1995: Dr. Wettstein + * Significant re-organization of the signal handling code. The + * signal handlers now only set variables. Not earth shaking by any + * means but aesthetically pleasing to the code purists in the group. + * + * Patch to make things more compliant with the file system standards. + * Thanks to Chris Metcalf for prompting this helpful change. + * + * The routines responsible for reading the kernel log sources now + * initialize the buffers before reading. I think that this will + * solve problems with non-terminated kernel messages producing + * output of the form: new old old old + * + * This may also help influence the occassional reports of klogd + * failing under significant load. I think that the jury may still + * be out on this one though. My thanks to Joerg Ahrens for initially + * tipping me off to the source of this problem. Also thanks to + * Michael O'Reilly for tipping me off to the best fix for this problem. + * And last but not least Mark Lord for prompting me to try this as + * a means of attacking the stability problem. + * + * Specifying a - as the arguement to the -f switch will cause output + * to be directed to stdout rather than a filename of -. Thanks to + * Randy Appleton for a patch which prompted me to do this. + * + * Wed Feb 22 15:37:37 CST 1995: Dr. Wettstein + * Added version information to logging startup messages. + * + * Wed Jul 26 18:57:23 MET DST 1995: Martin Schulze + * Added an commandline argument "-n" to avoid forking. This obsoletes + * the compiler define NO_FORK. It's more useful to have this as an + * argument as there are many binary versions and one doesn't need to + * recompile the daemon. + * + * 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. + * + * Fri Nov 17 15:05:43 CST 1995: Dr. Wettstein + * Added support for kernel address translation. This required moving + * some definitions and includes to the new klogd.h file. Some small + * code cleanups and modifications. + * + * Mon Nov 20 10:03:39 MET 1995 + * Added -v option to print the version and exit. + * + * Thu Jan 18 11:19:46 CST 1996: Dr. Wettstein + * Added suggested patches from beta-testers. These address two + * two problems. The first is segmentation faults which occur with + * the ELF libraries. This was caused by passing a null pointer to + * the strcmp function. + * + * Added a second patch to remove the pidfile as part of the + * termination cleanup sequence. This minimizes the potential for + * conflicting pidfiles causing immediate termination at boot time. + * + * Wed Aug 21 09:13:03 CDT 1996: Dr. Wettstein + * Added ability to reload static symbols and kernel module symbols + * under control of SIGUSR1 and SIGUSR2 signals. + * + * Added -p switch to select 'paranoid' behavior with respect to the + * loading of kernel module symbols. + * + * Informative line now printed whenever a state change occurs due + * to signal reception by the daemon. + * + * Added the -i and -I command line switches to signal the currently + * executing daemon. + * + * Tue Nov 19 10:15:36 PST 1996: Leland Olds + * Corrected vulnerability to buffer overruns by rewriting LogLine + * routine. Obscenely long kernel messages will now be broken up + * into lines no longer than LOG_LINE_LENGTH. + * + * The last version of LogLine was vulnerable to buffer overruns: + * - Kernel messages longer than LOG_LINE_LENGTH caused a buffer + * overrun. + * - If a line was determined to be shorter than LOG_LINE_LENGTH, + * the routine "ExpandKadds" could cause the line grow by + * an unknown amount and overrun a buffer. + * I turned these routines into a little parsing state machine that + * should not have these problems. + * + * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman + * Some more glibc patches made by . + * + * Thu Aug 21 12:11:27 MET DST 1997: Martin Schulze + * Fixed little mistake which prevented klogd from accepting a + * console log + * + * Fri Jan 9 00:39:52 CET 1998: Martin Schulze + * Changed the behaviour of klogd when receiving a terminate + * signal. Now the program terminates immediately instead of + * completing the receipt of a kernel message, i.e the read() + * call. The old behaveiour could result in klogd being + * recognized as being undead, because it'll only die after a + * message has been received. + * + * Fri Jan 9 11:03:48 CET 1998: Martin Schulze + * Corrected some code that caused klogd to dump core when + * receiving messages containing '%', some of them exist in + * 2.1.78. Thanks to Chu-yeon Park for + * informing me. + * + * Fri Jan 9 23:38:19 CET 1998: Florian La Roche + * Added -x switch to omit EIP translation and System.map evaluation. + * + * Sun Jan 25 20:47:46 CET 1998: Martin Schulze + * As the bug covering the %'s introduced a problem with + * unevaluated priorities I've worked out a real fix that strips + * %'s to an even number which is harmless for printf. + * + * Sat Oct 10 20:01:48 CEST 1998: Martin Schulze + * Added support for TESTING define which will turn klogd into + * stdio-mode used for debugging. + * + * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze + * Modified System.map read function to try all possible map + * files until a file with matching version is found. Added support for + * Debian release. + * + * Mon Oct 12 13:01:27 MET DST 1998: Martin Schulze + * Used unsigned long and strtoul() to resolve kernel oops symbols. + * + * Sun Jan 3 18:38:03 CET 1999: Martin Schulze + * Shortened LOG_LINE_LENGTH in order to get long lines splitted + * up earlier and syslogd has a better chance concatenating them + * together again. + * + * Sat Aug 21 12:27:02 CEST 1999: Martin Schulze + * Skip newline when reading in messages. + * + * Tue Sep 12 22:14:33 CEST 2000: Martin Schulze + * Don't feed a buffer directly to a printf-type routine, use + * "%s" as format string instead. Thanks to Jouko Pynnönen + * for pointing this out. + * + * Tue Sep 12 22:44:57 CEST 2000: Martin Schulze + * Commandline option `-2': When symbols are expanded, print the + * line twice. Once with addresses converted to symbols, once with the + * raw text. Allows external programs such as ksymoops do their own + * processing on the original data. Thanks to Keith Owens + * for the patch. + * + * Mon Sep 18 09:32:27 CEST 2000: Martin Schulze + * Added patch to fix priority decoding after moving kernel + * messgages into "%s". Thanks to Solar Designer + * for the patch. + * + * Sun Mar 11 20:23:44 CET 2001: Martin Schulze + * Stop LogLine() from being called with wrong argument when a + * former calculation failed already. Thanks to Thomas Roessler + * for providing a patch. + * + * Ignore zero bytes, no busy loop is entered anymore. Several + * people have submitted patches: Troels Walsted Hansen + * , Wolfgang Oertl + * and Thomas Roessler. + */ + + +/* Includes. */ +#include +#include +#include +#include +#include +#if !defined(__GLIBC__) +#include +#endif /* __GLIBC__ */ +#include +#include +#include +#include "klogd.h" +#include "ksyms.h" +#ifndef TESTING +#include "pidfile.h" +#endif +#include "version.h" + +#define __LIBRARY__ +#include +#if !defined(__GLIBC__) +# define __NR_ksyslog __NR_syslog +_syscall3(int,ksyslog,int, type, char *, buf, int, len); +#else +#include +#define ksyslog klogctl +#endif + +#define LOG_BUFFER_SIZE 4096 +#define LOG_LINE_LENGTH 1000 + +#ifndef TESTING +#if defined(FSSTND) +static char *PidFile = _PATH_VARRUN "klogd.pid"; +#else +static char *PidFile = "/etc/klogd.pid"; +#endif +#endif + +static int kmsg, + change_state = 0, + terminate = 0, + caught_TSTP = 0, + reload_symbols = 0, + console_log_level = -1; + +static int use_syscall = 0, + one_shot = 0, + symbol_lookup = 1, + no_fork = 0; /* don't fork - don't run in daemon mode */ + +static char *symfile = (char *) 0, + log_buffer[LOG_BUFFER_SIZE]; + +static FILE *output_file = (FILE *) 0; + +static enum LOGSRC {none, proc, kernel} logsrc; + +int debugging = 0; +int symbols_twice = 0; + + +/* Function prototypes. */ +extern int ksyslog(int type, char *buf, int len); +static void CloseLogSrc(void); +extern void restart(int sig); +extern void stop_logging(int sig); +extern void stop_daemon(int sig); +extern void reload_daemon(int sig); +static void Terminate(void); +static void SignalDaemon(int); +static void ReloadSymbols(void); +static void ChangeLogging(void); +static enum LOGSRC GetKernelLogSrc(void); +static void LogLine(char *ptr, int len); +static void LogKernelLine(void); +static void LogProcLine(void); +extern int main(int argc, char *argv[]); + + +static void CloseLogSrc() + +{ + /* Turn on logging of messages to console. */ + ksyslog(7, NULL, 0); + + /* Shutdown the log sources. */ + switch ( logsrc ) + { + case kernel: + ksyslog(0, 0, 0); + Syslog(LOG_INFO, "Kernel logging (ksyslog) stopped."); + break; + case proc: + close(kmsg); + Syslog(LOG_INFO, "Kernel logging (proc) stopped."); + break; + case none: + break; + } + + if ( output_file != (FILE *) 0 ) + fflush(output_file); + return; +} + + +void restart(sig) + + int sig; + +{ + signal(SIGCONT, restart); + change_state = 1; + caught_TSTP = 0; + return; +} + + +void stop_logging(sig) + + int sig; + +{ + signal(SIGTSTP, stop_logging); + change_state = 1; + caught_TSTP = 1; + return; +} + + +void stop_daemon(sig) + + int sig; + +{ + Terminate(); + return; +} + + +void reload_daemon(sig) + + int sig; + +{ + change_state = 1; + reload_symbols = 1; + + + if ( sig == SIGUSR2 ) + { + ++reload_symbols; + signal(SIGUSR2, reload_daemon); + } + else + signal(SIGUSR1, reload_daemon); + + return; +} + + +static void Terminate() + +{ + CloseLogSrc(); + Syslog(LOG_INFO, "Kernel log daemon terminating."); + sleep(1); + if ( output_file != (FILE *) 0 ) + fclose(output_file); + closelog(); +#ifndef TESTING + (void) remove_pid(PidFile); +#endif + exit(1); +} + +static void SignalDaemon(sig) + + int sig; + +{ +#ifndef TESTING + auto int pid = check_pid(PidFile); + + kill(pid, sig); +#else + kill(getpid(), sig); +#endif + return; +} + + +static void ReloadSymbols() + +{ + if (symbol_lookup) { + if ( reload_symbols > 1 ) + InitKsyms(symfile); + InitMsyms(); + } + reload_symbols = change_state = 0; + return; +} + + +static void ChangeLogging(void) + +{ + /* Terminate kernel logging. */ + if ( terminate == 1 ) + Terminate(); + + /* Indicate that something is happening. */ + Syslog(LOG_INFO, "klogd %s.%s, ---------- state change ----------\n", \ + VERSION, PATCHLEVEL); + + /* Reload symbols. */ + if ( reload_symbols > 0 ) + { + ReloadSymbols(); + return; + } + + /* Stop kernel logging. */ + if ( caught_TSTP == 1 ) + { + CloseLogSrc(); + logsrc = none; + change_state = 0; + return; + } + + /* + * The rest of this function is responsible for restarting + * kernel logging after it was stopped. + * + * In the following section we make a decision based on the + * kernel log state as to what is causing us to restart. Somewhat + * groady but it keeps us from creating another static variable. + */ + if ( logsrc != none ) + { + Syslog(LOG_INFO, "Kernel logging re-started after SIGSTOP."); + change_state = 0; + return; + } + + /* Restart logging. */ + logsrc = GetKernelLogSrc(); + change_state = 0; + return; +} + + +static enum LOGSRC GetKernelLogSrc(void) + +{ + auto struct stat sb; + + + /* Set level of kernel console messaging.. */ + if ( (console_log_level != -1) + && (ksyslog(8, NULL, console_log_level) < 0) && \ + (errno == EINVAL) ) + { + /* + * An invalid arguement error probably indicates that + * a pre-0.14 kernel is being run. At this point we + * issue an error message and simply shut-off console + * logging completely. + */ + Syslog(LOG_WARNING, "Cannot set console log level - disabling " + "console output."); + ksyslog(6, NULL, 0); + } + + /* + * First do a stat to determine whether or not the proc based + * file system is available to get kernel messages from. + */ + if ( use_syscall || + ((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)) ) + { + /* Initialize kernel logging. */ + ksyslog(1, NULL, 0); +#ifdef DEBRELEASE + Syslog(LOG_INFO, "klogd %s.%s#%s, log source = ksyslog " + "started.", VERSION, PATCHLEVEL, DEBRELEASE); +#else + Syslog(LOG_INFO, "klogd %s.%s, log source = ksyslog " + "started.", VERSION, PATCHLEVEL); +#endif + return(kernel); + } + +#ifndef TESTING + if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 ) + { + fprintf(stderr, "klogd: Cannot open proc file system, " \ + "%d - %s.\n", errno, strerror(errno)); + ksyslog(7, NULL, 0); + exit(1); + } +#else + kmsg = fileno(stdin); +#endif + +#ifdef DEBRELEASE + Syslog(LOG_INFO, "klogd %s.%s#%s, log source = %s started.", \ + VERSION, PATCHLEVEL, DEBRELEASE, _PATH_KLOG); +#else + Syslog(LOG_INFO, "klogd %s.%s, log source = %s started.", \ + VERSION, PATCHLEVEL, _PATH_KLOG); +#endif + return(proc); +} + + +extern void Syslog(int priority, char *fmt, ...) + +{ + va_list ap; + char *argl; + + if ( debugging ) + { + fputs("Logging line:\n", stderr); + fprintf(stderr, "\tLine: %s\n", fmt); + fprintf(stderr, "\tPriority: %d\n", priority); + } + + /* Handle output to a file. */ + if ( output_file != (FILE *) 0 ) + { + va_start(ap, fmt); + vfprintf(output_file, fmt, ap); + va_end(ap); + fputc('\n', output_file); + fflush(output_file); + if (!one_shot) + fsync(fileno(output_file)); + return; + } + + /* Output using syslog. */ + if (!strcmp(fmt, "%s")) + { + va_start(ap, fmt); + argl = va_arg(ap, char *); + if (argl[0] == '<' && argl[1] && argl[2] == '>') + { + switch ( argl[1] ) + { + case '0': + priority = LOG_EMERG; + break; + case '1': + priority = LOG_ALERT; + break; + case '2': + priority = LOG_CRIT; + break; + case '3': + priority = LOG_ERR; + break; + case '4': + priority = LOG_WARNING; + break; + case '5': + priority = LOG_NOTICE; + break; + case '6': + priority = LOG_INFO; + break; + case '7': + default: + priority = LOG_DEBUG; + } + argl += 3; + } + syslog(priority, fmt, argl); + va_end(ap); +#ifdef TESTING + putchar('\n'); +#endif + return; + } + + va_start(ap, fmt); + vsyslog(priority, fmt, ap); + va_end(ap); +#ifdef TESTING + printf ("\n"); +#endif + + return; +} + + +/* + * Copy characters from ptr to line until a char in the delim + * string is encountered or until min( space, len ) chars have + * been copied. + * + * Returns the actual number of chars copied. + */ +static int copyin( char *line, int space, + const char *ptr, int len, + const char *delim ) +{ + auto int i; + auto int count; + + count = len < space ? len : space; + + for(i=0; i]", + * where "aaaaaa" is the address. These are replaced with + * "[symbolname+offset/size]" in the output line - symbolname, + * offset, and size come from the kernel symbol table. + * + * If a kernel symbol happens to fall at the end of a message close + * in length to LOG_LINE_LENGTH, the symbol will not be expanded. + * (This should never happen, since the kernel should never generate + * messages that long. + * + * To preserve the original addresses, lines containing kernel symbols + * are output twice. Once with the symbols converted and again with the + * original text. Just in case somebody wants to run their own Oops + * analysis on the syslog, e.g. ksymoops. + */ +static void LogLine(char *ptr, int len) +{ + enum parse_state_enum { + PARSING_TEXT, + PARSING_SYMSTART, /* at < */ + PARSING_SYMBOL, + PARSING_SYMEND /* at ] */ + }; + + static char line_buff[LOG_LINE_LENGTH]; + + static char *line =line_buff; + static enum parse_state_enum parse_state = PARSING_TEXT; + static int space = sizeof(line_buff)-1; + + static char *sym_start; /* points at the '<' of a symbol */ + + auto int delta = 0; /* number of chars copied */ + auto int symbols_expanded = 0; /* 1 if symbols were expanded */ + auto int skip_symbol_lookup = 0; /* skip symbol lookup on this pass */ + auto char *save_ptr = ptr; /* save start of input line */ + auto int save_len = len; /* save length at start of input line */ + + while( len > 0 ) + { + if( space == 0 ) /* line buffer is full */ + { + /* + ** Line too long. Start a new line. + */ + *line = 0; /* force null terminator */ + + if ( debugging ) + { + fputs("Line buffer full:\n", stderr); + fprintf(stderr, "\tLine: %s\n", line); + } + + Syslog( LOG_INFO, "%s", line_buff ); + line = line_buff; + space = sizeof(line_buff)-1; + parse_state = PARSING_TEXT; + symbols_expanded = 0; + skip_symbol_lookup = 0; + save_ptr = ptr; + save_len = len; + } + + switch( parse_state ) + { + case PARSING_TEXT: + delta = copyin( line, space, ptr, len, "\n[%" ); + line += delta; + ptr += delta; + space -= delta; + len -= delta; + + if( space == 0 || len == 0 ) + { + break; /* full line_buff or end of input buffer */ + } + + if( *ptr == '\0' ) /* zero byte */ + { + ptr++; /* skip zero byte */ + space -= 1; + len -= 1; + + break; + } + + if( *ptr == '\n' ) /* newline */ + { + ptr++; /* skip newline */ + space -= 1; + len -= 1; + + *line = 0; /* force null terminator */ + Syslog( LOG_INFO, "%s", line_buff ); + line = line_buff; + space = sizeof(line_buff)-1; + if (symbols_twice) { + if (symbols_expanded) { + /* reprint this line without symbol lookup */ + symbols_expanded = 0; + skip_symbol_lookup = 1; + ptr = save_ptr; + len = save_len; + } + else + { + skip_symbol_lookup = 0; + save_ptr = ptr; + save_len = len; + } + } + break; + } + if( *ptr == '[' ) /* possible kernel symbol */ + { + *line++ = *ptr++; + space -= 1; + len -= 1; + if (!skip_symbol_lookup) + parse_state = PARSING_SYMSTART; /* at < */ + break; + } + if( *ptr == '%' ) /* dangerous printf marker */ + { + delta = 0; + while (len && *ptr == '%') + { + *line++ = *ptr++; /* copy it in */ + space -= 1; + len -= 1; + delta++; + } + if (delta % 2) /* odd amount of %'s */ + { + if (space) + { + *line++ = '%'; /* so simply add one */ + space -= 1; + } + else + { + *line++ = '\0'; /* remove the last one / terminate the string */ + } + + } + } + break; + + case PARSING_SYMSTART: + if( *ptr != '<' ) + { + parse_state = PARSING_TEXT; /* not a symbol */ + break; + } + + /* + ** Save this character for now. If this turns out to + ** be a valid symbol, this char will be replaced later. + ** If not, we'll just leave it there. + */ + + sym_start = line; /* this will point at the '<' */ + + *line++ = *ptr++; + space -= 1; + len -= 1; + parse_state = PARSING_SYMBOL; /* symbol... */ + break; + + case PARSING_SYMBOL: + delta = copyin( line, space, ptr, len, ">\n[" ); + line += delta; + ptr += delta; + space -= delta; + len -= delta; + if( space == 0 || len == 0 ) + { + break; /* full line_buff or end of input buffer */ + } + if( *ptr != '>' ) + { + parse_state = PARSING_TEXT; + break; + } + + *line++ = *ptr++; /* copy the '>' */ + space -= 1; + len -= 1; + + parse_state = PARSING_SYMEND; + + break; + + case PARSING_SYMEND: + if( *ptr != ']' ) + { + parse_state = PARSING_TEXT; /* not a symbol */ + break; + } + + /* + ** It's really a symbol! Replace address with the + ** symbol text. + */ + { + auto int sym_space; + + unsigned long value; + auto struct symbol sym; + auto char *symbol; + + *(line-1) = 0; /* null terminate the address string */ + value = strtoul(sym_start+1, (char **) 0, 16); + *(line-1) = '>'; /* put back delim */ + + symbol = LookupSymbol(value, &sym); + if ( !symbol_lookup || symbol == (char *) 0 ) + { + parse_state = PARSING_TEXT; + break; + } + + /* + ** verify there is room in the line buffer + */ + sym_space = space + ( line - sym_start ); + if( sym_space < strlen(symbol) + 30 ) /*(30 should be overkill)*/ + { + parse_state = PARSING_TEXT; /* not enough space */ + break; + } + + delta = sprintf( sym_start, "%s+%d/%d]", + symbol, sym.offset, sym.size ); + + space = sym_space + delta; + line = sym_start + delta; + symbols_expanded = 1; + } + ptr++; + len--; + parse_state = PARSING_TEXT; + break; + + default: /* Can't get here! */ + parse_state = PARSING_TEXT; + + } + } + + return; +} + + +static void LogKernelLine(void) + +{ + auto int rdcnt; + + /* + * Zero-fill the log buffer. This should cure a multitude of + * problems with klogd logging the tail end of the message buffer + * which will contain old messages. Then read the kernel log + * messages into this fresh buffer. + */ + memset(log_buffer, '\0', sizeof(log_buffer)); + if ( (rdcnt = ksyslog(2, log_buffer, sizeof(log_buffer))) < 0 ) + { + if ( errno == EINTR ) + return; + fprintf(stderr, "klogd: Error return from sys_sycall: " \ + "%d - %s\n", errno, strerror(errno)); + } + else + LogLine(log_buffer, rdcnt); + return; +} + + +static void LogProcLine(void) + +{ + auto int rdcnt; + + /* + * Zero-fill the log buffer. This should cure a multitude of + * problems with klogd logging the tail end of the message buffer + * which will contain old messages. Then read the kernel messages + * from the message pseudo-file into this fresh buffer. + */ + memset(log_buffer, '\0', sizeof(log_buffer)); + if ( (rdcnt = read(kmsg, log_buffer, sizeof(log_buffer)-1)) < 0 ) + { + if ( errno == EINTR ) + return; + Syslog(LOG_ERR, "Cannot read proc file system: %d - %s.", \ + errno, strerror(errno)); + } + else + LogLine(log_buffer, rdcnt); + + return; +} + + +int main(argc, argv) + + int argc; + + char *argv[]; + +{ + auto int ch, + use_output = 0; + + auto char *log_level = (char *) 0, + *output = (char *) 0; + +#ifndef TESTING + chdir ("/"); +#endif + /* Parse the command-line. */ + while ((ch = getopt(argc, argv, "c:df:iIk:nopsvx2")) != EOF) + switch((char)ch) + { + case '2': /* Print lines with symbols twice. */ + symbols_twice = 1; + break; + case 'c': /* Set console message level. */ + log_level = optarg; + break; + case 'd': /* Activity debug mode. */ + debugging = 1; + break; + case 'f': /* Define an output file. */ + output = optarg; + use_output++; + break; + case 'i': /* Reload module symbols. */ + SignalDaemon(SIGUSR1); + return(0); + case 'I': + SignalDaemon(SIGUSR2); + return(0); + case 'k': /* Kernel symbol file. */ + symfile = optarg; + break; + case 'n': /* don't fork */ + no_fork++; + break; + case 'o': /* One-shot mode. */ + one_shot = 1; + break; + case 'p': + SetParanoiaLevel(1); /* Load symbols on oops. */ + break; + case 's': /* Use syscall interface. */ + use_syscall = 1; + break; + case 'v': + printf("klogd %s.%s\n", VERSION, PATCHLEVEL); + exit (1); + case 'x': + symbol_lookup = 0; + break; + } + + + /* Set console logging level. */ + if ( log_level != (char *) 0 ) + { + if ( (strlen(log_level) > 1) || \ + (strchr("12345678", *log_level) == (char *) 0) ) + { + fprintf(stderr, "klogd: Invalid console logging " + "level <%s> specified.\n", log_level); + return(1); + } + console_log_level = *log_level - '0'; + } + + +#ifndef TESTING + /* + * The following code allows klogd to auto-background itself. + * What happens is that the program forks and the parent quits. + * The child closes all its open file descriptors, and issues a + * call to setsid to establish itself as an independent session + * immune from control signals. + * + * fork() is only called if it should run in daemon mode, fork is + * not disabled with the command line argument and there's no + * such process running. + */ + if ( (!one_shot) && (!no_fork) ) + { + if (!check_pid(PidFile)) + { + if ( fork() == 0 ) + { + auto int fl; + int num_fds = getdtablesize(); + + /* This is the child closing its file descriptors. */ + for (fl= 0; fl <= num_fds; ++fl) + { + if ( fileno(stdout) == fl && use_output ) + if ( strcmp(output, "-") == 0 ) + continue; + close(fl); + } + + setsid(); + } + else + exit(0); + } + else + { + fputs("klogd: Already running.\n", stderr); + exit(1); + } + } + + + /* tuck my process id away */ + if (!check_pid(PidFile)) + { + if (!write_pid(PidFile)) + Terminate(); + } + else + { + fputs("klogd: Already running.\n", stderr); + Terminate(); + } +#endif + + /* Signal setups. */ + for (ch= 1; ch < NSIG; ++ch) + signal(ch, SIG_IGN); + signal(SIGINT, stop_daemon); + signal(SIGKILL, stop_daemon); + signal(SIGTERM, stop_daemon); + signal(SIGHUP, stop_daemon); + signal(SIGTSTP, stop_logging); + signal(SIGCONT, restart); + signal(SIGUSR1, reload_daemon); + signal(SIGUSR2, reload_daemon); + + + /* Open outputs. */ + if ( use_output ) + { + if ( strcmp(output, "-") == 0 ) + output_file = stdout; + else if ( (output_file = fopen(output, "w")) == (FILE *) 0 ) + { + fprintf(stderr, "klogd: Cannot open output file " \ + "%s - %s\n", output, strerror(errno)); + return(1); + } + } + else + openlog("kernel", 0, LOG_KERN); + + + /* Handle one-shot logging. */ + if ( one_shot ) + { + if (symbol_lookup) { + InitKsyms(symfile); + InitMsyms(); + } + if ( (logsrc = GetKernelLogSrc()) == kernel ) + LogKernelLine(); + else + LogProcLine(); + Terminate(); + } + + /* Determine where kernel logging information is to come from. */ +#if defined(KLOGD_DELAY) + sleep(KLOGD_DELAY); +#endif + logsrc = GetKernelLogSrc(); + if (symbol_lookup) { + InitKsyms(symfile); + InitMsyms(); + } + + /* The main loop. */ + while (1) + { + if ( change_state ) + ChangeLogging(); + switch ( logsrc ) + { + case kernel: + LogKernelLine(); + break; + case proc: + LogProcLine(); + break; + case none: + pause(); + break; + } + } +} +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ -- cgit