1
/* The kernel log input module for Linux. This file heavily
2
* borrows from the klogd daemon provided by the sysklogd project.
3
* Many thanks for this piece of software.
5
* Please note that this file replaces the klogd daemon that was
6
* also present in pre-v3 versions of rsyslog.
8
* I have begun to convert this to an input module on 2007-12-17.
9
* IMPORTANT: more than a single instance is currently not supported. This
10
* needs to be revisited once the config file and input module interface
11
* supports multiple instances!
13
* This file is part of rsyslog.
15
* Rsyslog is free software: you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation, either version 3 of the License, or
18
* (at your option) any later version.
20
* Rsyslog is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
28
* A copy of the GPL can be found in the file "COPYING" in this distribution.
39
#include "cfsysline.h"
42
#include "module-template.h"
47
/* Module static data */
50
/* configuration settings TODO: move to instance data? */
51
int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */
52
static int symbols_twice = 0;
53
static int use_syscall = 0;
54
static int symbol_lookup = 1;
55
/* TODO: configuration for the following directives must be implemented. It
56
* was not done yet because we either do not yet have a config handler for
57
* that type or I thought it was acceptable to push it to a later stage when
58
* I gained more handson experience with the input module interface (and the
59
* changes resulting from that). -- rgerhards, 2007-12-20
61
static char *symfile = NULL;
62
static int console_log_level = -1;
68
#include <sys/fcntl.h>
83
#if !defined(__GLIBC__)
84
# define __NR_ksyslog __NR_syslog
85
_syscall3(int,ksyslog,int, type, char *, buf, int, len);
88
#define ksyslog klogctl
94
#define _PATH_KLOG "/proc/kmsg"
97
#define LOG_BUFFER_SIZE 4096
98
#define LOG_LINE_LENGTH 1000
101
static char log_buffer[LOG_BUFFER_SIZE];
103
static enum LOGSRC {none, proc, kernel} logsrc;
107
/* Function prototypes. */
108
extern int ksyslog(int type, char *buf, int len);
111
/* Write a message to the message queue.
112
* returns -1 if it fails, something else otherwise
114
static rsRetVal writeSyslogV(int iPRI, const char *szFmt, va_list va)
120
char msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */
122
assert(szFmt != NULL);
124
/* build the message */
126
/* we can use sprintf safely below, because we know the size of the constants.
127
* By doing so, we save some cpu cycles and code complexity (for unnecessary
130
iLen = sprintf(msgBuf, "<%d>%.15s kernel: ", iPRI, ctime(&tNow) + 4);
132
iChars = vsnprintf(msgBuf + iLen, sizeof(msgBuf) / sizeof(char) - iLen, szFmt, va);
134
/* here we must create our message object and supply it to the message queue
136
CHKiRet(parseAndSubmitMessage(LocalHostName, msgBuf, strlen(msgBuf), MSG_DONT_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY));
142
/* And now the same with variable arguments */
143
static int writeSyslog(int iPRI, const char *szFmt, ...)
148
assert(szFmt != NULL);
150
iRet = writeSyslogV(iPRI, szFmt, va);
156
rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
157
rsRetVal Syslog(int priority, char *fmt, ...)
163
/* Output using syslog. */
164
if (!strcmp(fmt, "%s")) {
166
argl = va_arg(ap, char *);
167
if (argl[0] == '<' && argl[1] && argl[2] == '>') {
171
priority = LOG_EMERG;
174
priority = LOG_ALERT;
183
priority = LOG_WARNING;
186
priority = LOG_NOTICE;
193
priority = LOG_DEBUG;
197
iRet = writeSyslog(priority, fmt, argl);
201
iRet = writeSyslogV(priority, fmt, ap);
209
static void CloseLogSrc(void)
211
/* Turn on logging of messages to console, but only if we had the -c
212
* option -- rgerhards, 2007-08-01
214
if (console_log_level != -1)
217
/* Shutdown the log sources. */
222
Syslog(LOG_INFO, "Kernel logging (ksyslog) stopped.");
226
Syslog(LOG_INFO, "Kernel logging (proc) stopped.");
236
static enum LOGSRC GetKernelLogSrc(void)
240
/* Set level of kernel console messaging.. */
241
if ( (console_log_level != -1) &&
242
(ksyslog(8, NULL, console_log_level) < 0) &&
246
* An invalid arguement error probably indicates that
247
* a pre-0.14 kernel is being run. At this point we
248
* issue an error message and simply shut-off console
249
* logging completely.
251
Syslog(LOG_WARNING, "Cannot set console log level - disabling "
256
* First do a stat to determine whether or not the proc based
257
* file system is available to get kernel messages from.
260
((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)) )
262
/* Initialize kernel logging. */
264
Syslog(LOG_INFO, "imklogd %s, log source = ksyslog "
265
"started.", VERSION);
269
if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 )
272
snprintf(sz, sizeof(sz), "imklog: Cannot open proc file system, %d - %s.\n", errno, strerror(errno));
273
logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE);
274
ksyslog(7, NULL, 0); /* TODO: check this, implement more */
278
Syslog(LOG_INFO, "imklog %s, log source = %s started.", \
279
VERSION, _PATH_KLOG);
284
/* Copy characters from ptr to line until a char in the delim
285
* string is encountered or until min( space, len ) chars have
288
* Returns the actual number of chars copied.
290
static int copyin( char *line, int space,
291
const char *ptr, int len,
297
count = len < space ? len : space;
299
for(i=0; i<count && !strchr(delim, *ptr); i++ ) {
307
* Messages are separated by "\n". Messages longer than
308
* LOG_LINE_LENGTH are broken up.
310
* Kernel symbols show up in the input buffer as : "[<aaaaaa>]",
311
* where "aaaaaa" is the address. These are replaced with
312
* "[symbolname+offset/size]" in the output line - symbolname,
313
* offset, and size come from the kernel symbol table.
315
* If a kernel symbol happens to fall at the end of a message close
316
* in length to LOG_LINE_LENGTH, the symbol will not be expanded.
317
* (This should never happen, since the kernel should never generate
318
* messages that long.
320
* To preserve the original addresses, lines containing kernel symbols
321
* are output twice. Once with the symbols converted and again with the
322
* original text. Just in case somebody wants to run their own Oops
323
* analysis on the syslog, e.g. ksymoops.
325
static void LogLine(char *ptr, int len)
327
enum parse_state_enum {
329
PARSING_SYMSTART, /* at < */
331
PARSING_SYMEND /* at ] */
334
static char line_buff[LOG_LINE_LENGTH];
336
static char *line =line_buff;
337
static enum parse_state_enum parse_state = PARSING_TEXT;
338
static int space = sizeof(line_buff)-1;
340
static char *sym_start; /* points at the '<' of a symbol */
342
auto int delta = 0; /* number of chars copied */
343
auto int symbols_expanded = 0; /* 1 if symbols were expanded */
344
auto int skip_symbol_lookup = 0; /* skip symbol lookup on this pass */
345
auto char *save_ptr = ptr; /* save start of input line */
346
auto int save_len = len; /* save length at start of input line */
350
if( space == 0 ) /* line buffer is full */
353
** Line too long. Start a new line.
355
*line = 0; /* force null terminator */
357
dbgprintf("Line buffer full:\n");
358
dbgprintf("\tLine: %s\n", line);
360
Syslog( LOG_INFO, "%s", line_buff );
362
space = sizeof(line_buff)-1;
363
parse_state = PARSING_TEXT;
364
symbols_expanded = 0;
365
skip_symbol_lookup = 0;
370
switch( parse_state )
373
delta = copyin( line, space, ptr, len, "\n[" );
379
if( space == 0 || len == 0 )
381
break; /* full line_buff or end of input buffer */
384
if( *ptr == '\0' ) /* zero byte */
386
ptr++; /* skip zero byte */
393
if( *ptr == '\n' ) /* newline */
395
ptr++; /* skip newline */
399
*line = 0; /* force null terminator */
400
Syslog( LOG_INFO, "%s", line_buff );
402
space = sizeof(line_buff)-1;
404
if (symbols_expanded) {
405
/* reprint this line without symbol lookup */
406
symbols_expanded = 0;
407
skip_symbol_lookup = 1;
413
skip_symbol_lookup = 0;
420
if( *ptr == '[' ) /* possible kernel symbol */
425
if (!skip_symbol_lookup)
426
parse_state = PARSING_SYMSTART; /* at < */
429
/* Now that line_buff is no longer fed to *printf as format
430
* string, '%'s are no longer "dangerous".
434
case PARSING_SYMSTART:
437
parse_state = PARSING_TEXT; /* not a symbol */
442
** Save this character for now. If this turns out to
443
** be a valid symbol, this char will be replaced later.
444
** If not, we'll just leave it there.
447
sym_start = line; /* this will point at the '<' */
452
parse_state = PARSING_SYMBOL; /* symbol... */
456
delta = copyin( line, space, ptr, len, ">\n[" );
461
if( space == 0 || len == 0 )
463
break; /* full line_buff or end of input buffer */
467
parse_state = PARSING_TEXT;
471
*line++ = *ptr++; /* copy the '>' */
475
parse_state = PARSING_SYMEND;
482
parse_state = PARSING_TEXT; /* not a symbol */
487
** It's really a symbol! Replace address with the
494
auto struct symbol sym;
497
*(line-1) = 0; /* null terminate the address string */
498
value = strtoul(sym_start+1, (char **) 0, 16);
499
*(line-1) = '>'; /* put back delim */
501
if ( !symbol_lookup || (symbol = LookupSymbol(value, &sym)) == (char *)0 )
503
parse_state = PARSING_TEXT;
508
** verify there is room in the line buffer
510
sym_space = space + ( line - sym_start );
511
if( (unsigned) sym_space < strlen(symbol) + 30 ) /*(30 should be overkill)*/
513
parse_state = PARSING_TEXT; /* not enough space */
517
delta = sprintf( sym_start, "%s+%d/%d]",
518
symbol, sym.offset, sym.size );
520
space = sym_space + delta;
521
line = sym_start + delta;
522
symbols_expanded = 1;
526
parse_state = PARSING_TEXT;
529
default: /* Can't get here! */
530
parse_state = PARSING_TEXT;
539
static void LogKernelLine(void)
544
* Zero-fill the log buffer. This should cure a multitude of
545
* problems with klogd logging the tail end of the message buffer
546
* which will contain old messages. Then read the kernel log
547
* messages into this fresh buffer.
549
memset(log_buffer, '\0', sizeof(log_buffer));
550
if ( (rdcnt = ksyslog(2, log_buffer, sizeof(log_buffer)-1)) < 0 )
555
snprintf(sz, sizeof(sz), "imklog: Error return from sys_sycall: %d - %s\n", errno, strerror(errno));
556
logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE);
559
LogLine(log_buffer, rdcnt);
564
static void LogProcLine(void)
569
* Zero-fill the log buffer. This should cure a multitude of
570
* problems with klogd logging the tail end of the message buffer
571
* which will contain old messages. Then read the kernel messages
572
* from the message pseudo-file into this fresh buffer.
574
memset(log_buffer, '\0', sizeof(log_buffer));
575
if ( (rdcnt = read(kmsg, log_buffer, sizeof(log_buffer)-1)) < 0 )
577
if ( errno == EINTR )
579
Syslog(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno));
582
LogLine(log_buffer, rdcnt);
590
/* this is an endless loop - it is terminated when the thread is
591
* signalled to do so. This, however, is handled by the framework,
592
* right into the sleep below.
594
while(!pThrd->bShallStop) {
595
/* we do not need to handle the RS_RET_TERMINATE_NOW case any
596
* special because we just need to terminate. This may be different
597
* if a cleanup is needed. But for now, we can just use CHKiRet().
598
* rgerhards, 2007-12-17
609
/* TODO: We need to handle this case here somewhat more intelligent
610
* This is now at least partly done - code should never reach this point
611
* as willRun() already checked for the "none" status -- rgerhards, 2007-12-17
622
/* Initialize this module. If that fails, we tell the engine we don't like to run */
623
/* Determine where kernel logging information is to come from. */
624
logsrc = GetKernelLogSrc();
626
iRet = RS_RET_NO_KERNEL_LOGSRC;
629
symbol_lookup = (InitKsyms(symfile) == 1);
630
symbol_lookup |= InitMsyms();
631
if (symbol_lookup == 0) {
632
Syslog(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
658
CODEqueryEtryPt_STD_IMOD_QUERIES
661
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
673
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
674
CODEmodInit_QueryRegCFSLineHdlr
675
CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, NULL, &dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
676
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary, NULL, &symbol_lookup, STD_LOADABLE_MODULE_ID));
677
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary, NULL, &symbols_twice, STD_LOADABLE_MODULE_ID));
678
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary, NULL, &use_syscall, STD_LOADABLE_MODULE_ID));
679
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));