diff options
author | Andre Lorbach <alorbach@adiscon.com> | 2013-06-18 13:51:00 +0200 |
---|---|---|
committer | Andre Lorbach <alorbach@adiscon.com> | 2013-06-18 13:51:00 +0200 |
commit | 95d69e9b1f846458d95d4ce74da2ae151a49fb39 (patch) | |
tree | 7d167a47e839614384fbf4894663cb5c6f1b1db3 /plugins/imklog/bsd.c | |
parent | c4fc57f4c551602e36091040b77bc5e22e299122 (diff) | |
parent | 520f0325e7c10b6a47f721ac353a6036d554cbc3 (diff) | |
download | rsyslog-95d69e9b1f846458d95d4ce74da2ae151a49fb39.tar.gz rsyslog-95d69e9b1f846458d95d4ce74da2ae151a49fb39.tar.bz2 rsyslog-95d69e9b1f846458d95d4ce74da2ae151a49fb39.zip |
Merge remote branch 'upstream/master'
Diffstat (limited to 'plugins/imklog/bsd.c')
-rw-r--r-- | plugins/imklog/bsd.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c new file mode 100644 index 00000000..9c2eebb2 --- /dev/null +++ b/plugins/imklog/bsd.c @@ -0,0 +1,298 @@ +/* combined imklog driver for BSD and Linux + * + * This contains OS-specific functionality to read the BSD + * 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 2008-2012 Adiscon GmbH + * + * This file is part of rsyslog. + * + * 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 +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#ifdef OS_LINUX +# include <sys/klog.h> +#endif + +#include "rsyslog.h" +#include "srUtils.h" +#include "debug.h" +#include "imklog.h" + +/* globals */ +static int fklog = -1; /* kernel log fd */ + +#ifndef _PATH_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 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(modConfData_t *pModConf, int pri, uchar *buf) +{ + long secs; + long usecs; + long secOffs; + long usecOffs; + unsigned i; + unsigned bufsize; + struct timespec monotonic, realtime; + struct timeval tv; + struct timeval *tp = NULL; + + if(!pModConf->bParseKernelStamp) + goto done; + + 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; /* space or first digit after '[' */ + while(buf[i] && isspace(buf[i])) + ++i; /* skip space */ + 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 */ + usecs = 0; + while(buf[i] && isdigit(buf[i])) { + usecs = usecs * 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, usecs); + if(!pModConf->bKeepKernelStamp) { + bufsize= strlen((char*)buf); + memmove(buf+3, buf+i, bufsize - i + 1); + } + + clock_gettime(CLOCK_MONOTONIC, &monotonic); + clock_gettime(CLOCK_REALTIME, &realtime); + secOffs = realtime.tv_sec - monotonic.tv_sec; + usecOffs = (realtime.tv_nsec - monotonic.tv_nsec) / 1000; + if(usecOffs < 0) { + secOffs--; + usecOffs += 1000000l; + } + + usecs += usecOffs; + if(usecs > 999999l) { + secs++; + usecs -= 1000000l; + } + secs += secOffs; + tv.tv_sec = secs; + tv.tv_usec = usecs; + tp = &tv; + +done: + Syslog(pri, buf, tp); +} +#else /* now comes the BSD "code" (just a shim) */ +static void +submitSyslog(modConfData_t *pModConf, int pri, uchar *buf) +{ + Syslog(pri, buf, NULL); +} +#endif /* #ifdef LINUX */ + + +static uchar *GetPath(modConfData_t *pModConf) +{ + return pModConf->pszPath ? pModConf->pszPath : (uchar*) _PATH_KLOG; +} + +/* open the kernel log - will be called inside the willRun() imklog + * entry point. -- rgerhards, 2008-04-09 + */ +rsRetVal +klogWillRun(modConfData_t *pModConf) +{ + char errmsg[2048]; + int r; + DEFiRet; + + fklog = open((char*)GetPath(pModConf), O_RDONLY, 0); + if (fklog < 0) { + imklogLogIntMsg(LOG_ERR, "imklog: cannot open kernel log(%s): %s.", + GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); + ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); + } + +# ifdef OS_LINUX + /* Set level of kernel console messaging.. */ + if(pModConf->console_log_level != -1) { + r = klogctl(8, NULL, pModConf->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! */ + pModConf->console_log_level = -1; + } + } +# endif /* #ifdef OS_LINUX */ + +finalize_it: + RETiRet; +} + + +/* Read kernel log while data are available, split into lines. + */ +static void +readklog(modConfData_t *pModConf) +{ + char *p, *q; + int len, i; + int iMaxLine; + uchar bufRcv[128*1024+1]; + char errmsg[2048]; + uchar *pRcv = NULL; /* receive buffer */ + + iMaxLine = klog_getMaxLine(); + + /* 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 + */ + if((size_t) iMaxLine < sizeof(bufRcv) - 1) { + pRcv = bufRcv; + } else { + if((pRcv = (uchar*) MALLOC(sizeof(uchar) * (iMaxLine + 1))) == NULL) + iMaxLine = sizeof(bufRcv) - 1; /* better this than noting */ + } + + len = 0; + for (;;) { + 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 reading kernel log - shutting down: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); + fklog = -1; + } + break; + } + + for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) { + *q = '\0'; + submitSyslog(pModConf, LOG_INFO, (uchar*) p); + } + len = strlen(p); + if (len >= iMaxLine - 1) { + submitSyslog(pModConf, LOG_INFO, (uchar*)p); + len = 0; + } + if(len > 0) + memmove(pRcv, p, len + 1); + } + if (len > 0) + submitSyslog(pModConf, LOG_INFO, pRcv); + + if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) + free(pRcv); +} + + +/* to be called in the module's AfterRun entry point + * rgerhards, 2008-04-09 + */ +rsRetVal klogAfterRun(modConfData_t *pModConf) +{ + 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(pModConf->console_log_level != -1) + klogctl(7, NULL, 0); +# endif + RETiRet; +} + + + +/* to be called in the module's WillRun entry point, this is the main + * "message pull" mechanism. + * rgerhards, 2008-04-09 + */ +rsRetVal klogLogKMsg(modConfData_t *pModConf) +{ + DEFiRet; + readklog(pModConf); + RETiRet; +} + + +/* provide the (system-specific) default facility for internal messages + * rgerhards, 2008-04-14 + */ +int +klogFacilIntMsg(void) +{ + return LOG_SYSLOG; +} |