diff options
Diffstat (limited to 'plugins/imklog/bsd.c')
-rw-r--r-- | plugins/imklog/bsd.c | 244 |
1 files changed, 165 insertions, 79 deletions
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c index 0a4c7cd4..eaf8e5ca 100644 --- a/plugins/imklog/bsd.c +++ b/plugins/imklog/bsd.c @@ -1,69 +1,30 @@ -/* klog for BSD, based on the FreeBSD syslogd implementation. +/* combined imklog driver for BSD and Linux * * This contains OS-specific functionality to read the BSD - * kernel log. For a general overview, see head comment in - * imklog.c. + * or Linux kernel log. For a general overview, see head comment in + * imklog.c. This started out as the BSD-specific drivers, but it + * turned out that on modern Linux the implementation details + * are very small, and so we use a single driver for both OS's with + * a little help of conditional compilation. * - * Copyright (C) 2008 by Rainer Gerhards for the modifications of - * the original FreeBSD sources. - * - * I would like to express my gratitude to those folks which - * layed an important foundation for rsyslog to build on. + * Copyright 2008-2012 Adiscon GmbH * * 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 is based on earlier work included in the FreeBSD sources. We - * integrated it into the rsyslog project. The copyright below applies, and - * I also reproduce the original license under which we aquired the code: - * - * Copyright (c) 1983, 1988, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * If you would like to use the code under the BSD license, you should - * aquire your own copy of BSD's syslogd, from which we have taken it. The - * code in this file is modified and may only be used under the terms of - * the GPLv3+ as specified above. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -72,18 +33,124 @@ #include <fcntl.h> #include <errno.h> #include <string.h> +#include <ctype.h> +#ifdef OS_LINUX +# include <sys/klog.h> +#endif #include "rsyslog.h" -#include "imklog.h" +#include "srUtils.h" #include "debug.h" +#include "imklog.h" /* globals */ -static int fklog = -1; /* /dev/klog */ +static int fklog = -1; /* kernel log fd */ #ifndef _PATH_KLOG -# define _PATH_KLOG "/dev/klog" +# ifdef OS_LINUX +# define _PATH_KLOG "/proc/kmsg" +# else +# define _PATH_KLOG "/dev/klog" +# endif #endif + +#ifdef OS_LINUX +/* submit a message to imklog Syslog() API. In this function, we check if + * a kernel timestamp is present and, if so, extract and strip it. + * Note: this is an extra processing step. We should revisit the whole + * idea in v6 and remove all that old stuff that we do not longer need + * (like symbol resolution). <-- TODO + * Note that this is heavily Linux specific and thus is not compiled or + * used for BSD. + * Special thanks to Lennart Poettering for suggesting on how to convert + * the kernel timestamp to a realtime timestamp. This method depends on + * the fact the the kernel timestamp is written using the monotonic clock. + * Shall that change (very unlikely), this code must be changed as well. Note + * that due to the way we generate the delta, we are unable to write the + * absolutely correct timestamp (system call overhead of the clock calls + * prevents us from doing so). However, the difference is very minor. + * rgerhards, 2011-06-24 + */ +static void +submitSyslog(int pri, uchar *buf) +{ + long secs; + long nsecs; + long secOffs; + long nsecOffs; + unsigned i; + unsigned bufsize; + struct timespec monotonic, realtime; + struct timeval tv; + struct timeval *tp = NULL; + + if(buf[3] != '[') + goto done; + DBGPRINTF("imklog: kernel timestamp detected, extracting it\n"); + + /* we now try to parse the timestamp. iff it parses, we assume + * it is a timestamp. Otherwise we know for sure it is no ts ;) + */ + i = 4; /* first digit after '[' */ + secs = 0; + while(buf[i] && isdigit(buf[i])) { + secs = secs * 10 + buf[i] - '0'; + ++i; + } + if(buf[i] != '.') { + DBGPRINTF("no dot --> no kernel timestamp\n"); + goto done; /* no TS! */ + } + + ++i; /* skip dot */ + nsecs = 0; + while(buf[i] && isdigit(buf[i])) { + nsecs = nsecs * 10 + buf[i] - '0'; + ++i; + } + if(buf[i] != ']') { + DBGPRINTF("no trailing ']' --> no kernel timestamp\n"); + goto done; /* no TS! */ + } + ++i; /* skip ']' */ + + /* we have a timestamp */ + DBGPRINTF("kernel timestamp is %ld %ld\n", secs, nsecs); + bufsize= strlen((char*)buf); + memcpy(buf+3, buf+i, bufsize - i + 1); + + clock_gettime(CLOCK_MONOTONIC, &monotonic); + clock_gettime(CLOCK_REALTIME, &realtime); + secOffs = realtime.tv_sec - monotonic.tv_sec; + nsecOffs = realtime.tv_nsec - monotonic.tv_nsec; + if(nsecOffs < 0) { + secOffs--; + nsecOffs += 1000000000l; + } + + nsecs +=nsecOffs; + if(nsecs > 999999999l) { + secs++; + nsecs -= 1000000000l; + } + secs += secOffs; + tv.tv_sec = secs; + tv.tv_usec = nsecs / 1000; + tp = &tv; + +done: + Syslog(pri, buf, tp); +} +#else /* now comes the BSD "code" (just a shim) */ +static void +submitSyslog(int pri, uchar *buf) +{ + Syslog(pri, buf, NULL); +} +#endif /* #ifdef LINUX */ + + static uchar *GetPath(void) { return pszPath ? pszPath : (uchar*) _PATH_KLOG; @@ -95,23 +162,36 @@ static uchar *GetPath(void) rsRetVal klogWillRun(void) { + char errmsg[2048]; + int r; DEFiRet; fklog = open((char*)GetPath(), O_RDONLY, 0); if (fklog < 0) { - dbgprintf("can't open %s (%d)\n", GetPath(), errno); - iRet = RS_RET_ERR; // TODO: better error code + imklogLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imklog: cannot open kernel log(%s): %s.", + GetPath(), rs_strerror_r(errno, errmsg, sizeof(errmsg))); + ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); + } + +# ifdef OS_LINUX + /* Set level of kernel console messaging.. */ + if(console_log_level != -1) { + r = klogctl(8, NULL, console_log_level); + if(r != 0) { + imklogLogIntMsg(LOG_WARNING, "imklog: cannot set console log level: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); + /* make sure we do not try to re-set! */ + console_log_level = -1; + } } +# endif /* #ifdef OS_LINUX */ +finalize_it: RETiRet; } -/* Read /dev/klog while data are available, split into lines. - * Contrary to standard BSD syslogd, we do a blocking read. We can - * afford this as imklog is running on its own threads. So if we have - * a single file, it really doesn't matter if we wait inside a 1-file - * select or the read() directly. +/* Read kernel log while data are available, split into lines. */ static void readklog(void) @@ -119,13 +199,14 @@ readklog(void) char *p, *q; int len, i; int iMaxLine; - uchar bufRcv[4096+1]; + uchar bufRcv[128*1024+1]; + char errmsg[2048]; uchar *pRcv = NULL; /* receive buffer */ iMaxLine = klog_getMaxLine(); - /* we optimize performance: if iMaxLine is below 4K (which it is in almost all - * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory + /* we optimize performance: if iMaxLine is below our fixed size buffer (which + * usually is sufficiently large), we use this buffer. if it is higher, heap memory * is used. We could use alloca() to achive a similar aspect, but there are so * many issues with alloca() that I do not want to take that route. * rgerhards, 2008-09-02 @@ -139,15 +220,15 @@ readklog(void) len = 0; for (;;) { - dbgprintf("----------imklog(BSD) waiting for kernel log line\n"); + dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n"); i = read(fklog, pRcv + len, iMaxLine - len); if (i > 0) { pRcv[i + len] = '\0'; } else { if (i < 0 && errno != EINTR && errno != EAGAIN) { imklogLogIntMsg(LOG_ERR, - "imklog error %d reading kernel log - shutting down imklog", - errno); + "imklog: error reading kernel log - shutting down: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); fklog = -1; } break; @@ -155,18 +236,18 @@ readklog(void) for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) { *q = '\0'; - Syslog(LOG_INFO, (uchar*) p); + submitSyslog(LOG_INFO, (uchar*) p); } len = strlen(p); if (len >= iMaxLine - 1) { - Syslog(LOG_INFO, (uchar*)p); + submitSyslog(LOG_INFO, (uchar*)p); len = 0; } - if (len > 0) + if(len > 0) memmove(pRcv, p, len + 1); } if (len > 0) - Syslog(LOG_INFO, pRcv); + submitSyslog(LOG_INFO, pRcv); if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) free(pRcv); @@ -181,6 +262,11 @@ rsRetVal klogAfterRun(void) DEFiRet; if(fklog != -1) close(fklog); +# ifdef OS_LINUX + /* Turn on logging of messages to console, but only if a log level was speficied */ + if(console_log_level != -1) + klogctl(7, NULL, 0); +# endif RETiRet; } |