]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-kmsg.c
journald: split /dev/kmsg related stuff into its own .c file
[thirdparty/systemd.git] / src / journal / journald-kmsg.c
CommitLineData
ef63833d
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
23#include <sys/epoll.h>
24#include <fcntl.h>
25#include <sys/mman.h>
26
27#include <systemd/sd-messages.h>
28#include <libudev.h>
29
30#include "journald.h"
31#include "journald-kmsg.h"
32
33void server_forward_kmsg(
34 Server *s,
35 int priority,
36 const char *identifier,
37 const char *message,
38 struct ucred *ucred) {
39
40 struct iovec iovec[5];
41 char header_priority[6], header_pid[16];
42 int n = 0;
43 char *ident_buf = NULL;
44
45 assert(s);
46 assert(priority >= 0);
47 assert(priority <= 999);
48 assert(message);
49
50 if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg))
51 return;
52
53 if (_unlikely_(s->dev_kmsg_fd < 0))
54 return;
55
56 /* Never allow messages with kernel facility to be written to
57 * kmsg, regardless where the data comes from. */
58 priority = syslog_fixup_facility(priority);
59
60 /* First: priority field */
61 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
62 char_array_0(header_priority);
63 IOVEC_SET_STRING(iovec[n++], header_priority);
64
65 /* Second: identifier and PID */
66 if (ucred) {
67 if (!identifier) {
68 get_process_comm(ucred->pid, &ident_buf);
69 identifier = ident_buf;
70 }
71
72 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
73 char_array_0(header_pid);
74
75 if (identifier)
76 IOVEC_SET_STRING(iovec[n++], identifier);
77
78 IOVEC_SET_STRING(iovec[n++], header_pid);
79 } else if (identifier) {
80 IOVEC_SET_STRING(iovec[n++], identifier);
81 IOVEC_SET_STRING(iovec[n++], ": ");
82 }
83
84 /* Fourth: message */
85 IOVEC_SET_STRING(iovec[n++], message);
86 IOVEC_SET_STRING(iovec[n++], "\n");
87
88 if (writev(s->dev_kmsg_fd, iovec, n) < 0)
89 log_debug("Failed to write to /dev/kmsg for logging: %s", strerror(errno));
90
91 free(ident_buf);
92}
93
94static bool is_us(const char *pid) {
95 pid_t t;
96
97 assert(pid);
98
99 if (parse_pid(pid, &t) < 0)
100 return false;
101
102 return t == getpid();
103}
104
105static void dev_kmsg_record(Server *s, char *p, size_t l) {
106 struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
107 char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL;
108 int priority, r;
109 unsigned n = 0, z = 0, j;
110 usec_t usec;
111 char *identifier = NULL, *pid = NULL, *e, *f, *k;
112 uint64_t serial;
113 size_t pl;
114 char *kernel_device = NULL;
115
116 assert(s);
117 assert(p);
118
119 if (l <= 0)
120 return;
121
122 e = memchr(p, ',', l);
123 if (!e)
124 return;
125 *e = 0;
126
127 r = safe_atoi(p, &priority);
128 if (r < 0 || priority < 0 || priority > 999)
129 return;
130
131 if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN)
132 return;
133
134 l -= (e - p) + 1;
135 p = e + 1;
136 e = memchr(p, ',', l);
137 if (!e)
138 return;
139 *e = 0;
140
141 r = safe_atou64(p, &serial);
142 if (r < 0)
143 return;
144
145 if (s->kernel_seqnum) {
146 /* We already read this one? */
147 if (serial < *s->kernel_seqnum)
148 return;
149
150 /* Did we lose any? */
151 if (serial > *s->kernel_seqnum)
152 server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %llu kernel messages", (unsigned long long) serial - *s->kernel_seqnum - 1);
153
154 /* Make sure we never read this one again. Note that
155 * we always store the next message serial we expect
156 * here, simply because this makes handling the first
157 * message with serial 0 easy. */
158 *s->kernel_seqnum = serial + 1;
159 }
160
161 l -= (e - p) + 1;
162 p = e + 1;
163 f = memchr(p, ';', l);
164 if (!f)
165 return;
166 /* Kernel 3.6 has the flags field, kernel 3.5 lacks that */
167 e = memchr(p, ',', l);
168 if (!e || f < e)
169 e = f;
170 *e = 0;
171
172 r = parse_usec(p, &usec);
173 if (r < 0)
174 return;
175
176 l -= (f - p) + 1;
177 p = f + 1;
178 e = memchr(p, '\n', l);
179 if (!e)
180 return;
181 *e = 0;
182
183 pl = e - p;
184 l -= (e - p) + 1;
185 k = e + 1;
186
187 for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) {
188 char *m;
189 /* Meta data fields attached */
190
191 if (*k != ' ')
192 break;
193
194 k ++, l --;
195
196 e = memchr(k, '\n', l);
197 if (!e)
198 return;
199
200 *e = 0;
201
202 m = cunescape_length_with_prefix(k, e - k, "_KERNEL_");
203 if (!m)
204 break;
205
206 if (startswith(m, "_KERNEL_DEVICE="))
207 kernel_device = m + 15;
208
209 IOVEC_SET_STRING(iovec[n++], m);
210 z++;
211
212 l -= (e - k) + 1;
213 k = e + 1;
214 }
215
216 if (kernel_device) {
217 struct udev_device *ud;
218
219 ud = udev_device_new_from_device_id(s->udev, kernel_device);
220 if (ud) {
221 const char *g;
222 struct udev_list_entry *ll;
223 char *b;
224
225 g = udev_device_get_devnode(ud);
226 if (g) {
227 b = strappend("_UDEV_DEVNODE=", g);
228 if (b) {
229 IOVEC_SET_STRING(iovec[n++], b);
230 z++;
231 }
232 }
233
234 g = udev_device_get_sysname(ud);
235 if (g) {
236 b = strappend("_UDEV_SYSNAME=", g);
237 if (b) {
238 IOVEC_SET_STRING(iovec[n++], b);
239 z++;
240 }
241 }
242
243 j = 0;
244 ll = udev_device_get_devlinks_list_entry(ud);
245 udev_list_entry_foreach(ll, ll) {
246
247 if (j > N_IOVEC_UDEV_FIELDS)
248 break;
249
250 g = udev_list_entry_get_name(ll);
251 b = strappend("_UDEV_DEVLINK=", g);
252 if (g) {
253 IOVEC_SET_STRING(iovec[n++], b);
254 z++;
255 }
256
257 j++;
258 }
259
260 udev_device_unref(ud);
261 }
262 }
263
264 if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu",
265 (unsigned long long) usec) >= 0)
266 IOVEC_SET_STRING(iovec[n++], source_time);
267
268 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel");
269
270 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
271 IOVEC_SET_STRING(iovec[n++], syslog_priority);
272
273 if ((priority & LOG_FACMASK) == LOG_KERN)
274 IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel");
275 else {
276 syslog_read_identifier((const char**) &p, &identifier, &pid);
277
278 /* Avoid any messages we generated ourselves via
279 * log_info() and friends. */
280 if (pid && is_us(pid))
281 goto finish;
282
283 if (identifier) {
284 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
285 if (syslog_identifier)
286 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
287 }
288
289 if (pid) {
290 syslog_pid = strappend("SYSLOG_PID=", pid);
291 if (syslog_pid)
292 IOVEC_SET_STRING(iovec[n++], syslog_pid);
293 }
294
295 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
296 IOVEC_SET_STRING(iovec[n++], syslog_facility);
297 }
298
299 message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
300 if (message)
301 IOVEC_SET_STRING(iovec[n++], message);
302
303 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority);
304
305finish:
306 for (j = 0; j < z; j++)
307 free(iovec[j].iov_base);
308
309 free(message);
310 free(syslog_priority);
311 free(syslog_identifier);
312 free(syslog_pid);
313 free(syslog_facility);
314 free(source_time);
315 free(identifier);
316 free(pid);
317}
318
319int server_read_dev_kmsg(Server *s) {
320 char buffer[8192+1]; /* the kernel-side limit per record is 8K currently */
321 ssize_t l;
322
323 assert(s);
324 assert(s->dev_kmsg_fd >= 0);
325
326 l = read(s->dev_kmsg_fd, buffer, sizeof(buffer) - 1);
327 if (l == 0)
328 return 0;
329 if (l < 0) {
330 /* Old kernels who don't allow reading from /dev/kmsg
331 * return EINVAL when we try. So handle this cleanly,
332 * but don' try to ever read from it again. */
333 if (errno == EINVAL) {
334 epoll_ctl(s->epoll_fd, EPOLL_CTL_DEL, s->dev_kmsg_fd, NULL);
335 return 0;
336 }
337
338 if (errno == EAGAIN || errno == EINTR || errno == EPIPE)
339 return 0;
340
341 log_error("Failed to read from kernel: %m");
342 return -errno;
343 }
344
345 dev_kmsg_record(s, buffer, l);
346 return 1;
347}
348
349int server_flush_dev_kmsg(Server *s) {
350 int r;
351
352 assert(s);
353
354 if (s->dev_kmsg_fd < 0)
355 return 0;
356
357 if (!s->dev_kmsg_readable)
358 return 0;
359
360 log_info("Flushing /dev/kmsg...");
361
362 for (;;) {
363 r = server_read_dev_kmsg(s);
364 if (r < 0)
365 return r;
366
367 if (r == 0)
368 break;
369 }
370
371 return 0;
372}
373
374int server_open_dev_kmsg(Server *s) {
375 struct epoll_event ev;
376
377 assert(s);
378
379 s->dev_kmsg_fd = open("/dev/kmsg", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
380 if (s->dev_kmsg_fd < 0) {
381 log_warning("Failed to open /dev/kmsg, ignoring: %m");
382 return 0;
383 }
384
385 zero(ev);
386 ev.events = EPOLLIN;
387 ev.data.fd = s->dev_kmsg_fd;
388 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->dev_kmsg_fd, &ev) < 0) {
389
390 /* This will fail with EPERM on older kernels where
391 * /dev/kmsg is not readable. */
392 if (errno == EPERM)
393 return 0;
394
395 log_error("Failed to add /dev/kmsg fd to epoll object: %m");
396 return -errno;
397 }
398
399 s->dev_kmsg_readable = true;
400
401 return 0;
402}
403
404int server_open_kernel_seqnum(Server *s) {
405 int fd;
406 uint64_t *p;
407
408 assert(s);
409
410 /* We store the seqnum we last read in an mmaped file. That
411 * way we can just use it like a variable, but it is
412 * persistant and automatically flushed at reboot. */
413
414 fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
415 if (fd < 0) {
416 log_error("Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
417 return 0;
418 }
419
420 if (posix_fallocate(fd, 0, sizeof(uint64_t)) < 0) {
421 log_error("Failed to allocate sequential number file, ignoring: %m");
422 close_nointr_nofail(fd);
423 return 0;
424 }
425
426 p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
427 if (p == MAP_FAILED) {
428 log_error("Failed to map sequential number file, ignoring: %m");
429 close_nointr_nofail(fd);
430 return 0;
431 }
432
433 close_nointr_nofail(fd);
434 s->kernel_seqnum = p;
435
436 return 0;
437}