]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/dmesg.c
dmesg: add --syslog to force to old syslog(2) interface
[thirdparty/util-linux.git] / sys-utils / dmesg.c
CommitLineData
5ef05369
KZ
1/*
2 * dmesg.c -- Print out the contents of the kernel ring buffer
7eda085c 3 *
5ef05369
KZ
4 * Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu>
5 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
6 *
7 * This program comes with ABSOLUTELY NO WARRANTY.
6dbe3af9 8 */
6dbe3af9
KZ
9#include <linux/unistd.h>
10#include <stdio.h>
11#include <getopt.h>
fd6b7a7f 12#include <stdlib.h>
15673c15 13#include <sys/klog.h>
f06ec64f 14#include <sys/syslog.h>
bd304d92 15#include <sys/time.h>
42fac79a 16#include <sys/sysinfo.h>
15103c4b 17#include <ctype.h>
bd304d92 18#include <time.h>
c672220f
KZ
19#include <sys/mman.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <unistd.h>
23#include <fcntl.h>
5423ccb1 24
15103c4b 25#include "c.h"
5423ccb1 26#include "nls.h"
15673c15 27#include "strutils.h"
15103c4b 28#include "xalloc.h"
b8300c0a 29#include "widechar.h"
e12c9866 30#include "all-io.h"
636a6207 31#include "bitops.h"
efb8854f 32#include "closestream.h"
94920134 33#include "optutils.h"
6dbe3af9 34
59a14899
KZ
35/* Close the log. Currently a NOP. */
36#define SYSLOG_ACTION_CLOSE 0
37/* Open the log. Currently a NOP. */
38#define SYSLOG_ACTION_OPEN 1
39/* Read from the log. */
40#define SYSLOG_ACTION_READ 2
41/* Read all messages remaining in the ring buffer. (allowed for non-root) */
42#define SYSLOG_ACTION_READ_ALL 3
43/* Read and clear all messages remaining in the ring buffer */
44#define SYSLOG_ACTION_READ_CLEAR 4
45/* Clear ring buffer. */
46#define SYSLOG_ACTION_CLEAR 5
47/* Disable printk's to console */
48#define SYSLOG_ACTION_CONSOLE_OFF 6
49/* Enable printk's to console */
50#define SYSLOG_ACTION_CONSOLE_ON 7
51/* Set level of messages printed to console */
52#define SYSLOG_ACTION_CONSOLE_LEVEL 8
53/* Return number of unread characters in the log buffer */
54#define SYSLOG_ACTION_SIZE_UNREAD 9
55/* Return size of the log buffer */
56#define SYSLOG_ACTION_SIZE_BUFFER 10
57
94920134
SK
58#define EXCL_ERROR "--{clear,read-clear,console-level,console-on,console-off}"
59
f06ec64f 60/*
5ef05369 61 * Priority and facility names
f06ec64f
KZ
62 */
63struct dmesg_name {
64 const char *name;
65 const char *help;
66};
67
5ef05369
KZ
68/*
69 * Priority names -- based on sys/syslog.h
70 */
f06ec64f
KZ
71static const struct dmesg_name level_names[] =
72{
73 [LOG_EMERG] = { "emerg", N_("system is unusable") },
74 [LOG_ALERT] = { "alert", N_("action must be taken immediately") },
75 [LOG_CRIT] = { "crit", N_("critical conditions") },
76 [LOG_ERR] = { "err", N_("error conditions") },
77 [LOG_WARNING] = { "warn", N_("warning conditions") },
78 [LOG_NOTICE] = { "notice",N_("normal but significant condition") },
79 [LOG_INFO] = { "info", N_("informational") },
80 [LOG_DEBUG] = { "debug", N_("debug-level messages") }
81};
82
85f3cc55
KZ
83/*
84 * sys/syslog.h uses (f << 3) for all facility codes.
85 * We want to use the codes as array idexes, so shift back...
86 *
87 * Note that libc LOG_FAC() macro returns the base codes, not the
88 * shifted code :-)
89 */
90#define FAC_BASE(f) ((f) >> 3)
91
92static const struct dmesg_name facility_names[] =
93{
94 [FAC_BASE(LOG_KERN)] = { "kern", N_("kernel messages") },
95 [FAC_BASE(LOG_USER)] = { "user", N_("random user-level messages") },
96 [FAC_BASE(LOG_MAIL)] = { "mail", N_("mail system") },
97 [FAC_BASE(LOG_DAEMON)] = { "daemon", N_("system daemons") },
98 [FAC_BASE(LOG_AUTH)] = { "auth", N_("security/authorization messages") },
99 [FAC_BASE(LOG_SYSLOG)] = { "syslog", N_("messages generated internally by syslogd") },
100 [FAC_BASE(LOG_LPR)] = { "lpr", N_("line printer subsystem") },
101 [FAC_BASE(LOG_NEWS)] = { "news", N_("network news subsystem") },
102 [FAC_BASE(LOG_UUCP)] = { "uucp", N_("UUCP subsystem") },
103 [FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") },
104 [FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") },
105 [FAC_BASE(LOG_FTP)] = { "ftp", N_("ftp daemon") },
106};
107
e6471b9f
KZ
108/* supported methods to read message buffer
109 */
110enum {
7af23060 111 DMESG_METHOD_KMSG, /* read messages from /dev/kmsg (default) */
e6471b9f
KZ
112 DMESG_METHOD_SYSLOG, /* klogctl() buffer */
113 DMESG_METHOD_MMAP /* mmap file with records (see --file) */
114};
115
aca1633a
KZ
116struct dmesg_control {
117 /* bit arrays -- see include/bitops.h */
118 char levels[ARRAY_SIZE(level_names) / NBBY + 1];
119 char facilities[ARRAY_SIZE(facility_names) / NBBY + 1];
120
bd304d92 121 struct timeval lasttime; /* last printed timestamp */
42fac79a 122 time_t boot_time; /* system boot time */
bd304d92 123
e6471b9f
KZ
124 int action; /* SYSLOG_ACTION_* */
125 int method; /* DMESG_METHOD_* */
126 size_t bufsize; /* size of buffer created by read_buffer() */
7af23060 127 int kmsg; /* /dev/kmsg file descriptor */
e6471b9f 128
c672220f
KZ
129 /*
130 * For the --file option we mmap whole file. The unnecessary (already
131 * printed) pages are always unmapped. The result is that we have in
455fe9a0 132 * memory only the currently used page(s).
c672220f 133 */
9b3a6984 134 char *filename;
c672220f
KZ
135 char *mmap_buff;
136 size_t pagesize;
137
9feec79c
KZ
138 unsigned int raw:1, /* raw mode */
139 fltr_lev:1, /* filter out by levels[] */
140 fltr_fac:1, /* filter out by facilities[] */
141 decode:1, /* use "facility: level: " prefix */
142 notime:1, /* don't print timestamp */
143 delta:1, /* show time deltas */
144 ctime:1; /* show human readable time */
aca1633a 145};
f4fa5b44 146
a7ee94f2
KZ
147struct dmesg_record {
148 const char *mesg;
149 size_t mesg_size;
150
151 int level;
152 int facility;
bd304d92 153 struct timeval tv;
a7ee94f2
KZ
154
155 const char *next; /* buffer with next unparsed record */
156 size_t next_size; /* size of the next buffer */
157};
158
4a3b7949 159static void __attribute__((__noreturn__)) usage(FILE *out)
15103c4b 160{
738767b9 161 size_t i;
f06ec64f 162
dcd16b0f
KZ
163 fputs(_("\nUsage:\n"), out);
164 fprintf(out,
165 _(" %s [options]\n"), program_invocation_short_name);
166
167 fputs(_("\nOptions:\n"), out);
168 fputs(_(" -C, --clear clear the kernel ring buffer\n"
169 " -c, --read-clear read and clear all messages\n"
170 " -D, --console-off disable printing messages to console\n"
171 " -d, --show-delta show time delta between printed messages\n"
172 " -E, --console-on enable printing messages to console\n"
c672220f 173 " -F, --file <file> use the file instead of the kernel log buffer\n"
dcd16b0f
KZ
174 " -f, --facility <list> restrict output to defined facilities\n"
175 " -h, --help display this help and exit\n"
176 " -k, --kernel display kernel messages\n"
177 " -l, --level <list> restrict output to defined levels\n"
178 " -n, --console-level <level> set level of messages printed to console\n"
179 " -r, --raw print the raw message buffer\n"
ed61acc2 180 " -S, --syslog force to use syslog(2) rather than /dev/kmsg\n"
dcd16b0f
KZ
181 " -s, --buffer-size <size> buffer size to query the kernel ring buffer\n"
182 " -T, --ctime show human readable timestamp (could be \n"
183 " inaccurate if you have used SUSPEND/RESUME)\n"
184 " -t, --notime don't print messages timestamp\n"
185 " -u, --userspace display userspace messages\n"
186 " -V, --version output version information and exit\n"
187 " -x, --decode decode facility and level to readable string\n"), out);
188
189 fputs(_("\nSupported log facilities:\n"), out);
f06ec64f 190 for (i = 0; i < ARRAY_SIZE(level_names); i++) {
85f3cc55
KZ
191 fprintf(stderr, " %7s - %s\n",
192 facility_names[i].name,
193 _(facility_names[i].help));
194 }
195
dcd16b0f 196 fputs(_("\nSupported log levels (priorities):\n"), out);
85f3cc55
KZ
197 for (i = 0; i < ARRAY_SIZE(level_names); i++) {
198 fprintf(stderr, " %7s - %s\n",
f06ec64f
KZ
199 level_names[i].name,
200 _(level_names[i].help));
201 }
202
203 fputc('\n', out);
204
4a3b7949 205 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
6dbe3af9
KZ
206}
207
5ef05369
KZ
208/*
209 * LEVEL ::= <number> | <name>
210 * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(level_names)
211 * <name> ::= case-insensitive text
212 */
5c8f6bc6 213static int parse_level(const char *str, size_t len)
f06ec64f 214{
5c8f6bc6 215 if (!str)
f06ec64f 216 return -1;
5c8f6bc6
KZ
217 if (!len)
218 len = strlen(str);
219 errno = 0;
f06ec64f 220
5c8f6bc6
KZ
221 if (isdigit(*str)) {
222 char *end = NULL;
738767b9 223 long x = strtol(str, &end, 10);
5c8f6bc6 224
738767b9
KZ
225 if (!errno && end && end > str && (size_t) (end - str) == len &&
226 x >= 0 && (size_t) x < ARRAY_SIZE(level_names))
227 return x;
5c8f6bc6 228 } else {
738767b9
KZ
229 size_t i;
230
5c8f6bc6
KZ
231 for (i = 0; i < ARRAY_SIZE(level_names); i++) {
232 const char *n = level_names[i].name;
233
234 if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
235 return i;
236 }
237 }
238
239 if (errno)
240 err(EXIT_FAILURE, _("failed to parse level '%s'"), str);
241
242 errx(EXIT_FAILURE, _("unknown level '%s'"), str);
f06ec64f
KZ
243 return -1;
244}
245
5ef05369
KZ
246/*
247 * FACILITY ::= <number> | <name>
248 * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(facility_names)
249 * <name> ::= case-insensitive text
250 */
0e24df3b
KZ
251static int parse_facility(const char *str, size_t len)
252{
0e24df3b
KZ
253 if (!str)
254 return -1;
255 if (!len)
256 len = strlen(str);
257 errno = 0;
258
259 if (isdigit(*str)) {
260 char *end = NULL;
738767b9 261 long x = strtol(str, &end, 10);
0e24df3b 262
738767b9
KZ
263 if (!errno && end && end > str && (size_t) (end - str) == len &&
264 x >= 0 && (size_t) x < ARRAY_SIZE(facility_names))
265 return x;
0e24df3b 266 } else {
738767b9
KZ
267 size_t i;
268
0e24df3b
KZ
269 for (i = 0; i < ARRAY_SIZE(facility_names); i++) {
270 const char *n = facility_names[i].name;
271
272 if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
273 return i;
274 }
275 }
276
277 if (errno)
278 err(EXIT_FAILURE, _("failed to parse facility '%s'"), str);
279
280 errx(EXIT_FAILURE, _("unknown facility '%s'"), str);
281 return -1;
282}
283
5ef05369
KZ
284/*
285 * Parses numerical prefix used for all messages in kernel ring buffer.
286 *
287 * Priorities/facilities are encoded into a single 32-bit quantity, where the
288 * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
289 * (0-big number).
290 *
291 * Note that the number has to end with '>' char.
292 */
636a6207
KZ
293static const char *parse_faclev(const char *str, int *fac, int *lev)
294{
295 long num;
296 char *end = NULL;
297
298 if (!str)
299 return str;
300
301 errno = 0;
302 num = strtol(str, &end, 10);
303
304 if (!errno && end && end > str) {
305 *fac = LOG_FAC(num);
306 *lev = LOG_PRI(num);
85f3cc55 307
738767b9 308 if (*lev < 0 || (size_t) *lev > ARRAY_SIZE(level_names))
85f3cc55 309 *lev = -1;
738767b9 310 if (*fac < 0 || (size_t) *fac > ARRAY_SIZE(facility_names))
85f3cc55 311 *fac = -1;
636a6207
KZ
312 return end + 1; /* skip '<' */
313 }
314
315 return str;
316}
317
bd304d92
KZ
318static const char *parse_timestamp(const char *str0, struct timeval *tv)
319{
320 const char *str = str0;
321 char *end = NULL;
322
323 if (!str0)
324 return str0;
325
326 errno = 0;
327 tv->tv_sec = strtol(str, &end, 10);
328
329 if (!errno && end && *end == '.' && *(end + 1)) {
330 str = end + 1;
331 end = NULL;
332 tv->tv_usec = strtol(str, &end, 10);
333 }
334 if (errno || !end || end == str || *end != ']')
335 return str0;
336
337 return end + 1; /* skip ']' */
338}
339
340
341static double time_diff(struct timeval *a, struct timeval *b)
342{
343 return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
344}
345
7ff1f63f 346static int get_syslog_buffer_size(void)
eed99b2a
KZ
347{
348 int n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
349
350 return n > 0 ? n : 0;
351}
352
42fac79a
KZ
353static time_t get_boot_time(void)
354{
355 struct sysinfo info;
356 struct timeval tv;
357
358 if (sysinfo(&info) != 0)
359 warn(_("sysinfo failed"));
360 else if (gettimeofday(&tv, NULL) != 0)
361 warn(_("gettimeofday failed"));
362 else
363 return tv.tv_sec -= info.uptime;
364 return 0;
365}
366
c672220f 367/*
7ff1f63f 368 * Reads messages from regular file by mmap
c672220f 369 */
7ff1f63f 370static ssize_t mmap_file_buffer(struct dmesg_control *ctl, char **buf)
c672220f
KZ
371{
372 struct stat st;
9b3a6984 373 int fd;
c672220f 374
9b3a6984
KZ
375 if (!ctl->filename)
376 return -1;
377
378 fd = open(ctl->filename, O_RDONLY);
c672220f 379 if (fd < 0)
9b3a6984 380 err(EXIT_FAILURE, _("cannot open %s"), ctl->filename);
c672220f 381 if (fstat(fd, &st))
9b3a6984 382 err(EXIT_FAILURE, _("stat failed %s"), ctl->filename);
c672220f
KZ
383
384 *buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
385 if (*buf == MAP_FAILED)
9b3a6984 386 err(EXIT_FAILURE, _("cannot mmap: %s"), ctl->filename);
c672220f
KZ
387 ctl->mmap_buff = *buf;
388 ctl->pagesize = getpagesize();
389 close(fd);
390
391 return st.st_size;
392}
393
5ef05369 394/*
7ff1f63f 395 * Reads messages from kernel ring buffer by klogctl()
5ef05369 396 */
7ff1f63f 397static ssize_t read_syslog_buffer(struct dmesg_control *ctl, char **buf)
65e3eed9
KZ
398{
399 size_t sz;
400 int rc = -1;
65e3eed9 401
e6471b9f
KZ
402 if (ctl->bufsize) {
403 sz = ctl->bufsize + 8;
65e3eed9 404 *buf = xmalloc(sz * sizeof(char));
e6471b9f 405 rc = klogctl(ctl->action, *buf, sz);
65e3eed9
KZ
406 } else {
407 sz = 16392;
408 while (1) {
409 *buf = xmalloc(sz * sizeof(char));
410 rc = klogctl(SYSLOG_ACTION_READ_ALL, *buf, sz);
738767b9
KZ
411 if (rc < 0)
412 break;
413 if ((size_t) rc != sz || sz > (1 << 28))
65e3eed9
KZ
414 break;
415 free(*buf);
416 *buf = NULL;
417 sz *= 4;
418 }
419
e6471b9f 420 if (rc > 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
65e3eed9
KZ
421 rc = klogctl(SYSLOG_ACTION_READ_CLEAR, *buf, sz);
422 }
423
424 return rc;
425}
426
7ff1f63f
KZ
427/*
428 * Top level function to read messages
429 */
e6471b9f
KZ
430static ssize_t read_buffer(struct dmesg_control *ctl, char **buf)
431{
432 ssize_t n = -1;
433
434 switch (ctl->method) {
435 case DMESG_METHOD_MMAP:
7ff1f63f 436 n = mmap_file_buffer(ctl, buf);
e6471b9f
KZ
437 break;
438 case DMESG_METHOD_SYSLOG:
439 if (!ctl->bufsize)
7ff1f63f 440 ctl->bufsize = get_syslog_buffer_size();
e6471b9f 441
7ff1f63f 442 n = read_syslog_buffer(ctl, buf);
e6471b9f
KZ
443 break;
444 }
445
446 return n;
447}
448
b8300c0a
KZ
449static int fwrite_hex(const char *buf, size_t size, FILE *out)
450{
738767b9 451 size_t i;
b8300c0a
KZ
452
453 for (i = 0; i < size; i++) {
454 int rc = fprintf(out, "\\x%02x", buf[i]);
455 if (rc < 0)
456 return rc;
457 }
458 return 0;
459}
460
5ef05369
KZ
461/*
462 * Prints to 'out' and non-printable chars are replaced with \x<hex> sequences.
463 */
b8300c0a
KZ
464static void safe_fwrite(const char *buf, size_t size, FILE *out)
465{
738767b9 466 size_t i;
b8300c0a
KZ
467#ifdef HAVE_WIDECHAR
468 mbstate_t s;
469 memset(&s, 0, sizeof (s));
470#endif
471 for (i = 0; i < size; i++) {
472 const char *p = buf + i;
473 int rc, hex = 0;
0720766e 474 size_t len = 1;
b8300c0a
KZ
475
476#ifdef HAVE_WIDECHAR
477 wchar_t wc;
0720766e 478 len = mbrtowc(&wc, p, size - i, &s);
b8300c0a
KZ
479
480 if (len == 0) /* L'\0' */
481 return;
482
730d5e77 483 if (len == (size_t)-1 || len == (size_t)-2) { /* invalid sequence */
b8300c0a
KZ
484 memset(&s, 0, sizeof (s));
485 len = hex = 1;
b8300c0a
KZ
486 } else if (len > 1 && !iswprint(wc)) { /* non-printable multibyte */
487 hex = 1;
b8300c0a 488 }
0720766e
PU
489 i += len - 1;
490#else
491 if (!isprint((unsigned int) *p) &&
492 !isspace((unsigned int) *p)) /* non-printable */
493 hex = 1;
494#endif
b8300c0a
KZ
495 if (hex)
496 rc = fwrite_hex(p, len, out);
497 else
85f3cc55 498 rc = fwrite(p, 1, len, out) != len;
b8300c0a
KZ
499 if (rc != 0)
500 err(EXIT_FAILURE, _("write failed"));
501 }
502}
503
7af23060
KZ
504static int get_next_syslog_record(struct dmesg_control *ctl,
505 struct dmesg_record *rec)
f4fa5b44 506{
738767b9 507 size_t i;
b8300c0a
KZ
508 const char *begin = NULL;
509
7af23060
KZ
510 if (ctl->method != DMESG_METHOD_MMAP &&
511 ctl->method != DMESG_METHOD_SYSLOG)
512 return -1;
513
a7ee94f2
KZ
514 if (!rec->next || !rec->next_size)
515 return 1;
f4fa5b44 516
a7ee94f2
KZ
517 rec->mesg = NULL;
518 rec->mesg_size = 0;
519 rec->facility = -1;
520 rec->level = -1;
bd304d92
KZ
521 rec->tv.tv_sec = 0;
522 rec->tv.tv_usec = 0;
a7ee94f2 523
c672220f
KZ
524 /*
525 * Unmap already printed file data from memory
526 */
527 if (ctl->mmap_buff && (size_t) (rec->next - ctl->mmap_buff) > ctl->pagesize) {
528 void *x = ctl->mmap_buff;
529
530 ctl->mmap_buff += ctl->pagesize;
531 munmap(x, ctl->pagesize);
532 }
533
a7ee94f2
KZ
534 for (i = 0; i < rec->next_size; i++) {
535 const char *p = rec->next + i;
536 const char *end = NULL;
b8300c0a
KZ
537
538 if (!begin)
539 begin = p;
a7ee94f2 540 if (i + 1 == rec->next_size) {
b8300c0a 541 end = p + 1;
f4fa5b44 542 i++;
22f69825
KZ
543 } else if (*p == '\n' && *(p + 1) == '<')
544 end = p;
545
a7ee94f2
KZ
546 if (begin && !*begin)
547 begin = NULL; /* zero(s) at the end of the buffer? */
b8300c0a
KZ
548 if (!begin || !end)
549 continue;
550 if (end <= begin)
551 continue; /* error or empty line? */
552
636a6207 553 if (*begin == '<') {
aca1633a 554 if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode) {
a7ee94f2
KZ
555 begin = parse_faclev(begin + 1, &rec->facility,
556 &rec->level);
636a6207
KZ
557 } else {
558 /* ignore facility and level */
559 while (begin < end) {
560 begin++;
561 if (*(begin - 1) == '>')
562 break;
563 }
b8300c0a
KZ
564 }
565 }
566
bd304d92
KZ
567 if (*begin == '[' && (*(begin + 1) == ' ' ||
568 isdigit(*(begin + 1)))) {
608d4501
KZ
569 if (ctl->delta || ctl->ctime) {
570 begin = parse_timestamp(begin + 1, &rec->tv);
571 } else if (ctl->notime) {
bd304d92
KZ
572 while (begin < end) {
573 begin++;
574 if (*(begin - 1) == ']')
575 break;
576 }
d74b8dfc 577 }
31c9099a
KZ
578 if (begin < end && *begin == ' ')
579 begin++;
a7ee94f2 580 }
d74b8dfc 581
a7ee94f2
KZ
582 rec->mesg = begin;
583 rec->mesg_size = end - begin;
b8300c0a 584
a7ee94f2
KZ
585 rec->next_size -= end - rec->next;
586 rec->next = rec->next_size > 0 ? end + 1 : NULL;
59202950
PU
587 if (rec->next_size > 0)
588 rec->next_size--;
a7ee94f2
KZ
589
590 return 0;
591 }
592
593 return 1;
594}
595
596static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec)
597{
598 if (ctl->fltr_lev && (rec->facility < 0 ||
599 !isset(ctl->levels, rec->level)))
600 return 0;
601
602 if (ctl->fltr_fac && (rec->facility < 0 ||
603 !isset(ctl->facilities, rec->facility)))
604 return 0;
605
606 return 1;
607}
608
7ff1f63f 609static void raw_print(struct dmesg_control *ctl, const char *buf, size_t size)
c672220f
KZ
610{
611 int lastc = '\n';
612
613 if (!ctl->mmap_buff) {
614 /*
615 * Print whole ring buffer
616 */
617 safe_fwrite(buf, size, stdout);
618 lastc = buf[size - 1];
619 } else {
620 /*
621 * Print file in small chunks to save memory
622 */
623 while (size) {
624 size_t sz = size > ctl->pagesize ? ctl->pagesize : size;
625 char *x = ctl->mmap_buff;
626
627 safe_fwrite(x, sz, stdout);
628 lastc = x[sz - 1];
629 size -= sz;
630 ctl->mmap_buff += sz;
631 munmap(x, sz);
632 }
633 }
634
635 if (lastc != '\n')
636 putchar('\n');
637}
638
7af23060
KZ
639static void print_record(struct dmesg_control *ctl, struct dmesg_record *rec)
640{
641 char tbuf[256];
642
643 if (!accept_record(ctl, rec))
644 return;
645
646 if (!rec->mesg_size) {
647 putchar('\n');
648 return;
649 }
650
651 if (ctl->decode && rec->level >= 0 && rec->facility >= 0)
652 printf("%-6s:%-6s: ", facility_names[rec->facility].name,
653 level_names[rec->level].name);
654
655 *tbuf = '\0';
656 if (ctl->ctime) {
657 time_t t = ctl->boot_time + rec->tv.tv_sec;
658 if (strftime(tbuf, sizeof(tbuf), "%a %b %e %H:%M:%S %Y",
659 localtime(&t)) == 0)
660 *tbuf = '\0';
661 }
662 if (ctl->delta) {
663 double delta = 0;
664
665 if (timerisset(&ctl->lasttime))
666 delta = time_diff(&rec->tv, &ctl->lasttime);
667 ctl->lasttime = rec->tv;
668
669 if (ctl->ctime && *tbuf)
670 printf("[%s ", tbuf);
671 else if (ctl->notime)
672 putchar('[');
673 else
674 printf("[%5d.%06d ", (int) rec->tv.tv_sec,
675 (int) rec->tv.tv_usec);
676 printf("<%12.06f>] ", delta);
677 } else if (ctl->ctime && *tbuf) {
678 printf("[%s] ", tbuf);
679 }
680
681 safe_fwrite(rec->mesg, rec->mesg_size, stdout);
682
683 if (*(rec->mesg + rec->mesg_size - 1) != '\n')
684 putchar('\n');
685}
686
a7ee94f2
KZ
687/*
688 * Prints the 'buf' kernel ring buffer; the messages are filtered out according
689 * to 'levels' and 'facilities' bitarrays.
690 */
7ff1f63f
KZ
691static void print_buffer(struct dmesg_control *ctl,
692 const char *buf, size_t size)
a7ee94f2
KZ
693{
694 struct dmesg_record rec = { .next = buf, .next_size = size };
695
696 if (ctl->raw) {
7ff1f63f 697 raw_print(ctl, buf, size);
a7ee94f2
KZ
698 return;
699 }
700
7af23060
KZ
701 while (get_next_syslog_record(ctl, &rec) == 0)
702 print_record(ctl, &rec);
703}
a7ee94f2 704
7af23060
KZ
705static int init_kmsg(struct dmesg_control *ctl)
706{
707 ctl->kmsg = open("/dev/kmsg", O_RDONLY|O_NONBLOCK);
708 return ctl->kmsg < 0 ? -1 : 0;
f4fa5b44
KZ
709}
710
15103c4b
MP
711int main(int argc, char *argv[])
712{
713 char *buf = NULL;
c672220f 714 ssize_t n;
df1dddf9 715 int c;
48c5c662 716 int console_level = 0;
9b3a6984 717 static struct dmesg_control ctl = {
e6471b9f 718 .filename = NULL,
7ff1f63f 719 .action = SYSLOG_ACTION_READ_ALL,
ed61acc2 720 .method = DMESG_METHOD_KMSG,
7af23060 721 .kmsg = -1,
9b3a6984 722 };
6dbe3af9 723
94920134
SK
724 enum {
725 EXCL_NONE,
726 EXCL_CLEAR,
727 EXCL_READ_CLEAR,
728 EXCL_CONSOLE_LEVEL,
729 EXCL_CONSOLE_ON,
730 EXCL_CONSOLE_OFF
731 };
732 int excl_any = EXCL_NONE;
733
4a3b7949 734 static const struct option longopts[] = {
4a3b7949 735 { "buffer-size", required_argument, NULL, 's' },
5ef05369 736 { "clear", no_argument, NULL, 'C' },
4a3b7949 737 { "console-level", required_argument, NULL, 'n' },
bd304d92
KZ
738 { "console-off", no_argument, NULL, 'D' },
739 { "console-on", no_argument, NULL, 'E' },
5ef05369 740 { "decode", no_argument, NULL, 'x' },
c672220f 741 { "file", required_argument, NULL, 'F' },
0e24df3b 742 { "facility", required_argument, NULL, 'f' },
4a3b7949 743 { "help", no_argument, NULL, 'h' },
25000180 744 { "kernel", no_argument, NULL, 'k' },
5ef05369 745 { "level", required_argument, NULL, 'l' },
ed61acc2 746 { "syslog", no_argument, NULL, 'S' },
5ef05369
KZ
747 { "raw", no_argument, NULL, 'r' },
748 { "read-clear", no_argument, NULL, 'c' },
bd304d92 749 { "show-delta", no_argument, NULL, 'd' },
42fac79a 750 { "ctime", no_argument, NULL, 'T' },
d74b8dfc 751 { "notime", no_argument, NULL, 't' },
25000180 752 { "userspace", no_argument, NULL, 'u' },
5ef05369 753 { "version", no_argument, NULL, 'V' },
4a3b7949
KZ
754 { NULL, 0, NULL, 0 }
755 };
756
df1dddf9
KZ
757 setlocale(LC_ALL, "");
758 bindtextdomain(PACKAGE, LOCALEDIR);
759 textdomain(PACKAGE);
efb8854f 760 atexit(close_stdout);
7eda085c 761
ed61acc2 762 while ((c = getopt_long(argc, argv, "CcDdEF:f:hkl:n:rSs:TtuVx",
aca1633a 763 longopts, NULL)) != -1) {
df1dddf9 764 switch (c) {
04199860 765 case 'C':
94920134 766 exclusive_option(&excl_any, EXCL_CLEAR, EXCL_ERROR);
e6471b9f 767 ctl.action = SYSLOG_ACTION_CLEAR;
04199860 768 break;
df1dddf9 769 case 'c':
94920134 770 exclusive_option(&excl_any, EXCL_READ_CLEAR, EXCL_ERROR);
e6471b9f 771 ctl.action = SYSLOG_ACTION_READ_CLEAR;
df1dddf9 772 break;
bd304d92 773 case 'D':
94920134 774 exclusive_option(&excl_any, EXCL_CONSOLE_OFF, EXCL_ERROR);
e6471b9f 775 ctl.action = SYSLOG_ACTION_CONSOLE_OFF;
2170174e 776 break;
bd304d92
KZ
777 case 'd':
778 ctl.delta = 1;
779 break;
780 case 'E':
94920134 781 exclusive_option(&excl_any, EXCL_CONSOLE_ON, EXCL_ERROR);
e6471b9f 782 ctl.action = SYSLOG_ACTION_CONSOLE_ON;
2170174e 783 break;
c672220f 784 case 'F':
9b3a6984 785 ctl.filename = optarg;
e6471b9f 786 ctl.method = DMESG_METHOD_MMAP;
c672220f 787 break;
0e24df3b 788 case 'f':
aca1633a 789 ctl.fltr_fac = 1;
c87638ad
KZ
790 if (string_to_bitarray(optarg,
791 ctl.facilities, parse_facility) < 0)
792 return EXIT_FAILURE;
0e24df3b 793 break;
5ef05369
KZ
794 case 'h':
795 usage(stdout);
df1dddf9 796 break;
25000180 797 case 'k':
aca1633a
KZ
798 ctl.fltr_fac = 1;
799 setbit(ctl.facilities, FAC_BASE(LOG_KERN));
25000180 800 break;
636a6207 801 case 'l':
aca1633a 802 ctl.fltr_lev= 1;
c87638ad
KZ
803 if (string_to_bitarray(optarg,
804 ctl.levels, parse_level) < 0)
805 return EXIT_FAILURE;
636a6207 806 break;
5ef05369 807 case 'n':
94920134 808 exclusive_option(&excl_any, EXCL_CONSOLE_LEVEL, EXCL_ERROR);
e6471b9f 809 ctl.action = SYSLOG_ACTION_CONSOLE_LEVEL;
5ef05369
KZ
810 console_level = parse_level(optarg, 0);
811 break;
11ea22d5 812 case 'r':
aca1633a 813 ctl.raw = 1;
11ea22d5 814 break;
ed61acc2
KZ
815 case 'S':
816 ctl.method = DMESG_METHOD_SYSLOG;
817 break;
df1dddf9 818 case 's':
e6471b9f
KZ
819 ctl.bufsize = strtou32_or_err(optarg,
820 _("invalid buffer size argument"));
821 if (ctl.bufsize < 4096)
822 ctl.bufsize = 4096;
df1dddf9 823 break;
42fac79a
KZ
824 case 'T':
825 ctl.boot_time = get_boot_time();
826 if (ctl.boot_time)
827 ctl.ctime = 1;
828 break;
d74b8dfc 829 case 't':
aca1633a 830 ctl.notime = 1;
d74b8dfc 831 break;
25000180 832 case 'u':
aca1633a 833 ctl.fltr_fac = 1;
738767b9 834 for (n = 1; (size_t) n < ARRAY_SIZE(facility_names); n++)
aca1633a 835 setbit(ctl.facilities, n);
25000180 836 break;
4a3b7949
KZ
837 case 'V':
838 printf(_("%s from %s\n"), program_invocation_short_name,
839 PACKAGE_STRING);
840 return EXIT_SUCCESS;
5ef05369 841 case 'x':
aca1633a 842 ctl.decode = 1;
4a3b7949 843 break;
df1dddf9
KZ
844 case '?':
845 default:
4a3b7949 846 usage(stderr);
df1dddf9
KZ
847 }
848 }
849 argc -= optind;
850 argv += optind;
25000180 851 n = 0;
e6c379eb 852
15103c4b 853 if (argc > 1)
4a3b7949 854 usage(stderr);
6dbe3af9 855
641986cf
KZ
856 if (ctl.raw && (ctl.fltr_lev || ctl.fltr_fac || ctl.delta ||
857 ctl.notime || ctl.ctime || ctl.decode))
858 errx(EXIT_FAILURE, _("--raw can't be used together with level, "
859 "facility, decode, delta, ctime or notime options"));
860
608d4501
KZ
861 if (ctl.notime && ctl.ctime)
862 errx(EXIT_FAILURE, _("--notime can't be used together with ctime "));
85f3cc55 863
e6471b9f 864 switch (ctl.action) {
48c5c662
KZ
865 case SYSLOG_ACTION_READ_ALL:
866 case SYSLOG_ACTION_READ_CLEAR:
7af23060
KZ
867 if (ctl.method == DMESG_METHOD_KMSG && init_kmsg(&ctl) != 0)
868 ctl.method = DMESG_METHOD_SYSLOG;
869
e6471b9f 870 n = read_buffer(&ctl, &buf);
48c5c662 871 if (n > 0)
7ff1f63f 872 print_buffer(&ctl, buf, n);
7ff1f63f 873 if (!ctl.mmap_buff)
c672220f 874 free(buf);
48c5c662 875 break;
04199860 876 case SYSLOG_ACTION_CLEAR:
2170174e
KZ
877 case SYSLOG_ACTION_CONSOLE_OFF:
878 case SYSLOG_ACTION_CONSOLE_ON:
e6471b9f 879 n = klogctl(ctl.action, NULL, 0);
04199860 880 break;
48c5c662 881 case SYSLOG_ACTION_CONSOLE_LEVEL:
e6471b9f 882 n = klogctl(ctl.action, NULL, console_level);
48c5c662
KZ
883 break;
884 default:
885 errx(EXIT_FAILURE, _("unsupported command"));
886 break;
df1dddf9 887 }
6dbe3af9 888
7af23060
KZ
889 if (ctl.kmsg >= 0)
890 close(ctl.kmsg);
891
7ff1f63f 892 if (n < 0 && ctl.method == DMESG_METHOD_SYSLOG)
15103c4b 893 err(EXIT_FAILURE, _("klogctl failed"));
6dbe3af9 894
15103c4b 895 return EXIT_SUCCESS;
6dbe3af9 896}