journald-kmsg.c revision 445ea9be520b9549aee45d0b6427cf48b446987f
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/***
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen This file is part of systemd.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Copyright 2011 Lennart Poettering
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is free software; you can redistribute it and/or modify it
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen under the terms of the GNU Lesser General Public License as published by
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen (at your option) any later version.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is distributed in the hope that it will be useful, but
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Lesser General Public License for more details.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen You should have received a copy of the GNU Lesser General Public License
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen***/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <unistd.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <sys/epoll.h>
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include <fcntl.h>
7c99d940c11e4da1863a218b6b70dd16e65b7518Tom Gundersen#include <sys/mman.h>
7c99d940c11e4da1863a218b6b70dd16e65b7518Tom Gundersen#include <sys/socket.h>
7c99d940c11e4da1863a218b6b70dd16e65b7518Tom Gundersen
7c99d940c11e4da1863a218b6b70dd16e65b7518Tom Gundersen#include <systemd/sd-messages.h>
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen#include <libudev.h>
7c99d940c11e4da1863a218b6b70dd16e65b7518Tom Gundersen
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen#include "journald-server.h"
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen#include "journald-kmsg.h"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include "journald-syslog.h"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenvoid server_forward_kmsg(
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Server *s,
505f8da7325591defe5f751f328bd26915267602Tom Gundersen int priority,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen const char *identifier,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen const char *message,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen struct ucred *ucred) {
505f8da7325591defe5f751f328bd26915267602Tom Gundersen
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen struct iovec iovec[5];
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen char header_priority[6], header_pid[16];
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen int n = 0;
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen char *ident_buf = NULL;
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen assert(s);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen assert(priority >= 0);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen assert(priority <= 999);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen assert(message);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg))
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen return;
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen if (_unlikely_(s->dev_kmsg_fd < 0))
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen return;
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen /* Never allow messages with kernel facility to be written to
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen * kmsg, regardless where the data comes from. */
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen priority = syslog_fixup_facility(priority);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen /* First: priority field */
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen char_array_0(header_priority);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], header_priority);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen /* Second: identifier and PID */
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen if (ucred) {
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen if (!identifier) {
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen get_process_comm(ucred->pid, &ident_buf);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen identifier = ident_buf;
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen }
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen char_array_0(header_pid);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen if (identifier)
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], identifier);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], header_pid);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen } else if (identifier) {
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], identifier);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], ": ");
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen }
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen /* Fourth: message */
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], message);
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen IOVEC_SET_STRING(iovec[n++], "\n");
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (writev(s->dev_kmsg_fd, iovec, n) < 0)
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen log_debug("Failed to write to /dev/kmsg for logging: %m");
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen free(ident_buf);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen}
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersen
9505d3c6deda0452c22ab2ed47bca74b98d87a17Tom Gundersenstatic bool is_us(const char *pid) {
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen pid_t t;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen assert(pid);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (parse_pid(pid, &t) < 0)
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen return false;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return t == getpid();
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen}
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic void dev_kmsg_record(Server *s, char *p, size_t l) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL;
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen int priority, r;
12e0f830f592ec4c6bb49ac7ae1e0e84f74105e3Tom Gundersen unsigned n = 0, z = 0, j;
505f8da7325591defe5f751f328bd26915267602Tom Gundersen unsigned long long usec;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen char *identifier = NULL, *pid = NULL, *e, *f, *k;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen uint64_t serial;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen size_t pl;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen char *kernel_device = NULL;
assert(s);
assert(p);
if (l <= 0)
return;
e = memchr(p, ',', l);
if (!e)
return;
*e = 0;
r = safe_atoi(p, &priority);
if (r < 0 || priority < 0 || priority > 999)
return;
if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN)
return;
l -= (e - p) + 1;
p = e + 1;
e = memchr(p, ',', l);
if (!e)
return;
*e = 0;
r = safe_atou64(p, &serial);
if (r < 0)
return;
if (s->kernel_seqnum) {
/* We already read this one? */
if (serial < *s->kernel_seqnum)
return;
/* Did we lose any? */
if (serial > *s->kernel_seqnum)
server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %"PRIu64" kernel messages",
serial - *s->kernel_seqnum - 1);
/* Make sure we never read this one again. Note that
* we always store the next message serial we expect
* here, simply because this makes handling the first
* message with serial 0 easy. */
*s->kernel_seqnum = serial + 1;
}
l -= (e - p) + 1;
p = e + 1;
f = memchr(p, ';', l);
if (!f)
return;
/* Kernel 3.6 has the flags field, kernel 3.5 lacks that */
e = memchr(p, ',', l);
if (!e || f < e)
e = f;
*e = 0;
r = safe_atollu(p, &usec);
if (r < 0)
return;
l -= (f - p) + 1;
p = f + 1;
e = memchr(p, '\n', l);
if (!e)
return;
*e = 0;
pl = e - p;
l -= (e - p) + 1;
k = e + 1;
for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) {
char *m;
/* Meta data fields attached */
if (*k != ' ')
break;
k ++, l --;
e = memchr(k, '\n', l);
if (!e)
return;
*e = 0;
m = cunescape_length_with_prefix(k, e - k, "_KERNEL_");
if (!m)
break;
if (startswith(m, "_KERNEL_DEVICE="))
kernel_device = m + 15;
IOVEC_SET_STRING(iovec[n++], m);
z++;
l -= (e - k) + 1;
k = e + 1;
}
if (kernel_device) {
struct udev_device *ud;
ud = udev_device_new_from_device_id(s->udev, kernel_device);
if (ud) {
const char *g;
struct udev_list_entry *ll;
char *b;
g = udev_device_get_devnode(ud);
if (g) {
b = strappend("_UDEV_DEVNODE=", g);
if (b) {
IOVEC_SET_STRING(iovec[n++], b);
z++;
}
}
g = udev_device_get_sysname(ud);
if (g) {
b = strappend("_UDEV_SYSNAME=", g);
if (b) {
IOVEC_SET_STRING(iovec[n++], b);
z++;
}
}
j = 0;
ll = udev_device_get_devlinks_list_entry(ud);
udev_list_entry_foreach(ll, ll) {
if (j > N_IOVEC_UDEV_FIELDS)
break;
g = udev_list_entry_get_name(ll);
if (g) {
b = strappend("_UDEV_DEVLINK=", g);
if (b) {
IOVEC_SET_STRING(iovec[n++], b);
z++;
}
}
j++;
}
udev_device_unref(ud);
}
}
if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0)
IOVEC_SET_STRING(iovec[n++], source_time);
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel");
if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
IOVEC_SET_STRING(iovec[n++], syslog_priority);
if ((priority & LOG_FACMASK) == LOG_KERN)
IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel");
else {
pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid);
/* Avoid any messages we generated ourselves via
* log_info() and friends. */
if (pid && is_us(pid))
goto finish;
if (identifier) {
syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
if (syslog_identifier)
IOVEC_SET_STRING(iovec[n++], syslog_identifier);
}
if (pid) {
syslog_pid = strappend("SYSLOG_PID=", pid);
if (syslog_pid)
IOVEC_SET_STRING(iovec[n++], syslog_pid);
}
if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
IOVEC_SET_STRING(iovec[n++], syslog_facility);
}
message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
if (message)
IOVEC_SET_STRING(iovec[n++], message);
server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0);
finish:
for (j = 0; j < z; j++)
free(iovec[j].iov_base);
free(message);
free(syslog_priority);
free(syslog_identifier);
free(syslog_pid);
free(syslog_facility);
free(source_time);
free(identifier);
free(pid);
}
static int server_read_dev_kmsg(Server *s) {
char buffer[8192+1]; /* the kernel-side limit per record is 8K currently */
ssize_t l;
assert(s);
assert(s->dev_kmsg_fd >= 0);
l = read(s->dev_kmsg_fd, buffer, sizeof(buffer) - 1);
if (l == 0)
return 0;
if (l < 0) {
/* Old kernels who don't allow reading from /dev/kmsg
* return EINVAL when we try. So handle this cleanly,
* but don' try to ever read from it again. */
if (errno == EINVAL) {
s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source);
return 0;
}
if (errno == EAGAIN || errno == EINTR || errno == EPIPE)
return 0;
log_error("Failed to read from kernel: %m");
return -errno;
}
dev_kmsg_record(s, buffer, l);
return 1;
}
int server_flush_dev_kmsg(Server *s) {
int r;
assert(s);
if (s->dev_kmsg_fd < 0)
return 0;
if (!s->dev_kmsg_readable)
return 0;
log_debug("Flushing /dev/kmsg...");
for (;;) {
r = server_read_dev_kmsg(s);
if (r < 0)
return r;
if (r == 0)
break;
}
return 0;
}
static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
Server *s = userdata;
assert(es);
assert(fd == s->dev_kmsg_fd);
assert(s);
if (revents & EPOLLERR)
log_warning("/dev/kmsg buffer overrun, some messages lost.");
if (!(revents & EPOLLIN))
log_error("Got invalid event from epoll for /dev/kmsg: %"PRIx32, revents);
return server_read_dev_kmsg(s);
}
int server_open_dev_kmsg(Server *s) {
int r;
assert(s);
s->dev_kmsg_fd = open("/dev/kmsg", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (s->dev_kmsg_fd < 0) {
log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
"Failed to open /dev/kmsg, ignoring: %m");
return 0;
}
r = sd_event_add_io(s->event, s->dev_kmsg_fd, EPOLLIN, dispatch_dev_kmsg, s, &s->dev_kmsg_event_source);
if (r < 0) {
/* This will fail with EPERM on older kernels where
* /dev/kmsg is not readable. */
if (r == -EPERM)
return 0;
log_error("Failed to add /dev/kmsg fd to event loop: %s", strerror(-r));
return -errno;
}
r = sd_event_source_set_priority(s->dev_kmsg_event_source, SD_EVENT_PRIORITY_IMPORTANT+10);
if (r < 0) {
log_error("Failed to adjust priority of kmsg event source: %s", strerror(-r));
return -errno;
}
s->dev_kmsg_readable = true;
return 0;
}
int server_open_kernel_seqnum(Server *s) {
int fd;
uint64_t *p;
assert(s);
/* We store the seqnum we last read in an mmaped file. That
* way we can just use it like a variable, but it is
* persistent and automatically flushed at reboot. */
fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
if (fd < 0) {
log_error("Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
return 0;
}
if (posix_fallocate(fd, 0, sizeof(uint64_t)) < 0) {
log_error("Failed to allocate sequential number file, ignoring: %m");
close_nointr_nofail(fd);
return 0;
}
p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
log_error("Failed to map sequential number file, ignoring: %m");
close_nointr_nofail(fd);
return 0;
}
close_nointr_nofail(fd);
s->kernel_seqnum = p;
return 0;
}