From 6e3980abbdb5d98a66d656456f993374b0be9565 Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Tue, 11 Sep 2012 10:10:37 +0200 Subject: Added new module, imkmsg, for structured kernel logs from /dev/kmsg. This is still in development, bute ready to be commited to master. modified: Makefile.am modified: configure.ac new file: plugins/imkmsg/Makefile.am new file: plugins/imkmsg/imkmsg.c new file: plugins/imkmsg/imkmsg.h new file: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 plugins/imkmsg/kmsg.c (limited to 'plugins/imkmsg/kmsg.c') diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c new file mode 100644 index 00000000..2513b2ad --- /dev/null +++ b/plugins/imkmsg/kmsg.c @@ -0,0 +1,257 @@ +/* imkmsg driver for Linux /dev/kmsg structured logging + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "rsyslog.h" +#include "srUtils.h" +#include "debug.h" +#include "imkmsg.h" + +/* globals */ +static int fklog = -1; /* kernel log fd */ + +#ifndef _PATH_KLOG +# define _PATH_KLOG "/dev/kmsg" +#endif + +/* submit a message to imklog Syslog() API. In this function, we parse + * necessary information from kernel log line, and make json string + * from the rest. + */ +static void +submitSyslog(uchar *buf) +{ + struct timeval tv; + long offs = 0; + long int timestamp = 0; + struct timespec monotonic; + struct timespec realtime; + char outMsg[8096]; + int priority = 0; + + offs = snprintf(outMsg, 8, "%s", "@cee: {"); + + /* get priority */ + for (; isdigit(*buf); buf++) { + priority += (priority * 10) + (*buf - '0'); + } + buf++; + + /* messages sequence number */ + offs += snprintf(outMsg+offs, 12, "%s", "\"sequnum\":\""); + for (; isdigit(*buf); buf++, offs++) { + outMsg[offs] = *buf; + } + buf++; /* skip , */ + + /* get timestamp */ + for (; isdigit(*buf); buf++) { + timestamp += (timestamp * 10) + (*buf - '0'); + } + buf++; /* skip ; */ + + offs += snprintf(outMsg+offs, 10, "%s", "\",\"msg\":\""); + + for (; *buf != '\n' && *buf != '\0'; buf++, offs++) { + outMsg[offs] = *buf; + } + + if (*buf != '\0') /* message has appended properties, skip \n */ + buf++; + + while (strlen((char *)buf)) { + offs += snprintf(outMsg+offs, 4, "%s", "\",\""); + buf++; /* skip ' ' */ + for (; *buf != '=' && *buf != ' '; buf++, offs++) { /* separator is = or ' ' */ + outMsg[offs] = *buf; + } + buf++; /* skip = */ + + offs += snprintf(outMsg+offs, 4, "%s", "\":\""); + for (; *buf != '\n' && *buf != '\0'; buf++, offs++) { + outMsg[offs] = *buf; + } + + if (*buf != '\0') + buf++; /* another property, skip \n */ + } + offs += snprintf(outMsg+offs, 3, "%s", "\"}"); + + outMsg[offs] = '\0'; + + /* calculate timestamp */ + clock_gettime(CLOCK_MONOTONIC, &monotonic); + clock_gettime(CLOCK_REALTIME, &realtime); + tv.tv_sec = realtime.tv_sec + ((timestamp / 1000000l) - monotonic.tv_sec); + tv.tv_usec = (realtime.tv_nsec + ((timestamp / 1000000000l) - monotonic.tv_nsec)) / 1000; + + Syslog(priority, (uchar *)outMsg, &tv); +} + + +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(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot open kernel log(%s): %s.", + GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); + ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); + } + + /* 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, "imkmsg: 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; + } + } + +finalize_it: + RETiRet; +} + +/* Read kernel log while data are available, split into lines. + */ +static void +readklog(void) +{ + int i; + uchar pRcv[1024+1]; /* LOG_LINE_MAX is 1024 */ + char errmsg[2048]; + +#if 0 +XXX not sure if LOG_LINE_MAX is 1024 +- int iMaxLine; +- uchar bufRcv[128*1024+1]; ++ uchar pRcv[1024+1]; /* LOG_LINE_MAX is 1024 */ + 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 */ +- } +#endif + + for (;;) { + dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n"); + + /* every read() from the opened device node receives one record of the printk buffer */ + i = read(fklog, pRcv, 1024); + + if (i > 0) { + /* successful read of message of nonzero length */ + pRcv[i] = '\0'; + } else { + /* something went wrong - error or zero length message */ + if (i < 0 && errno != EINTR && errno != EAGAIN) { + /* error occured */ + imklogLogIntMsg(LOG_ERR, + "imkmsg: error reading kernel log - shutting down: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); + fklog = -1; + } + break; + } + + submitSyslog(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); + /* 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); + 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 __attribute__((unused)) *pModConf) +{ + DEFiRet; + readklog(); + RETiRet; +} + + +/* provide the (system-specific) default facility for internal messages + * rgerhards, 2008-04-14 + */ +int +klogFacilIntMsg(void) +{ + return LOG_SYSLOG; +} -- cgit v1.2.3 From 91948f610807cb5f42d15af5448126315fd9a0dd Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Wed, 12 Sep 2012 08:25:39 +0200 Subject: Modified comments. modified: imkmsg.c modified: imkmsg.h modified: kmsg.c --- plugins/imkmsg/kmsg.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'plugins/imkmsg/kmsg.c') diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index 2513b2ad..88e00502 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -1,11 +1,8 @@ /* imkmsg driver for Linux /dev/kmsg structured logging * - * 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. + * This contains Linux-specific functionality to read /dev/kmsg + * For a general overview, see head comment in imkmsg.c. + * This is heavily based on imklog bsd.c file. * * Copyright 2008-2012 Adiscon GmbH * @@ -49,7 +46,7 @@ static int fklog = -1; /* kernel log fd */ # define _PATH_KLOG "/dev/kmsg" #endif -/* submit a message to imklog Syslog() API. In this function, we parse +/* submit a message to imkmsg Syslog() API. In this function, we parse * necessary information from kernel log line, and make json string * from the rest. */ @@ -129,8 +126,7 @@ 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 +/* open the kernel log - will be called inside the willRun() imkmsg entry point */ rsRetVal klogWillRun(modConfData_t *pModConf) @@ -141,7 +137,7 @@ klogWillRun(modConfData_t *pModConf) fklog = open((char*)GetPath(pModConf), O_RDONLY, 0); if (fklog < 0) { - imklogLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot open kernel log(%s): %s.", + imkmsgLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot open kernel log(%s): %s.", GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); } @@ -150,7 +146,7 @@ klogWillRun(modConfData_t *pModConf) if(pModConf->console_log_level != -1) { r = klogctl(8, NULL, pModConf->console_log_level); if(r != 0) { - imklogLogIntMsg(LOG_WARNING, "imkmsg: cannot set console log level: %s", + imkmsgLogIntMsg(LOG_WARNING, "imkmsg: 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; @@ -161,7 +157,8 @@ finalize_it: RETiRet; } -/* Read kernel log while data are available, split into lines. +/* Read kernel log while data are available, each read() reads one + * record of printk buffer. */ static void readklog(void) @@ -195,7 +192,7 @@ XXX not sure if LOG_LINE_MAX is 1024 #endif for (;;) { - dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n"); + dbgprintf("imkmsg waiting for kernel log line\n"); /* every read() from the opened device node receives one record of the printk buffer */ i = read(fklog, pRcv, 1024); @@ -207,7 +204,7 @@ XXX not sure if LOG_LINE_MAX is 1024 /* something went wrong - error or zero length message */ if (i < 0 && errno != EINTR && errno != EAGAIN) { /* error occured */ - imklogLogIntMsg(LOG_ERR, + imkmsgLogIntMsg(LOG_ERR, "imkmsg: error reading kernel log - shutting down: %s", rs_strerror_r(errno, errmsg, sizeof(errmsg))); fklog = -1; @@ -255,3 +252,4 @@ klogFacilIntMsg(void) { return LOG_SYSLOG; } + -- cgit v1.2.3 From 498b8600707e979f78ee02641d56702784138b70 Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Wed, 12 Sep 2012 09:39:34 +0200 Subject: Made imkmsg parse the messages instead of creating string to be parsed later. modified: imkmsg.c modified: imkmsg.h modified: kmsg.c --- plugins/imkmsg/kmsg.c | 89 ++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 50 deletions(-) (limited to 'plugins/imkmsg/kmsg.c') diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index 88e00502..9f1de60f 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "rsyslog.h" #include "srUtils.h" @@ -53,15 +54,20 @@ static int fklog = -1; /* kernel log fd */ static void submitSyslog(uchar *buf) { - struct timeval tv; long offs = 0; + struct timeval tv; long int timestamp = 0; struct timespec monotonic; struct timespec realtime; - char outMsg[8096]; + char name[1024]; + char value[1024]; + char msg[1024]; int priority = 0; + long int sequnum = 0; + struct json_object *json = NULL, *jval; - offs = snprintf(outMsg, 8, "%s", "@cee: {"); + /* create new json object */ + json = json_object_new_object(); /* get priority */ for (; isdigit(*buf); buf++) { @@ -69,12 +75,13 @@ submitSyslog(uchar *buf) } buf++; - /* messages sequence number */ - offs += snprintf(outMsg+offs, 12, "%s", "\"sequnum\":\""); - for (; isdigit(*buf); buf++, offs++) { - outMsg[offs] = *buf; + /* get messages sequence number and add it to json */ + for (; isdigit(*buf); buf++) { + sequnum = (sequnum * 10) + (*buf - '0'); } buf++; /* skip , */ + jval = json_object_new_int(sequnum); + json_object_object_add(json, "sequnum", jval); /* get timestamp */ for (; isdigit(*buf); buf++) { @@ -82,34 +89,40 @@ submitSyslog(uchar *buf) } buf++; /* skip ; */ - offs += snprintf(outMsg+offs, 10, "%s", "\",\"msg\":\""); - + /* get message */ + offs = 0; for (; *buf != '\n' && *buf != '\0'; buf++, offs++) { - outMsg[offs] = *buf; + msg[offs] = *buf; } + msg[offs] = '\0'; + jval = json_object_new_string((char*)msg); + json_object_object_add(json, "msg", jval); if (*buf != '\0') /* message has appended properties, skip \n */ buf++; while (strlen((char *)buf)) { - offs += snprintf(outMsg+offs, 4, "%s", "\",\""); + /* get name of the property */ buf++; /* skip ' ' */ - for (; *buf != '=' && *buf != ' '; buf++, offs++) { /* separator is = or ' ' */ - outMsg[offs] = *buf; + offs = 0; + for (; *buf != '=' && *buf != ' '; buf++, offs++) { + name[offs] = *buf; } - buf++; /* skip = */ + name[offs] = '\0'; + buf++; /* skip = or ' ' */; - offs += snprintf(outMsg+offs, 4, "%s", "\":\""); + offs = 0; for (; *buf != '\n' && *buf != '\0'; buf++, offs++) { - outMsg[offs] = *buf; + value[offs] = *buf; } - - if (*buf != '\0') + value[offs] = '\0'; + if (*buf != '\0') { buf++; /* another property, skip \n */ - } - offs += snprintf(outMsg+offs, 3, "%s", "\"}"); + } - outMsg[offs] = '\0'; + jval = json_object_new_string((char*)value); + json_object_object_add(json, name, jval); + } /* calculate timestamp */ clock_gettime(CLOCK_MONOTONIC, &monotonic); @@ -117,7 +130,7 @@ submitSyslog(uchar *buf) tv.tv_sec = realtime.tv_sec + ((timestamp / 1000000l) - monotonic.tv_sec); tv.tv_usec = (realtime.tv_nsec + ((timestamp / 1000000000l) - monotonic.tv_nsec)) / 1000; - Syslog(priority, (uchar *)outMsg, &tv); + Syslog(priority, (uchar *)msg, &tv, json); } @@ -161,41 +174,17 @@ finalize_it: * record of printk buffer. */ static void -readklog(void) +readkmsg(void) { int i; - uchar pRcv[1024+1]; /* LOG_LINE_MAX is 1024 */ + uchar pRcv[8096+1]; char errmsg[2048]; -#if 0 -XXX not sure if LOG_LINE_MAX is 1024 -- int iMaxLine; -- uchar bufRcv[128*1024+1]; -+ uchar pRcv[1024+1]; /* LOG_LINE_MAX is 1024 */ - 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 */ -- } -#endif - for (;;) { dbgprintf("imkmsg waiting for kernel log line\n"); /* every read() from the opened device node receives one record of the printk buffer */ - i = read(fklog, pRcv, 1024); + i = read(fklog, pRcv, 8096); if (i > 0) { /* successful read of message of nonzero length */ @@ -239,7 +228,7 @@ rsRetVal klogAfterRun(modConfData_t *pModConf) rsRetVal klogLogKMsg(modConfData_t __attribute__((unused)) *pModConf) { DEFiRet; - readklog(); + readkmsg(); RETiRet; } -- cgit v1.2.3 From 432934b3dd2f5b2802a76f568b634c01a382e4a8 Mon Sep 17 00:00:00 2001 From: Milan Bartos Date: Mon, 17 Sep 2012 10:14:45 +0200 Subject: Remove unnecessary config options in imkmsg modified: plugins/imkmsg/imkmsg.c modified: plugins/imkmsg/kmsg.c --- plugins/imkmsg/kmsg.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'plugins/imkmsg/kmsg.c') diff --git a/plugins/imkmsg/kmsg.c b/plugins/imkmsg/kmsg.c index 9f1de60f..9ad98da4 100644 --- a/plugins/imkmsg/kmsg.c +++ b/plugins/imkmsg/kmsg.c @@ -134,11 +134,6 @@ submitSyslog(uchar *buf) } -static uchar *GetPath(modConfData_t *pModConf) -{ - return pModConf->pszPath ? pModConf->pszPath : (uchar*) _PATH_KLOG; -} - /* open the kernel log - will be called inside the willRun() imkmsg entry point */ rsRetVal @@ -148,10 +143,10 @@ klogWillRun(modConfData_t *pModConf) int r; DEFiRet; - fklog = open((char*)GetPath(pModConf), O_RDONLY, 0); + fklog = open(_PATH_KLOG, O_RDONLY, 0); if (fklog < 0) { imkmsgLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot open kernel log(%s): %s.", - GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); + _PATH_KLOG, rs_strerror_r(errno, errmsg, sizeof(errmsg))); ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); } -- cgit v1.2.3