Fix #19: Prefer /dev/kmsg over /proc/kmsg on Linux

This patch migrates the sysklogd project to use the modern /dev/kmsg
interface on Linux.  There are many advantages over the older /proc
interface; 1) no need to wait for /proc to be mounted, 2) it provides
multiple simultaneous access.  For more information, see:

  https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg 2020-08-31 21:18:17 +02:00
parent 93117e5801
commit 5aa7372f4c
2 changed files with 69 additions and 9 deletions

View File

@ -114,7 +114,7 @@ domain socket
from an Internet domain socket specified in from an Internet domain socket specified in
.Pa /etc/services , .Pa /etc/services ,
and from and from
.Pa /proc/kmsg .Pa /dev/kmsg
.Pq to read kernel messages . .Pq to read kernel messages .
The command line options defined below can be used to change this The command line options defined below can be used to change this
behavior. behavior.
@ -244,7 +244,7 @@ to facility
Usually the Usually the
.Dq kern .Dq kern
facility is reserved for messages read directly from facility is reserved for messages read directly from
.Pa /proc/kmsg . .Pa /dev/kmsg .
.It Fl m Ar interval .It Fl m Ar interval
Select the number of minutes between Select the number of minutes between
.Dq mark .Dq mark
@ -461,7 +461,7 @@ default process ID file
name of the name of the
.Ux .Ux
domain datagram log socket domain datagram log socket
.It Pa /proc/kmsg .It Pa /dev/kmsg
kernel log device kernel log device
.El .El
.Sh SEE ALSO .Sh SEE ALSO

View File

@ -76,6 +76,9 @@ static char sccsid[] __attribute__((unused)) =
#include <sys/time.h> #include <sys/time.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/wait.h> #include <sys/wait.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#include <arpa/inet.h> #include <arpa/inet.h>
#include <arpa/nameser.h> #include <arpa/nameser.h>
@ -117,6 +120,7 @@ struct filed consfile;
static int Debug; /* debug flag */ static int Debug; /* debug flag */
static int Foreground = 0; /* don't fork - don't run in daemon mode */ static int Foreground = 0; /* don't fork - don't run in daemon mode */
static time_t boot_time; /* Offset for printsys() */
static int resolve = 1; /* resolve hostname */ static int resolve = 1; /* resolve hostname */
static char LocalHostName[MAXHOSTNAMELEN + 1]; /* our hostname */ static char LocalHostName[MAXHOSTNAMELEN + 1]; /* our hostname */
static char *LocalDomain; /* our local domain name */ static char *LocalDomain; /* our local domain name */
@ -165,6 +169,7 @@ void doflush(void *arg);
void debug_switch(); void debug_switch();
void die(int sig); void die(int sig);
static void signal_init(void); static void signal_init(void);
static void boot_time_init(void);
static void init(void); static void init(void);
static int strtobytes(char *arg); static int strtobytes(char *arg);
static int cfparse(FILE *fp, struct files *newf); static int cfparse(FILE *fp, struct files *newf);
@ -378,16 +383,24 @@ int main(int argc, char *argv[])
setlinebuf(stdout); setlinebuf(stdout);
} }
/* Attempt to open kernel log pipe */ /*
if (opensys(_PATH_KLOG)) * Attempt to open kernel log pipe. On Linux we prefer
warn("Kernel logging disabled, failed opening %s", _PATH_KLOG); * /dev/kmsg and fall back to _PROC_KLOG, which on GLIBC
else * systems is /proc/kmsg, and /dev/klog on *BSD.
*/
if (opensys("/dev/kmsg")) {
if (opensys(_PATH_KLOG))
warn("Kernel logging disabled, failed opening %s", _PATH_KLOG);
else
kern_console_off();
} else
kern_console_off(); kern_console_off();
consfile.f_type = F_CONSOLE; consfile.f_type = F_CONSOLE;
strlcpy(consfile.f_un.f_fname, ctty, sizeof(consfile.f_un.f_fname)); strlcpy(consfile.f_un.f_fname, ctty, sizeof(consfile.f_un.f_fname));
logit("Starting.\n"); logit("Starting.\n");
boot_time_init();
signal_init(); signal_init();
init(); init();
@ -1066,7 +1079,8 @@ parsemsg(const char *from, char *msg)
} }
/* /*
* Take a raw input line from /dev/klog, split and format similar to syslog(). * Take a raw input line from /dev/klog, Linux /proc/klog, or Linux
* /dev/kmsg, split and format similar to syslog().
*/ */
void printsys(char *msg) void printsys(char *msg)
{ {
@ -1084,11 +1098,35 @@ void printsys(char *msg)
buffer.msg = line; buffer.msg = line;
if (*p == '<') { if (*p == '<') {
/* /proc/klog or *BSD /dev/klog */
buffer.pri = 0; buffer.pri = 0;
while (isdigit(*++p)) while (isdigit(*++p))
buffer.pri = 10 * buffer.pri + (*p - '0'); buffer.pri = 10 * buffer.pri + (*p - '0');
if (*p == '>') if (*p == '>')
++p; ++p;
} else if (isdigit(*p)) {
/* Linux /dev/kmsg: "pri,seq#,msec,flag[,..];msg" */
time_t now = boot_time;
while (isdigit(*++p))
buffer.pri = 10 * buffer.pri + (*p - '0');
++p;
/* skip sequence number for now */
while (isdigit(*++p))
;
++p;
while (isdigit(*++p))
buffer.timestamp.usec = 10 * buffer.timestamp.usec + (*p - '0');
now += buffer.timestamp.usec / 1000000;
buffer.timestamp.usec = buffer.timestamp.usec % 1000000;
localtime_r(&now, &buffer.timestamp.tm);
/* skip flags for now */
q = strchr(p, ';');
if (q)
p = ++q;
} else if (*p == ' ') {
/* Linux /dev/kmsg continuation line w/ SUBSYSTEM= DEVICE=, skip */
return;
} else { } else {
/* kernel printf's come out on console */ /* kernel printf's come out on console */
buffer.flags |= IGN_CONS; buffer.flags |= IGN_CONS;
@ -1098,8 +1136,18 @@ void printsys(char *msg)
buffer.pri = DEFSPRI; buffer.pri = DEFSPRI;
q = lp; q = lp;
while (*p != '\0' && (c = *p++) != '\n' && q < &line[MAXLINE]) while (*p != '\0' && (c = *p++) != '\n' && q < &line[MAXLINE]) {
/* Linux /dev/kmsg C-style hex encoding. */
if (c == '\\' && *p == 'x') {
char code[5] = "0x\0\0\0";
p++;
code[2] = *p++;
code[3] = *p++;
c = (int)strtol(code, NULL, 16);
}
*q++ = c; *q++ = c;
}
*q = '\0'; *q = '\0';
logmsg(&buffer); logmsg(&buffer);
@ -2136,6 +2184,18 @@ static void signal_init(void)
SIGNAL(SIGCHLD, reapchild); SIGNAL(SIGCHLD, reapchild);
} }
static void boot_time_init(void)
{
#ifdef __linux__
struct sysinfo si;
struct timeval tv;
gettimeofday(&tv, NULL);
sysinfo(&si);
boot_time = tv.tv_sec - si.uptime;
#endif
}
/* /*
* INIT -- Initialize syslogd from configuration table * INIT -- Initialize syslogd from configuration table
*/ */