mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-18 19:10:42 +01:00
262 lines
6.0 KiB
C
262 lines
6.0 KiB
C
/* imkmsg driver for Linux /dev/kmsg structured logging
|
|
*
|
|
* 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-2014 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 <time.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/klog.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/time.h>
|
|
#include <json.h>
|
|
|
|
#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 imkmsg 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)
|
|
{
|
|
long offs = 0;
|
|
struct timeval tv;
|
|
struct sysinfo info;
|
|
unsigned long int timestamp = 0;
|
|
char name[1024];
|
|
char value[1024];
|
|
char msg[1024];
|
|
syslog_pri_t priority = 0;
|
|
long int sequnum = 0;
|
|
struct json_object *json = NULL, *jval;
|
|
|
|
/* create new json object */
|
|
json = json_object_new_object();
|
|
|
|
/* get priority */
|
|
for (; isdigit(*buf); buf++) {
|
|
priority = (priority * 10) + (*buf - '0');
|
|
}
|
|
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++) {
|
|
timestamp = (timestamp * 10) + (*buf - '0');
|
|
}
|
|
|
|
while (*buf != ';') {
|
|
buf++; /* skip everything till the first ; */
|
|
}
|
|
buf++; /* skip ; */
|
|
|
|
/* get message */
|
|
offs = 0;
|
|
for (; *buf != '\n' && *buf != '\0'; buf++, offs++) {
|
|
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 (*buf) {
|
|
/* get name of the property */
|
|
buf++; /* skip ' ' */
|
|
offs = 0;
|
|
for (; *buf != '=' && *buf != ' '; buf++, offs++) {
|
|
name[offs] = *buf;
|
|
}
|
|
name[offs] = '\0';
|
|
buf++; /* skip = or ' ' */;
|
|
|
|
offs = 0;
|
|
for (; *buf != '\n' && *buf != '\0'; buf++, offs++) {
|
|
value[offs] = *buf;
|
|
}
|
|
value[offs] = '\0';
|
|
if (*buf != '\0') {
|
|
buf++; /* another property, skip \n */
|
|
}
|
|
|
|
jval = json_object_new_string((char*)value);
|
|
json_object_object_add(json, name, jval);
|
|
}
|
|
|
|
/* calculate timestamp */
|
|
sysinfo(&info);
|
|
gettimeofday(&tv, NULL);
|
|
|
|
/* get boot time */
|
|
tv.tv_sec -= info.uptime;
|
|
|
|
tv.tv_sec += timestamp / 1000000;
|
|
tv.tv_usec += timestamp % 1000000;
|
|
|
|
while (tv.tv_usec < 0) {
|
|
tv.tv_sec--;
|
|
tv.tv_usec += 1000000;
|
|
}
|
|
|
|
while (tv.tv_usec >= 1000000) {
|
|
tv.tv_sec++;
|
|
tv.tv_usec -= 1000000;
|
|
}
|
|
|
|
Syslog(priority, (uchar *)msg, &tv, json);
|
|
}
|
|
|
|
|
|
/* open the kernel log - will be called inside the willRun() imkmsg entry point
|
|
*/
|
|
rsRetVal
|
|
klogWillRun(modConfData_t *pModConf)
|
|
{
|
|
int i;
|
|
char errmsg[2048];
|
|
DEFiRet;
|
|
|
|
fklog = open(_PATH_KLOG, O_RDONLY, 0);
|
|
if (fklog < 0) {
|
|
imkmsgLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot open kernel log(%s): %s.",
|
|
_PATH_KLOG, rs_strerror_r(errno, errmsg, sizeof(errmsg)));
|
|
ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG);
|
|
}
|
|
|
|
/* make sure the kernel log is readable */
|
|
/* this normally returns EINVAL - on an OpenVZ VM, we get EBADF */
|
|
i = read(fklog, NULL, 0);
|
|
if (i < 0 && errno == EBADF) {
|
|
imkmsgLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imkmsg: cannot read kernel log(%s): %s.",
|
|
_PATH_KLOG, rs_strerror_r(errno, errmsg, sizeof(errmsg)));
|
|
fklog = -1;
|
|
ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG);
|
|
}
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
/* Read kernel log while data are available, each read() reads one
|
|
* record of printk buffer.
|
|
*/
|
|
static void
|
|
readkmsg(void)
|
|
{
|
|
int i;
|
|
uchar pRcv[8192+1];
|
|
char errmsg[2048];
|
|
|
|
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, 8192);
|
|
|
|
if (i > 0) {
|
|
/* successful read of message of nonzero length */
|
|
pRcv[i] = '\0';
|
|
} else if (i == -EPIPE) {
|
|
imkmsgLogIntMsg(LOG_WARNING,
|
|
"imkmsg: some messages in circular buffer got overwritten");
|
|
continue;
|
|
} else {
|
|
/* something went wrong - error or zero length message */
|
|
if (i < 0 && errno != EINTR && errno != EAGAIN) {
|
|
/* error occured */
|
|
imkmsgLogIntMsg(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;
|
|
readkmsg();
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* provide the (system-specific) default facility for internal messages
|
|
* rgerhards, 2008-04-14
|
|
*/
|
|
int
|
|
klogFacilIntMsg(void)
|
|
{
|
|
return LOG_SYSLOG;
|
|
}
|
|
|