summaryrefslogtreecommitdiffstats
path: root/plugins/imklog
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2007-12-17 11:52:10 +0000
committerRainer Gerhards <rgerhards@adiscon.com>2007-12-17 11:52:10 +0000
commit9088ee33893803cc47a69ba671ebfe10cc09c95b (patch)
treee0889ed5bb7dc349eff57d0af1a21c5a81c86218 /plugins/imklog
parent369b0ebd927545b9a9e4be211580ce6e0d77284d (diff)
downloadrsyslog-9088ee33893803cc47a69ba671ebfe10cc09c95b.tar.gz
rsyslog-9088ee33893803cc47a69ba671ebfe10cc09c95b.tar.xz
rsyslog-9088ee33893803cc47a69ba671ebfe10cc09c95b.zip
begun imklog, replacing klogd.c (finally we get rid of it...)
Diffstat (limited to 'plugins/imklog')
-rw-r--r--plugins/imklog/.cvsignore6
-rw-r--r--plugins/imklog/Makefile.am8
-rw-r--r--plugins/imklog/imklog.c753
-rw-r--r--plugins/imklog/ksym.c986
-rw-r--r--plugins/imklog/ksym_mod.c704
-rw-r--r--plugins/imklog/ksyms.h38
-rw-r--r--plugins/imklog/module.h81
7 files changed, 2576 insertions, 0 deletions
diff --git a/plugins/imklog/.cvsignore b/plugins/imklog/.cvsignore
new file mode 100644
index 00000000..f43c5593
--- /dev/null
+++ b/plugins/imklog/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+imklog.la
+immklog-ommysql.lo
diff --git a/plugins/imklog/Makefile.am b/plugins/imklog/Makefile.am
new file mode 100644
index 00000000..23530758
--- /dev/null
+++ b/plugins/imklog/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = imklog.la
+
+imklog_la_SOURCES = imklog.c module.h ksym.c ksyms.h ksym_mod.c ../../module-template.h
+imklog_la_CPPFLAGS = -I$(srcdir)/../.. $(pthreads_cflags)
+imklog_la_LDFLAGS = -module -avoid-version
+imklog_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
new file mode 100644
index 00000000..a6e2a856
--- /dev/null
+++ b/plugins/imklog/imklog.c
@@ -0,0 +1,753 @@
+/* The kernel log input module for Linux. This file heavily
+ * borrows from the klogd daemon provided by the sysklogd project.
+ * Many thanks for this piece of software.
+ *
+ * Please note that this file replaces the klogd daemon that was
+ * also present in pre-v3 versions of rsyslog.
+ *
+ * I have begun to convert this to an input module on 2007-12-17.
+ * IMPORTANT: more than a single instance is currently not supported. This
+ * needs to be revisited once the config file and input module interface
+ * supports multiple instances!
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <pthread.h>
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+
+MODULE_TYPE_INPUT
+TERM_SYNC_TYPE(eTermSync_SIGNAL)
+
+/* defines */
+#define DEFAULT_MARK_PERIOD (20 * 60)
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+typedef struct _instanceData {
+} instanceData;
+
+
+/* Includes. */
+#include <unistd.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#if !defined(__GLIBC__)
+#include <linux/time.h>
+#endif /* __GLIBC__ */
+#include <stdarg.h>
+#include <paths.h>
+#include "ksyms.h"
+
+#define __LIBRARY__
+#include <linux/unistd.h>
+#if !defined(__GLIBC__)
+# define __NR_ksyslog __NR_syslog
+_syscall3(int,ksyslog,int, type, char *, buf, int, len);
+#else
+#include <sys/klog.h>
+#define ksyslog klogctl
+#endif
+
+#ifndef _PATH_KLOG
+#define _PATH_KLOG "/proc/kmsg"
+#endif
+
+#define LOG_BUFFER_SIZE 4096
+#define LOG_LINE_LENGTH 1000
+
+static int kmsg,
+ console_log_level = -1;
+
+static int use_syscall = 0,
+ symbol_lookup = 1;
+
+static char *symfile = (char *) 0,
+ log_buffer[LOG_BUFFER_SIZE];
+
+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 enum LOGSRC GetKernelLogSrc(void);
+static void LogLine(char *ptr, int len);
+static void LogKernelLine(void);
+static void LogProcLine(void);
+
+
+
+/* Write a message to the message queue.
+ * returns -1 if it fails, something else otherwise
+ */
+static rsRetVal writeSyslogV(int iPRI, const char *szFmt, va_list va)// __attribute__((format(printf,2, 3)));
+{
+ DEFiRet;
+ int iChars;
+ int iLen;
+ time_t tNow;
+ char msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */
+ msg_t *pMsg;
+
+ assert(szFmt != NULL);
+
+ /* build the message */
+ time(&tNow);
+ /* we can use sprintf safely below, because we know the size of the constants.
+ * By doing so, we save some cpu cycles and code complexity (for unnecessary
+ * error checking).
+ */
+ iLen = sprintf(msgBuf, "<%d>%.15s kernel: ", iPRI, ctime(&tNow) + 4);
+
+ iChars = vsnprintf(msgBuf + iLen, sizeof(msgBuf) / sizeof(char) - iLen, szFmt, va);
+
+ /* here we must create our message object and supply it to the message queue
+ */
+ if((pMsg = MsgConstruct()) == NULL){
+ /* There is not much we can do in this case - we discard the message
+ * then.
+ */
+ dbgprintf("Memory shortage in imklogd: could not construct Msg object.\n");
+ return RS_RET_OUT_OF_MEMORY;
+ }
+
+dbgprintf("klogd logging Raw: '%s',\nmsg: '%s'\n", msgBuf, msgBuf + iLen);
+ MsgSetUxTradMsg(pMsg, msgBuf);
+ MsgSetRawMsg(pMsg, msgBuf);
+ MsgSetMSG(pMsg, (msgBuf + iLen));
+ MsgSetHOSTNAME(pMsg, LocalHostName);
+ MsgSetTAG(pMsg, "kernel:");
+ pMsg->iFacility = LOG_FAC(LOG_KERN);
+ pMsg->iSeverity = LOG_PRI(iPRI);
+ pMsg->bParseHOSTNAME = 0;
+ getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */
+
+ /* provide message to the queue engine */
+ logmsg(iPRI, pMsg, INTERNAL_MSG);
+
+ return iRet;
+}
+
+/* And now the same with variable arguments */
+static int writeSyslog(int iPRI, const char *szFmt, ...)
+{
+ int iRet;
+ va_list va;
+
+ assert(szFmt != NULL);
+ va_start(va, szFmt);
+ iRet = writeSyslogV(iPRI, szFmt, va);
+ va_end(va);
+
+ return(iRet);
+}
+
+rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
+rsRetVal Syslog(int priority, char *fmt, ...)
+{
+ DEFiRet;
+ va_list ap;
+ char *argl;
+
+ /* 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;
+ }
+ iRet = writeSyslog(priority, fmt, argl);
+ va_end(ap);
+ } else {
+ va_start(ap, fmt);
+ iRet = writeSyslogV(priority, fmt, ap);
+ va_end(ap);
+ }
+
+ return iRet;
+}
+
+
+static void CloseLogSrc(void)
+{
+ /* Turn on logging of messages to console, but only if we had the -c
+ * option -- rgerhards, 2007-08-01
+ */
+ if (console_log_level != -1)
+ 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;
+ }
+
+ 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.");
+ }
+
+ /*
+ * 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);
+ Syslog(LOG_INFO, "imklogd %s, log source = ksyslog "
+ "started.", VERSION);
+ return(kernel);
+ }
+
+ if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 )
+ {
+ fprintf(stderr, "rklogd: Cannot open proc file system, " \
+ "%d - %s.\n", errno, strerror(errno));
+ ksyslog(7, NULL, 0);
+ exit(1);
+ }
+
+ Syslog(LOG_INFO, "imklog %s, log source = %s started.", \
+ VERSION, _PATH_KLOG);
+ return(proc);
+}
+
+
+/* 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<count && !strchr(delim, *ptr); i++ ) {
+ *line++ = *ptr++;
+ }
+
+ return( i );
+}
+
+/*
+ * Messages are separated by "\n". Messages longer than
+ * LOG_LINE_LENGTH are broken up.
+ *
+ * Kernel symbols show up in the input buffer as : "[<aaaaaa>]",
+ * 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;
+ }
+ /* Now that line_buff is no longer fed to *printf as format
+ * string, '%'s are no longer "dangerous".
+ */
+ 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 */
+
+ if ( !symbol_lookup || (symbol = LookupSymbol(value, &sym)) == (char *)0 )
+ {
+ parse_state = PARSING_TEXT;
+ break;
+ }
+
+ /*
+ ** verify there is room in the line buffer
+ */
+ sym_space = space + ( line - sym_start );
+ if( (unsigned) 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)-1)) < 0 )
+ {
+ if ( errno == EINTR )
+ return;
+ fprintf(stderr, "rklogd: 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;
+}
+
+
+#if 0
+int main(int argc, char *argv[])
+{
+ int ch,
+ use_output = 0;
+
+ char *log_level = (char *) 0,
+ *output = (char *) 0;
+
+ /* 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 'k': /* Kernel symbol file. */
+ symfile = optarg;
+ break;
+ case 'p':
+ SetParanoiaLevel(1); /* Load symbols on oops. */
+ break;
+ case 's': /* Use syscall interface. */
+ use_syscall = 1;
+ break;
+ 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, "rklogd: Invalid console logging "
+ "level <%s> specified.\n", log_level);
+ return(1);
+ }
+ console_log_level = *log_level - '0';
+ }
+}
+#endif
+
+BEGINrunInput
+CODESTARTrunInput
+ /* Determine where kernel logging information is to come from. */
+ logsrc = GetKernelLogSrc();
+ if (symbol_lookup) {
+ symbol_lookup = (InitKsyms(symfile) == 1);
+ symbol_lookup |= InitMsyms();
+ if (symbol_lookup == 0) {
+ Syslog(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
+ }
+ }
+
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(!pThrd->bShallStop) {
+ /* we do not need to handle the RS_RET_TERMINATE_NOW case any
+ * special because we just need to terminate. This may be different
+ * if a cleanup is needed. But for now, we can just use CHKiRet().
+ * rgerhards, 2007-12-17
+ */
+ switch ( logsrc )
+ {
+ case kernel:
+ LogKernelLine();
+ break;
+ case proc:
+ LogProcLine();
+ break;
+ case none:
+ pause();
+ break;
+ }
+// CHKiRet(thrdSleep(pThrd, iMarkMessagePeriod, 0)); /* seconds, micro seconds */
+ }
+finalize_it:
+ /* cleanup here */
+ CloseLogSrc();
+
+ return iRet;
+ENDrunInput
+
+
+BEGINwillRun
+CODESTARTwillRun
+ENDwillRun
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+
+ return RS_RET_OK;
+}
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = 1; /* so far, we only support the initial definition */
+CODEmodInit_QueryRegCFSLineHdlr
+ //CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL, &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ * vi:set ai:
+ */
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
new file mode 100644
index 00000000..baee2c05
--- /dev/null
+++ b/plugins/imklog/ksym.c
@@ -0,0 +1,986 @@
+#include "config.h"
+
+#ifdef FEATURE_KLOGD
+/*
+ ksym.c - functions for kernel address->symbol translation
+ Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ Copyright (c) 1996 Enjellic Systems Development
+
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/*
+ * This file contains functions which handle the translation of kernel
+ * numeric addresses into symbols for the klogd utility.
+ *
+ * Sat Oct 28 09:00:14 CDT 1995: Dr. Wettstein
+ * Initial Version.
+ *
+ * Fri Nov 24 12:50:52 CST 1995: Dr. Wettstein
+ * Added VERBOSE_DEBUGGING define to make debugging output more
+ * manageable.
+ *
+ * Added support for verification of the loaded kernel symbols. If
+ * no version information can be be found in the mapfile a warning
+ * message is issued but translation will still take place. This
+ * will be the default case if kernel versions < 1.3.43 are used.
+ *
+ * If the symbols in the mapfile are of the same version as the kernel
+ * that is running an informative message is issued. If the symbols
+ * in the mapfile do not match the current kernel version a warning
+ * message is issued and translation is disabled.
+ *
+ * Wed Dec 6 16:14:11 CST 1995: Dr. Wettstein
+ * Added /boot/System.map to the list of symbol maps to search for.
+ * Also made this map the first item in the search list. I am open
+ * to CONSTRUCTIVE suggestions for any additions or corrections to
+ * the list of symbol maps to search for. Be forewarned that the
+ * list in use is the consensus agreement between myself, Linus and
+ * some package distributers. It is a given that no list will suit
+ * everyone's taste. If you have rabid concerns about the list
+ * please feel free to edit the system_maps array and compile your
+ * own binaries.
+ *
+ * Added support for searching of the list of symbol maps. This
+ * allows support for access to multiple symbol maps. The theory
+ * behind this is that a production kernel may have a system map in
+ * /boot/System.map. If a test kernel is booted this system map
+ * would be skipped in favor of one found in /usr/src/linux.
+ *
+ * Thu Jan 18 11:18:31 CST 1996: Dr. Wettstein
+ * Added patch from beta-testers to allow for reading of both
+ * ELF and a.out map files.
+ *
+ * Wed Aug 21 09:15:49 CDT 1996: Dr. Wettstein
+ * Reloading of kernel module symbols is now turned on by the
+ * SetParanoiaLevel function. The default behavior is to NOT reload
+ * the kernel module symbols when a protection fault is detected.
+ *
+ * Added support for freeing of the current kernel module symbols.
+ * This was necessary to support reloading of the kernel module symbols.
+ *
+ * When a matching static symbol table is loaded the kernel version
+ * number is printed.
+ *
+ * Mon Jun 9 17:12:42 CST 1997: Martin Schulze
+ * Added #1 and #2 to some error messages in order to being able
+ * to divide them (ulmo@Q.Net)
+ *
+ * Fri Jun 13 10:50:23 CST 1997: Martin Schulze
+ * Changed definition of LookupSymbol to non-static because it is
+ * used in klogd.c, too.
+ *
+ * Fri Jan 9 23:00:08 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Fixed bug that caused klogd to die if there is no System.map available.
+ *
+ * Sun 29 Mar 18:14:07 BST 1998: Mark Simon Phillips <M.S.Phillips@nortel.co.uk>
+ * Switched to fgets() as gets() is not buffer overrun secure.
+ *
+ * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Modified loop for detecting the correct system map. Now it won't
+ * stop if a file has been found but doesn't contain the correct map.
+ * Special thanks go go Mark Simon Phillips for the hint.
+ *
+ * Mon Oct 12 00:42:30 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Modified CheckVersion()
+ * . Use shift to decode the kernel version
+ * . Compare integers of kernel version
+ * . extract major.minor.patch from utsname.release via sscanf()
+ * The reason lays in possible use of kernel flavours which
+ * modify utsname.release but no the Version_ symbol.
+ *
+ * Sun Feb 21 22:27:49 EST 1999: Keith Owens <kaos@ocs.com.au>
+ * Fixed bug that caused klogd to die if there is no sym_array available.
+ *
+ * Tue Sep 12 23:48:12 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
+ * Close symbol file in InitKsyms() when an error occurred.
+ */
+
+
+/* Includes. */
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#include "klogd.h"
+#include "ksyms.h"
+
+#define VERBOSE_DEBUGGING 0
+
+
+/* Variables static to this module. */
+struct sym_table
+{
+ unsigned long value;
+ char *name;
+};
+
+static int num_syms = 0;
+static int i_am_paranoid = 0;
+static char vstring[12];
+static struct sym_table *sym_array = (struct sym_table *) 0;
+
+static char *system_maps[] =
+{
+ "/boot/System.map",
+ "/System.map",
+#if defined(TEST)
+ "./System.map",
+#endif
+ (char *) 0
+};
+
+
+#if defined(TEST)
+int debugging;
+#else
+extern int debugging;
+#endif
+
+
+/* Function prototypes. */
+static char * FindSymbolFile(void);
+static int AddSymbol(unsigned long, char*);
+static void FreeSymbols(void);
+static int CheckVersion(char *);
+static int CheckMapVersion(char *);
+
+
+/**************************************************************************
+ * Function: InitKsyms
+ *
+ * Purpose: This function is responsible for initializing and loading
+ * the data tables used by the kernel address translations.
+ *
+ * Arguements: (char *) mapfile
+ *
+ * mapfile:-> A pointer to a complete path
+ * specification of the file containing
+ * the kernel map to use.
+ *
+ * Return: int
+ *
+ * A boolean style context is returned. The return value will
+ * be true if initialization was successful. False if not.
+ **************************************************************************/
+
+extern int InitKsyms(mapfile)
+
+ char *mapfile;
+
+{
+ auto char type,
+ sym[512];
+
+ auto int version = 0;
+
+ auto unsigned long int address;
+
+ auto FILE *sym_file;
+
+
+ /* Check and make sure that we are starting with a clean slate. */
+ if ( num_syms > 0 )
+ FreeSymbols();
+
+
+ /*
+ * Search for and open the file containing the kernel symbols.
+ */
+ if ( mapfile != (char *) 0 )
+ {
+ if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+ {
+ Syslog(LOG_WARNING, "Cannot open map file: %s.", \
+ mapfile);
+ return(0);
+ }
+ }
+ else
+ {
+ if ( (mapfile = FindSymbolFile()) == (char *) 0 )
+ {
+ Syslog(LOG_WARNING, "Cannot find map file.");
+ if ( debugging )
+ fputs("Cannot find map file.\n", stderr);
+ return(0);
+ }
+
+ if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+ {
+ Syslog(LOG_WARNING, "Cannot open map file.");
+ if ( debugging )
+ fputs("Cannot open map file.\n", stderr);
+ return(0);
+ }
+ }
+
+
+ /*
+ * Read the kernel symbol table file and add entries for each
+ * line. I suspect that the use of fscanf is not really in vogue
+ * but it was quick and dirty and IMHO suitable for fixed format
+ * data such as this. If anybody doesn't agree with this please
+ * e-mail me a diff containing a parser with suitable political
+ * correctness -- GW.
+ */
+ while ( !feof(sym_file) )
+ {
+ if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym)
+ != 3 )
+ {
+ Syslog(LOG_ERR, "Error in symbol table input (#1).");
+ fclose(sym_file);
+ return(0);
+ }
+ if ( VERBOSE_DEBUGGING && debugging )
+ fprintf(stderr, "Address: %lx, Type: %c, Symbol: %s\n",
+ address, type, sym);
+
+ if ( AddSymbol(address, sym) == 0 )
+ {
+ Syslog(LOG_ERR, "Error adding symbol - %s.", sym);
+ fclose(sym_file);
+ return(0);
+ }
+
+ if ( version == 0 )
+ version = CheckVersion(sym);
+ }
+
+
+ Syslog(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
+ switch ( version )
+ {
+ case -1:
+ Syslog(LOG_WARNING, "Symbols do not match kernel version.");
+ num_syms = 0;
+ break;
+
+ case 0:
+ Syslog(LOG_WARNING, "Cannot verify that symbols match " \
+ "kernel version.");
+ break;
+
+ case 1:
+ Syslog(LOG_INFO, "Symbols match kernel version %s.", vstring);
+ break;
+ }
+
+ fclose(sym_file);
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: FindSymbolFile
+ *
+ * Purpose: This function is responsible for encapsulating the search
+ * for a valid symbol file. Encapsulating the search for
+ * the map file in this function allows an intelligent search
+ * process to be implemented.
+ *
+ * The list of symbol files will be searched until either a
+ * symbol file is found whose version matches the currently
+ * executing kernel or the end of the list is encountered. If
+ * the end of the list is encountered the first available
+ * symbol file is returned to the caller.
+ *
+ * This strategy allows klogd to locate valid symbol files
+ * for both a production and an experimental kernel. For
+ * example a map for a production kernel could be installed
+ * in /boot. If an experimental kernel is loaded the map
+ * in /boot will be skipped and the map in /usr/src/linux would
+ * be used if its version number matches the executing kernel.
+ *
+ * Arguements: None specified.
+ *
+ * Return: char *
+ *
+ * If a valid system map cannot be located a null pointer
+ * is returned to the caller.
+ *
+ * If the search is succesful a pointer is returned to the
+ * caller which points to the name of the file containing
+ * the symbol table to be used.
+ **************************************************************************/
+
+static char * FindSymbolFile()
+
+{
+ auto char *file = (char *) 0,
+ **mf = system_maps;
+
+ auto struct utsname utsname;
+ static char symfile[100];
+
+ auto FILE *sym_file = (FILE *) 0;
+
+ if ( uname(&utsname) < 0 )
+ {
+ Syslog(LOG_ERR, "Cannot get kernel version information.");
+ return(0);
+ }
+
+ if ( debugging )
+ fputs("Searching for symbol map.\n", stderr);
+
+ for (mf = system_maps; *mf != (char *) 0 && file == (char *) 0; ++mf)
+ {
+
+ sprintf (symfile, "%s-%s", *mf, utsname.release);
+ if ( debugging )
+ fprintf(stderr, "Trying %s.\n", symfile);
+ if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
+ if (CheckMapVersion(symfile) == 1)
+ file = symfile;
+ fclose(sym_file);
+ }
+ if (sym_file == (FILE *) 0 || file == (char *) 0) {
+ sprintf (symfile, "%s", *mf);
+ if ( debugging )
+ fprintf(stderr, "Trying %s.\n", symfile);
+ if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
+ if (CheckMapVersion(symfile) == 1)
+ file = symfile;
+ fclose(sym_file);
+ }
+ }
+
+ }
+
+ /*
+ * At this stage of the game we are at the end of the symbol
+ * tables.
+ */
+ if ( debugging )
+ fprintf(stderr, "End of search list encountered.\n");
+ return(file);
+}
+
+
+/**************************************************************************
+ * Function: CheckVersion
+ *
+ * Purpose: This function is responsible for determining whether or
+ * the system map being loaded matches the version of the
+ * currently running kernel.
+ *
+ * The kernel version is checked by examing a variable which
+ * is of the form: _Version_66347 (a.out) or Version_66437 (ELF).
+ *
+ * The suffix of this variable is the current kernel version
+ * of the kernel encoded in base 256. For example the
+ * above variable would be decoded as:
+ *
+ * (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
+ *
+ * (Insert appropriate deities here) help us if Linus ever
+ * needs more than 255 patch levels to get a kernel out the
+ * door... :-)
+ *
+ * Arguements: (char *) version
+ *
+ * version:-> A pointer to the string which
+ * is to be decoded as a kernel
+ * version variable.
+ *
+ * Return: int
+ *
+ * -1:-> The currently running kernel version does
+ * not match this version string.
+ *
+ * 0:-> The string is not a kernel version variable.
+ *
+ * 1:-> The executing kernel is of the same version
+ * as the version string.
+ **************************************************************************/
+
+static int CheckVersion(version)
+
+ char *version;
+
+
+{
+ auto int vnum,
+ major,
+ minor,
+ patch;
+
+#ifndef TESTING
+ int kvnum;
+ auto struct utsname utsname;
+#endif
+
+ static char *prefix = { "Version_" };
+
+
+ /* Early return if there is no hope. */
+ if ( strncmp(version, prefix, strlen(prefix)) == 0 /* ELF */ ||
+ (*version == '_' &&
+ strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out */ )
+ ;
+ else
+ return(0);
+
+
+ /*
+ * Since the symbol looks like a kernel version we can start
+ * things out by decoding the version string into its component
+ * parts.
+ */
+ vnum = atoi(version + strlen(prefix));
+ patch = vnum & 0x000000FF;
+ minor = (vnum >> 8) & 0x000000FF;
+ major = (vnum >> 16) & 0x000000FF;
+ if ( debugging )
+ fprintf(stderr, "Version string = %s, Major = %d, " \
+ "Minor = %d, Patch = %d.\n", version +
+ strlen(prefix), major, minor, \
+ patch);
+ sprintf(vstring, "%d.%d.%d", major, minor, patch);
+
+#ifndef TESTING
+ /*
+ * We should now have the version string in the vstring variable in
+ * the same format that it is stored in by the kernel. We now
+ * ask the kernel for its version information and compare the two
+ * values to determine if our system map matches the kernel
+ * version level.
+ */
+ if ( uname(&utsname) < 0 )
+ {
+ Syslog(LOG_ERR, "Cannot get kernel version information.");
+ return(0);
+ }
+ if ( debugging )
+ fprintf(stderr, "Comparing kernel %s with symbol table %s.\n",\
+ utsname.release, vstring);
+
+ if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 )
+ {
+ Syslog(LOG_ERR, "Kernel send bogus release string `%s'.",
+ utsname.release);
+ return(0);
+ }
+
+ /* Compute the version code from data sent by the kernel */
+ kvnum = (major << 16) | (minor << 8) | patch;
+
+ /* Failure. */
+ if ( vnum != kvnum )
+ return(-1);
+
+ /* Success. */
+#endif
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: CheckMapVersion
+ *
+ * Purpose: This function is responsible for determining whether or
+ * the system map being loaded matches the version of the
+ * currently running kernel. It uses CheckVersion as
+ * backend.
+ *
+ * Arguements: (char *) fname
+ *
+ * fname:-> A pointer to the string which
+ * references the system map file to
+ * be used.
+ *
+ * Return: int
+ *
+ * -1:-> The currently running kernel version does
+ * not match the version in the given file.
+ *
+ * 0:-> No system map file or no version information.
+ *
+ * 1:-> The executing kernel is of the same version
+ * as the version of the map file.
+ **************************************************************************/
+
+static int CheckMapVersion(fname)
+
+ char *fname;
+
+
+{
+ int version;
+ FILE *sym_file;
+ auto unsigned long int address;
+ auto char type,
+ sym[512];
+
+ if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
+ /*
+ * At this point a map file was successfully opened. We
+ * now need to search this file and look for version
+ * information.
+ */
+ Syslog(LOG_INFO, "Inspecting %s", fname);
+
+ version = 0;
+ while ( !feof(sym_file) && (version == 0) )
+ {
+ if ( fscanf(sym_file, "%lx %c %s\n", &address, \
+ &type, sym) != 3 )
+ {
+ Syslog(LOG_ERR, "Error in symbol table input (#2).");
+ fclose(sym_file);
+ return(0);
+ }
+ if ( VERBOSE_DEBUGGING && debugging )
+ fprintf(stderr, "Address: %lx, Type: %c, " \
+ "Symbol: %s\n", address, type, sym);
+
+ version = CheckVersion(sym);
+ }
+ fclose(sym_file);
+
+ switch ( version )
+ {
+ case -1:
+ Syslog(LOG_ERR, "Symbol table has incorrect " \
+ "version number.\n");
+ break;
+
+ case 0:
+ if ( debugging )
+ fprintf(stderr, "No version information " \
+ "found.\n");
+ break;
+ case 1:
+ if ( debugging )
+ fprintf(stderr, "Found table with " \
+ "matching version number.\n");
+ break;
+ }
+
+ return(version);
+ }
+
+ return(0);
+}
+
+
+/**************************************************************************
+ * Function: AddSymbol
+ *
+ * Purpose: This function is responsible for adding a symbol name
+ * and its address to the symbol table.
+ *
+ * Arguements: (unsigned long) address, (char *) symbol
+ *
+ * Return: int
+ *
+ * A boolean value is assumed. True if the addition is
+ * successful. False if not.
+ **************************************************************************/
+
+static int AddSymbol(address, symbol)
+
+ unsigned long address;
+
+ char *symbol;
+
+{
+ /* Allocate the the symbol table entry. */
+ sym_array = (struct sym_table *) realloc(sym_array, (num_syms+1) * \
+ sizeof(struct sym_table));
+ if ( sym_array == (struct sym_table *) 0 )
+ return(0);
+
+ /* Then the space for the symbol. */
+ sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char)\
+ + 1);
+ if ( sym_array[num_syms].name == (char *) 0 )
+ return(0);
+
+ sym_array[num_syms].value = address;
+ strcpy(sym_array[num_syms].name, symbol);
+ ++num_syms;
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: LookupSymbol
+ *
+ * Purpose: Find the symbol which is related to the given kernel
+ * address.
+ *
+ * Arguements: (long int) value, (struct symbol *) sym
+ *
+ * value:-> The address to be located.
+ *
+ * sym:-> A pointer to a structure which will be
+ * loaded with the symbol's parameters.
+ *
+ * Return: (char *)
+ *
+ * If a match cannot be found a diagnostic string is printed.
+ * If a match is found the pointer to the symbolic name most
+ * closely matching the address is returned.
+ **************************************************************************/
+
+char * LookupSymbol(value, sym)
+
+ unsigned long value;
+
+ struct symbol *sym;
+
+{
+ auto int lp;
+
+ auto char *last;
+
+ if (!sym_array)
+ return((char *) 0);
+
+ last = sym_array[0].name;
+ sym->offset = 0;
+ sym->size = 0;
+ if ( value < sym_array[0].value )
+ return((char *) 0);
+
+ for(lp= 0; lp <= num_syms; ++lp)
+ {
+ if ( sym_array[lp].value > value )
+ {
+ sym->offset = value - sym_array[lp-1].value;
+ sym->size = sym_array[lp].value - \
+ sym_array[lp-1].value;
+ return(last);
+ }
+ last = sym_array[lp].name;
+ }
+
+ if ( (last = LookupModuleSymbol(value, sym)) != (char *) 0 )
+ return(last);
+
+ return((char *) 0);
+}
+
+
+/**************************************************************************
+ * Function: FreeSymbols
+ *
+ * Purpose: This function is responsible for freeing all memory which
+ * has been allocated to hold the static symbol table. It
+ * also initializes the symbol count and in general prepares
+ * for a re-read of a static symbol table.
+ *
+ * Arguements: void
+ *
+ * Return: void
+ **************************************************************************/
+
+static void FreeSymbols()
+
+{
+ auto int lp;
+
+ /* Free each piece of memory allocated for symbol names. */
+ for(lp= 0; lp < num_syms; ++lp)
+ free(sym_array[lp].name);
+
+ /* Whack the entire array and initialize everything. */
+ free(sym_array);
+ sym_array = (struct sym_table *) 0;
+ num_syms = 0;
+
+ return;
+}
+
+
+/**************************************************************************
+ * Function: LogExpanded
+ *
+ * Purpose: This function is responsible for logging a kernel message
+ * line after all potential numeric kernel addresses have
+ * been resolved symolically.
+ *
+ * Arguements: (char *) line, (char *) el
+ *
+ * line:-> A pointer to the buffer containing the kernel
+ * message to be expanded and logged.
+ *
+ * el:-> A pointer to the buffer into which the expanded
+ * kernel line will be written.
+ *
+ * Return: void
+ **************************************************************************/
+
+extern char * ExpandKadds(line, el)
+
+ char *line;
+
+ char *el;
+
+{
+ auto char dlm,
+ *kp,
+ *sl = line,
+ *elp = el,
+ *symbol;
+
+ char num[15];
+ auto unsigned long int value;
+
+ auto struct symbol sym;
+
+
+ /*
+ * This is as handy a place to put this as anyplace.
+ *
+ * Since the insertion of kernel modules can occur in a somewhat
+ * dynamic fashion we need some mechanism to insure that the
+ * kernel symbol tables get read just prior to when they are
+ * needed.
+ *
+ * To accomplish this we look for the Oops string and use its
+ * presence as a signal to load the module symbols.
+ *
+ * This is not the best solution of course, especially if the
+ * kernel is rapidly going out to lunch. What really needs to
+ * be done is to somehow generate a callback from the
+ * kernel whenever a module is loaded or unloaded. I am
+ * open for patches.
+ */
+ if ( i_am_paranoid &&
+ (strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
+ Syslog(LOG_WARNING, "Cannot load kernel module symbols.\n");
+
+
+ /*
+ * Early return if there do not appear to be any kernel
+ * messages in this line.
+ */
+ if ( (num_syms == 0) ||
+ (kp = strstr(line, "[<")) == (char *) 0 )
+ {
+#ifdef __sparc__
+ if (num_syms) {
+ /*
+ * On SPARC, register dumps do not have the [< >] characters in it.
+ */
+ static struct sparc_tests {
+ char *str;
+ int len;
+ } tests[] = { { "PC: ", 4 },
+ { " o7: ", 5 },
+ { " ret_pc: ", 9 },
+ { " i7: ", 5 },
+ { "Caller[", 7 }
+ };
+ int i, j, ndigits;
+ char *kp2;
+ for (i = 0; i < 5; i++) {
+ kp = strstr(line, tests[i].str);
+ if (!kp) continue;
+ kp2 = kp + tests[i].len;
+ if (!isxdigit(*kp2)) continue;
+ for (ndigits = 1; isxdigit(kp2[ndigits]); ndigits++);
+ if (ndigits != 8 && ndigits != 16) continue;
+ /* On sparc64, all kernel addresses are in first 4GB */
+ if (ndigits == 16) {
+ if (strncmp (kp2, "00000000", 8)) continue;
+ kp2 += 8;
+ }
+ if (!i) {
+ char *kp3;
+ if (ndigits == 16 && kp > line && kp[-1L] != 'T') continue;
+ kp3 = kp2 + 8;
+ if (ndigits == 16) {
+ if (strncmp (kp3, " TNPC: 00000000", 15) || !isxdigit(kp3[15]))
+ continue;
+ kp3 += 15;
+ } else {
+ if (strncmp (kp3, " NPC: ", 6) || !isxdigit(kp3[6]))
+ continue;
+ kp3 += 6;
+ }
+ for (j = 0; isxdigit(kp3[j]); j++);
+ if (j != 8) continue;
+ strncpy(elp, line, kp2 + 8 - line);
+ elp += kp2 + 8 - line;
+ value = strtol(kp2, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strncpy(elp, kp2 + 8, kp3 - kp2);
+ elp += kp3 - kp2;
+ value = strtol(kp3, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strcpy(elp, kp3 + 8);
+ } else {
+ strncpy(elp, line, kp2 + 8 - line);
+ elp += kp2 + 8 - line;
+ value = strtol(kp2, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strcpy(elp, kp2 + 8);
+ }
+ return el;
+ }
+ }
+#endif
+ strcpy(el, line);
+ return(el);
+ }
+
+ /* Loop through and expand all kernel messages. */
+ do
+ {
+ while ( sl < kp+1 )
+ *elp++ = *sl++;
+
+ /* Now poised at a kernel delimiter. */
+ if ( (kp = strstr(sl, ">]")) == (char *) 0 )
+ {
+ strcpy(el, sl);
+ return(el);
+ }
+ dlm = *kp;
+ strncpy(num,sl+1,kp-sl-1);
+ num[kp-sl-1] = '\0';
+ value = strtoul(num, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
+ symbol = sl;
+
+ strcat(elp, symbol);
+ elp += strlen(symbol);
+ if ( debugging )
+ fprintf(stderr, "Symbol: %s = %lx = %s, %x/%d\n", \
+ sl+1, value, \
+ (sym.size==0) ? symbol+1 : symbol, \
+ sym.offset, sym.size);
+
+ value = 2;
+ if ( sym.size != 0 )
+ {
+ --value;
+ ++kp;
+ elp += sprintf(elp, "+%x/%d", sym.offset, sym.size);
+ }
+ strncat(elp, kp, value);
+ elp += value;
+ sl = kp + value;
+ if ( (kp = strstr(sl, "[<")) == (char *) 0 )
+ strcat(elp, sl);
+ }
+ while ( kp != (char *) 0);
+
+ if ( debugging )
+ fprintf(stderr, "Expanded line: %s\n", el);
+ return(el);
+}
+
+
+/**************************************************************************
+ * Function: SetParanoiaLevel
+ *
+ * Purpose: This function is an interface function for setting the
+ * mode of loadable module symbol lookups. Probably overkill
+ * but it does slay another global variable.
+ *
+ * Arguements: (int) level
+ *
+ * level:-> The amount of paranoia which is to be
+ * present when resolving kernel exceptions.
+ * Return: void
+ **************************************************************************/
+
+extern void SetParanoiaLevel(level)
+
+ int level;
+
+{
+ i_am_paranoid = level;
+ return;
+}
+
+
+/*
+ * Setting the -DTEST define enables the following code fragment to
+ * be compiled. This produces a small standalone program which will
+ * echo the standard input of the process to stdout while translating
+ * all numeric kernel addresses into their symbolic equivalent.
+ */
+#if defined(TEST)
+
+#include <stdarg.h>
+
+extern int main(int, char **);
+
+
+extern int main(int argc, char *argv[])
+{
+ auto char line[1024], eline[2048];
+
+ debugging = 1;
+
+
+ if ( !InitKsyms((char *) 0) )
+ {
+ fputs("ksym: Error loading system map.\n", stderr);
+ return(1);
+ }
+
+ while ( !feof(stdin) )
+ {
+ fgets(line, sizeof(line), stdin);
+ if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; /* Trash NL char */
+ memset(eline, '\0', sizeof(eline));
+ ExpandKadds(line, eline);
+ fprintf(stdout, "%s\n", eline);
+ }
+
+
+ return(0);
+}
+
+extern void Syslog(int priority, char *fmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stdout, "Pr: %d, ", priority);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ fputc('\n', stdout);
+
+ return;
+}
+#endif
+#endif /* #ifdef FEATURE_KLOGD */
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
new file mode 100644
index 00000000..51ca2fb8
--- /dev/null
+++ b/plugins/imklog/ksym_mod.c
@@ -0,0 +1,704 @@
+#include "config.h"
+
+#ifdef FEATURE_KLOGD
+/*
+ ksym_mod.c - functions for building symbol lookup tables for klogd
+ Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ Copyright (c) 1996 Enjellic Systems Development
+
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/*
+ * This file implements functions which are useful for building
+ * a symbol lookup table based on the in kernel symbol table
+ * maintained by the Linux kernel.
+ *
+ * Proper logging of kernel panics generated by loadable modules
+ * tends to be difficult. Since the modules are loaded dynamically
+ * their addresses are not known at kernel load time. A general
+ * protection fault (Oops) cannot be properly deciphered with
+ * classic methods using the static symbol map produced at link time.
+ *
+ * One solution to this problem is to have klogd attempt to translate
+ * addresses from module when the fault occurs. By referencing the
+ * the kernel symbol table proper resolution of these symbols is made
+ * possible.
+ *
+ * At least that is the plan.
+ *
+ * Wed Aug 21 09:20:09 CDT 1996: Dr. Wettstein
+ * The situation where no module support has been compiled into a
+ * kernel is now detected. An informative message is output indicating
+ * that the kernel has no loadable module support whenever kernel
+ * module symbols are loaded.
+ *
+ * An informative message is printed indicating the number of kernel
+ * modules and the number of symbols loaded from these modules.
+ *
+ * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman
+ * Some more glibc patches made by <mdorman@debian.org>.
+ *
+ * Sat Jan 10 15:00:18 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Fixed problem with klogd not being able to be built on a kernel
+ * newer than 2.1.18. It was caused by modified structures
+ * inside the kernel that were included. I have worked in a
+ * patch from Alessandro Suardi <asuardi@uninetcom.it>.
+ *
+ * Sun Jan 25 20:57:34 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Another patch for Linux/alpha by Christopher C Chimelis
+ * <chris@classnet.med.miami.edu>.
+ *
+ * Thu Mar 19 23:39:29 CET 1998: Manuel Rodrigues <pmanuel@cindy.fe.up.pt>
+ * Changed lseek() to llseek() in order to support > 2GB address
+ * space which provided by kernels > 2.1.70.
+ *
+ * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Removed <sys/module.h> as it's no longer part of recent glibc
+ * versions. Added prototyp for llseek() which has been
+ * forgotton in <unistd.h> from glibc. Added more log
+ * information if problems occurred while reading a system map
+ * file, by submission from Mark Simon Phillips <M.S.Phillips@nortel.co.uk>.
+ *
+ * Sun Jan 3 18:38:03 CET 1999: Martin Schulze <joey@infodrom.north.de>
+ * Corrected return value of AddModule if /dev/kmem can't be
+ * loaded. This will prevent klogd from segfaulting if /dev/kmem
+ * is not available. Patch from Topi Miettinen <tom@medialab.sonera.net>.
+ *
+ * Tue Sep 12 23:11:13 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
+ * Changed llseek() to lseek64() in order to skip a libc warning.
+ */
+
+
+/* Includes. */
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#if !defined(__GLIBC__)
+#include <linux/time.h>
+#include <linux/module.h>
+#else /* __GLIBC__ */
+#include "module.h"
+extern __off64_t lseek64 __P ((int __fd, __off64_t __offset, int __whence));
+extern int get_kernel_syms __P ((struct kernel_sym *__table));
+#endif /* __GLIBC__ */
+#include <stdarg.h>
+#include <paths.h>
+#include <linux/version.h>
+
+#include "klogd.h"
+#include "ksyms.h"
+
+
+#if !defined(__GLIBC__)
+/*
+ * The following bit uses some kernel/library magic to product what
+ * looks like a function call to user level code. This function is
+ * actually a system call in disguise. The purpose of the getsyms
+ * call is to return a current copy of the in-kernel symbol table.
+ */
+#define __LIBRARY__
+#include <linux/unistd.h>
+#define __NR_getsyms __NR_get_kernel_syms
+_syscall1(int, getsyms, struct kernel_sym *, syms);
+#undef __LIBRARY__
+extern int getsyms(struct kernel_sym *);
+#else /* __GLIBC__ */
+#define getsyms get_kernel_syms
+#endif /* __GLIBC__ */
+
+/* Variables static to this module. */
+struct sym_table
+{
+ unsigned long value;
+ char *name;
+};
+
+struct Module
+{
+ struct sym_table *sym_array;
+ int num_syms;
+
+ char *name;
+ struct module module;
+#if LINUX_VERSION_CODE >= 0x20112
+ struct module_info module_info;
+#endif
+};
+
+static int num_modules = 0;
+struct Module *sym_array_modules = (struct Module *) 0;
+
+static int have_modules = 0;
+
+#if defined(TEST)
+static int debugging = 1;
+#else
+extern int debugging;
+#endif
+
+
+/* Function prototypes. */
+static void FreeModules(void);
+static int AddSymbol(struct Module *mp, unsigned long, char *);
+static int AddModule(unsigned long, char *);
+static int symsort(const void *, const void *);
+
+
+/**************************************************************************
+ * Function: InitMsyms
+ *
+ * Purpose: This function is responsible for building a symbol
+ * table which can be used to resolve addresses for
+ * loadable modules.
+ *
+ * Arguements: Void
+ *
+ * Return: A boolean return value is assumed.
+ *
+ * A false value indicates that something went wrong.
+ *
+ * True if loading is successful.
+ **************************************************************************/
+
+extern int InitMsyms()
+
+{
+ auto int rtn,
+ tmp;
+
+ auto struct kernel_sym *ksym_table,
+ *p;
+
+
+ /* Initialize the kernel module symbol table. */
+ FreeModules();
+
+
+ /*
+ * The system call which returns the kernel symbol table has
+ * essentialy two modes of operation. Called with a null pointer
+ * the system call returns the number of symbols defined in the
+ * the table.
+ *
+ * The second mode of operation is to pass a valid pointer to
+ * the call which will then load the current symbol table into
+ * the memory provided.
+ *
+ * Returning the symbol table is essentially an all or nothing
+ * proposition so we need to pre-allocate enough memory for the
+ * complete table regardless of how many symbols we need.
+ *
+ * Bummer.
+ */
+ if ( (rtn = getsyms((struct kernel_sym *) 0)) < 0 )
+ {
+ if ( errno == ENOSYS )
+ Syslog(LOG_INFO, "No module symbols loaded - "
+ "kernel modules not enabled.\n");
+ else
+ Syslog(LOG_ERR, "Error loading kernel symbols " \
+ "- %s\n", strerror(errno));
+ return(0);
+ }
+ if ( debugging )
+ fprintf(stderr, "Loading kernel module symbols - "
+ "Size of table: %d\n", rtn);
+
+ ksym_table = (struct kernel_sym *) malloc(rtn * \
+ sizeof(struct kernel_sym));
+ if ( ksym_table == (struct kernel_sym *) 0 )
+ {
+ Syslog(LOG_WARNING, " Failed memory allocation for kernel " \
+ "symbol table.\n");
+ return(0);
+ }
+ if ( (rtn = getsyms(ksym_table)) < 0 )
+ {
+ Syslog(LOG_WARNING, "Error reading kernel symbols - %s\n", \
+ strerror(errno));
+ return(0);
+ }
+
+
+ /*
+ * Build a symbol table compatible with the other one used by
+ * klogd.
+ */
+ tmp = rtn;
+ p = ksym_table;
+ while ( tmp-- )
+ {
+ if ( !AddModule(p->value, p->name) )
+ {
+ Syslog(LOG_WARNING, "Error adding kernel module table "
+ "entry.\n");
+ free(ksym_table);
+ return(0);
+ }
+ ++p;
+ }
+
+ /* Sort the symbol tables in each module. */
+ for (rtn = tmp= 0; tmp < num_modules; ++tmp)
+ {
+ rtn += sym_array_modules[tmp].num_syms;
+ if ( sym_array_modules[tmp].num_syms < 2 )
+ continue;
+ qsort(sym_array_modules[tmp].sym_array, \
+ sym_array_modules[tmp].num_syms, \
+ sizeof(struct sym_table), symsort);
+ }
+
+ if ( rtn == 0 )
+ Syslog(LOG_INFO, "No module symbols loaded.");
+ else
+ Syslog(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \
+ (rtn == 1) ? "symbol" : "symbols", \
+ num_modules, (num_modules == 1) ? "." : "s.");
+ free(ksym_table);
+ return(1);
+}
+
+
+static int symsort(p1, p2)
+
+ const void *p1;
+
+ const void *p2;
+
+{
+ auto const struct sym_table *sym1 = p1,
+ *sym2 = p2;
+
+ if ( sym1->value < sym2->value )
+ return(-1);
+ if ( sym1->value == sym2->value )
+ return(0);
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: FreeModules
+ *
+ * Purpose: This function is used to free all memory which has been
+ * allocated for the modules and their symbols.
+ *
+ * Arguements: None specified.
+ *
+ * Return: void
+ **************************************************************************/
+
+static void FreeModules()
+
+{
+ auto int nmods,
+ nsyms;
+
+ auto struct Module *mp;
+
+
+ /* Check to see if the module symbol tables need to be cleared. */
+ have_modules = 0;
+ if ( num_modules == 0 )
+ return;
+
+
+ for (nmods= 0; nmods < num_modules; ++nmods)
+ {
+ mp = &sym_array_modules[nmods];
+ if ( mp->num_syms == 0 )
+ continue;
+
+ for (nsyms= 0; nsyms < mp->num_syms; ++nsyms)
+ free(mp->sym_array[nsyms].name);
+ free(mp->sym_array);
+ }
+
+ free(sym_array_modules);
+ sym_array_modules = (struct Module *) 0;
+ num_modules = 0;
+ return;
+}
+
+
+/**************************************************************************
+ * Function: AddModule
+ *
+ * Purpose: This function is responsible for adding a module to
+ * the list of currently loaded modules.
+ *
+ * Arguements: (unsigned long) address, (char *) symbol
+ *
+ * address:-> The address of the module.
+ *
+ * symbol:-> The name of the module.
+ *
+ * Return: int
+ **************************************************************************/
+
+static int AddModule(address, symbol)
+
+ unsigned long address;
+
+ char *symbol;
+
+{
+ auto int memfd;
+
+ auto struct Module *mp;
+
+
+ /* Return if we have loaded the modules. */
+ if ( have_modules )
+ return(1);
+
+ /*
+ * The following section of code is responsible for determining
+ * whether or not we are done reading the list of modules.
+ */
+ if ( symbol[0] == '#' )
+ {
+
+ if ( symbol[1] == '\0' )
+ {
+ /*
+ * A symbol which consists of a # sign only
+ * signifies a a resident kernel segment. When we
+ * hit one of these we are done reading the
+ * module list.
+ */
+ have_modules = 1;
+ return(1);
+ }
+ /* Allocate space for the module. */
+ sym_array_modules = (struct Module *) \
+ realloc(sym_array_modules, \
+ (num_modules+1) * sizeof(struct Module));
+ if ( sym_array_modules == (struct Module *) 0 )
+ {
+ Syslog(LOG_WARNING, "Cannot allocate Module array.\n");
+ return(0);
+ }
+ mp = &sym_array_modules[num_modules];
+
+ if ( (memfd = open("/dev/kmem", O_RDONLY)) < 0 )
+ {
+ Syslog(LOG_WARNING, "Error opening /dev/kmem\n");
+ return(0);
+ }
+ if ( lseek64(memfd, address, SEEK_SET) < 0 )
+ {
+ Syslog(LOG_WARNING, "Error seeking in /dev/kmem\n");
+ Syslog(LOG_WARNING, "Symbol %s, value %08x\n", symbol, address);
+ return(0);
+ }
+ if ( read(memfd, \
+ (char *)&sym_array_modules[num_modules].module, \
+ sizeof(struct module)) < 0 )
+ {
+ Syslog(LOG_WARNING, "Error reading module "
+ "descriptor.\n");
+ return(0);
+ }
+ close(memfd);
+
+ /* Save the module name. */
+ mp->name = (char *) malloc(strlen(&symbol[1]) + 1);
+ if ( mp->name == (char *) 0 )
+ return(0);
+ strcpy(mp->name, &symbol[1]);
+
+ mp->num_syms = 0;
+ mp->sym_array = (struct sym_table *) 0;
+ ++num_modules;
+ return(1);
+ }
+ else
+ {
+ if (num_modules > 0)
+ mp = &sym_array_modules[num_modules - 1];
+ else
+ mp = &sym_array_modules[0];
+ AddSymbol(mp, address, symbol);
+ }
+
+
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: AddSymbol
+ *
+ * Purpose: This function is responsible for adding a symbol name
+ * and its address to the symbol table.
+ *
+ * Arguements: (struct Module *) mp, (unsigned long) address, (char *) symbol
+ *
+ * mp:-> A pointer to the module which the symbol is
+ * to be added to.
+ *
+ * address:-> The address of the symbol.
+ *
+ * symbol:-> The name of the symbol.
+ *
+ * Return: int
+ *
+ * A boolean value is assumed. True if the addition is
+ * successful. False if not.
+ **************************************************************************/
+
+static int AddSymbol(mp, address, symbol)
+
+ struct Module *mp;
+
+ unsigned long address;
+
+ char *symbol;
+
+{
+ auto int tmp;
+
+
+ /* Allocate space for the symbol table entry. */
+ mp->sym_array = (struct sym_table *) realloc(mp->sym_array, \
+ (mp->num_syms+1) * sizeof(struct sym_table));
+ if ( mp->sym_array == (struct sym_table *) 0 )
+ return(0);
+
+ /* Then the space for the symbol. */
+ tmp = strlen(symbol);
+ tmp += (strlen(mp->name) + 1);
+ mp->sym_array[mp->num_syms].name = (char *) malloc(tmp + 1);
+ if ( mp->sym_array[mp->num_syms].name == (char *) 0 )
+ return(0);
+ memset(mp->sym_array[mp->num_syms].name, '\0', tmp + 1);
+
+ /* Stuff interesting information into the module. */
+ mp->sym_array[mp->num_syms].value = address;
+ strcpy(mp->sym_array[mp->num_syms].name, mp->name);
+ strcat(mp->sym_array[mp->num_syms].name, ":");
+ strcat(mp->sym_array[mp->num_syms].name, symbol);
+ ++mp->num_syms;
+
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: LookupModuleSymbol
+ *
+ * Purpose: Find the symbol which is related to the given address from
+ * a kernel module.
+ *
+ * Arguements: (long int) value, (struct symbol *) sym
+ *
+ * value:-> The address to be located.
+ *
+ * sym:-> A pointer to a structure which will be
+ * loaded with the symbol's parameters.
+ *
+ * Return: (char *)
+ *
+ * If a match cannot be found a diagnostic string is printed.
+ * If a match is found the pointer to the symbolic name most
+ * closely matching the address is returned.
+ **************************************************************************/
+
+extern char * LookupModuleSymbol(value, sym)
+
+ unsigned long value;
+
+ struct symbol *sym;
+
+{
+ auto int nmod,
+ nsym;
+
+ auto struct sym_table *last;
+
+ auto struct Module *mp;
+
+
+ sym->size = 0;
+ sym->offset = 0;
+ if ( num_modules == 0 )
+ return((char *) 0);
+
+ for(nmod= 0; nmod < num_modules; ++nmod)
+ {
+ mp = &sym_array_modules[nmod];
+
+ /*
+ * Run through the list of symbols in this module and
+ * see if the address can be resolved.
+ */
+ for(nsym= 1, last = &mp->sym_array[0];
+ nsym < mp->num_syms;
+ ++nsym)
+ {
+ if ( mp->sym_array[nsym].value > value )
+ {
+ sym->offset = value - last->value;
+ sym->size = mp->sym_array[nsym].value - \
+ last->value;
+ return(last->name);
+ }
+ last = &mp->sym_array[nsym];
+ }
+
+
+ /*
+ * At this stage of the game we still cannot give up the
+ * ghost. There is the possibility that the address is
+ * from a module which has no symbols registered with
+ * the kernel. The solution is to compare the address
+ * against the starting address and extant of the module
+ * If it is in this range we can at least return the
+ * name of the module.
+ */
+#if LINUX_VERSION_CODE < 0x20112
+ if ( (void *) value >= mp->module.addr &&
+ (void *) value <= (mp->module.addr + \
+ mp->module.size * 4096) )
+#else
+ if ( value >= mp->module_info.addr &&
+ value <= (mp->module_info.addr + \
+ mp->module.size * 4096) )
+#endif
+ {
+ /*
+ * A special case needs to be checked for. The above
+ * conditional tells us that we are within the
+ * extant of this module but symbol lookup has
+ * failed.
+ *
+ * We need to check to see if any symbols have
+ * been defined in this module. If there have been
+ * symbols defined the assumption must be made that
+ * the faulting address lies somewhere beyond the
+ * last symbol. About the only thing we can do
+ * at this point is use an offset from this
+ * symbol.
+ */
+ if ( mp->num_syms > 0 )
+ {
+ last = &mp->sym_array[mp->num_syms - 1];
+#if LINUX_VERSION_CODE < 0x20112
+ sym->size = (int) mp->module.addr + \
+ (mp->module.size * 4096) - value;
+#else
+ sym->size = (int) mp->module_info.addr + \
+ (mp->module.size * 4096) - value;
+#endif
+ sym->offset = value - last->value;
+ return(last->name);
+ }
+
+ /*
+ * There were no symbols defined for this module.
+ * Return the module name and the offset of the
+ * faulting address in the module.
+ */
+ sym->size = mp->module.size * 4096;
+#if LINUX_VERSION_CODE < 0x20112
+ sym->offset = (void *) value - mp->module.addr;
+#else
+ sym->offset = value - mp->module_info.addr;
+#endif
+ return(mp->name);
+ }
+ }
+
+ /* It has been a hopeless exercise. */
+ return((char *) 0);
+}
+
+
+/*
+ * Setting the -DTEST define enables the following code fragment to
+ * be compiled. This produces a small standalone program which will
+ * dump the current kernel symbol table.
+ */
+#if defined(TEST)
+
+#include <stdarg.h>
+
+
+extern int main(int, char **);
+
+
+int main(argc, argv)
+
+ int argc;
+
+ char *argv[];
+
+{
+ auto int lp, syms;
+
+
+ if ( !InitMsyms() )
+ {
+ fprintf(stderr, "Cannot load module symbols.\n");
+ return(1);
+ }
+
+ printf("Number of modules: %d\n\n", num_modules);
+
+ for(lp= 0; lp < num_modules; ++lp)
+ {
+ printf("Module #%d = %s, Number of symbols = %d\n", lp + 1, \
+ sym_array_modules[lp].name, \
+ sym_array_modules[lp].num_syms);
+
+ for (syms= 0; syms < sym_array_modules[lp].num_syms; ++syms)
+ {
+ printf("\tSymbol #%d\n", syms + 1);
+ printf("\tName: %s\n", \
+ sym_array_modules[lp].sym_array[syms].name);
+ printf("\tAddress: %lx\n\n", \
+ sym_array_modules[lp].sym_array[syms].value);
+ }
+ }
+
+ FreeModules();
+ return(0);
+}
+
+extern void Syslog(int priority, char *fmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stdout, "Pr: %d, ", priority);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ fputc('\n', stdout);
+
+ return;
+}
+
+#endif
+#endif /* #ifdef FEATURE_KLOGD */
diff --git a/plugins/imklog/ksyms.h b/plugins/imklog/ksyms.h
new file mode 100644
index 00000000..316950a0
--- /dev/null
+++ b/plugins/imklog/ksyms.h
@@ -0,0 +1,38 @@
+/*
+ ksym.h - Definitions for symbol table utilities.
+ Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ Copyright (c) 1996 Enjellic Systems Development
+
+ This file is part of the sysklogd package, a kernel and system log daemon.
+
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/* Variables, structures and type definitions static to this module. */
+
+struct symbol
+{
+ char *name;
+ int size;
+ int offset;
+};
+
+
+/* Function prototypes. */
+extern char * LookupSymbol(unsigned long, struct symbol *);
+extern char * LookupModuleSymbol(unsigned long int, struct symbol *);
diff --git a/plugins/imklog/module.h b/plugins/imklog/module.h
new file mode 100644
index 00000000..71eac2c4
--- /dev/null
+++ b/plugins/imklog/module.h
@@ -0,0 +1,81 @@
+/* Module definitions for klogd's module support
+ *
+ * Copyright 2007 by Rainer Gerhards and others
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+struct kernel_sym
+{
+ unsigned long value;
+ char name[60];
+};
+
+struct module_symbol
+{
+ unsigned long value;
+ const char *name;
+};
+
+struct module_ref
+{
+ struct module *dep; /* "parent" pointer */
+ struct module *ref; /* "child" pointer */
+ struct module_ref *next_ref;
+};
+
+struct module_info
+{
+ unsigned long addr;
+ unsigned long size;
+ unsigned long flags;
+ long usecount;
+};
+
+
+typedef struct { volatile int counter; } atomic_t;
+
+struct module
+{
+ unsigned long size_of_struct; /* == sizeof(module) */
+ struct module *next;
+ const char *name;
+ unsigned long size;
+
+ union
+ {
+ atomic_t usecount;
+ long pad;
+ } uc; /* Needs to keep its size - so says rth */
+
+ unsigned long flags; /* AUTOCLEAN et al */
+
+ unsigned nsyms;
+ unsigned ndeps;
+
+ struct module_symbol *syms;
+ struct module_ref *deps;
+ struct module_ref *refs;
+ int (*init)(void);
+ void (*cleanup)(void);
+ const struct exception_table_entry *ex_table_start;
+ const struct exception_table_entry *ex_table_end;
+#ifdef __alpha__
+ unsigned long gp;
+#endif
+};
+