1
/* dmesg.c -- Print out the contents of the kernel ring buffer
2
* Created: Sat Oct 9 16:19:47 1993
3
* Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu
4
* Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu)
2
* dmesg.c -- Print out the contents of the kernel ring buffer
4
* Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu>
5
* Copyright (C) 2011 Karel Zak <kzak@redhat.com>
5
7
* This program comes with ABSOLUTELY NO WARRANTY.
6
* Modifications by Rick Sladkey (jrs@world.std.com)
7
* Larger buffersize 3 June 1998 by Nicolai Langfeldt, based on a patch
8
* by Peeter Joot. This was also suggested by John Hudson.
9
* 1999-02-22 Arkadiusz Miļæ½kiewicz <misiek@pld.ORG.PL>
10
* - added Native Language Support
15
* Commands to sys_syslog:
17
* 0 -- Close the log. Currently a NOP.
18
* 1 -- Open the log. Currently a NOP.
19
* 2 -- Read from the log.
20
* 3 -- Read all messages remaining in the ring buffer.
21
* 4 -- Read and clear all messages remaining in the ring buffer
22
* 5 -- Clear ring buffer.
23
* 6 -- Disable printk's to console
24
* 7 -- Enable printk's to console
25
* 8 -- Set level of messages printed to console
26
* 9 -- Return number of unread characters in the log buffer
27
* [supported since 2.4.10]
29
* Only function 3 is allowed to non-root processes.
32
9
#include <linux/unistd.h>
34
11
#include <getopt.h>
35
12
#include <stdlib.h>
36
13
#include <sys/klog.h>
14
#include <sys/syslog.h>
16
#include <sys/sysinfo.h>
39
22
#include "strutils.h"
41
static char *progname;
46
_("Usage: %s [-c] [-n level] [-r] [-s bufsize]\n"), progname);
50
main(int argc, char *argv[]) {
28
/* Close the log. Currently a NOP. */
29
#define SYSLOG_ACTION_CLOSE 0
30
/* Open the log. Currently a NOP. */
31
#define SYSLOG_ACTION_OPEN 1
32
/* Read from the log. */
33
#define SYSLOG_ACTION_READ 2
34
/* Read all messages remaining in the ring buffer. (allowed for non-root) */
35
#define SYSLOG_ACTION_READ_ALL 3
36
/* Read and clear all messages remaining in the ring buffer */
37
#define SYSLOG_ACTION_READ_CLEAR 4
38
/* Clear ring buffer. */
39
#define SYSLOG_ACTION_CLEAR 5
40
/* Disable printk's to console */
41
#define SYSLOG_ACTION_CONSOLE_OFF 6
42
/* Enable printk's to console */
43
#define SYSLOG_ACTION_CONSOLE_ON 7
44
/* Set level of messages printed to console */
45
#define SYSLOG_ACTION_CONSOLE_LEVEL 8
46
/* Return number of unread characters in the log buffer */
47
#define SYSLOG_ACTION_SIZE_UNREAD 9
48
/* Return size of the log buffer */
49
#define SYSLOG_ACTION_SIZE_BUFFER 10
52
* Priority and facility names
60
* Priority names -- based on sys/syslog.h
62
static const struct dmesg_name level_names[] =
64
[LOG_EMERG] = { "emerg", N_("system is unusable") },
65
[LOG_ALERT] = { "alert", N_("action must be taken immediately") },
66
[LOG_CRIT] = { "crit", N_("critical conditions") },
67
[LOG_ERR] = { "err", N_("error conditions") },
68
[LOG_WARNING] = { "warn", N_("warning conditions") },
69
[LOG_NOTICE] = { "notice",N_("normal but significant condition") },
70
[LOG_INFO] = { "info", N_("informational") },
71
[LOG_DEBUG] = { "debug", N_("debug-level messages") }
75
* sys/syslog.h uses (f << 3) for all facility codes.
76
* We want to use the codes as array idexes, so shift back...
78
* Note that libc LOG_FAC() macro returns the base codes, not the
81
#define FAC_BASE(f) ((f) >> 3)
83
static const struct dmesg_name facility_names[] =
85
[FAC_BASE(LOG_KERN)] = { "kern", N_("kernel messages") },
86
[FAC_BASE(LOG_USER)] = { "user", N_("random user-level messages") },
87
[FAC_BASE(LOG_MAIL)] = { "mail", N_("mail system") },
88
[FAC_BASE(LOG_DAEMON)] = { "daemon", N_("system daemons") },
89
[FAC_BASE(LOG_AUTH)] = { "auth", N_("security/authorization messages") },
90
[FAC_BASE(LOG_SYSLOG)] = { "syslog", N_("messages generated internally by syslogd") },
91
[FAC_BASE(LOG_LPR)] = { "lpr", N_("line printer subsystem") },
92
[FAC_BASE(LOG_NEWS)] = { "news", N_("network news subsystem") },
93
[FAC_BASE(LOG_UUCP)] = { "uucp", N_("UUCP subsystem") },
94
[FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") },
95
[FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") },
96
[FAC_BASE(LOG_FTP)] = { "ftp", N_("ftp daemon") },
99
struct dmesg_control {
100
/* bit arrays -- see include/bitops.h */
101
char levels[ARRAY_SIZE(level_names) / NBBY + 1];
102
char facilities[ARRAY_SIZE(facility_names) / NBBY + 1];
104
struct timeval lasttime; /* last printed timestamp */
105
time_t boot_time; /* system boot time */
107
unsigned int raw:1, /* raw mode */
108
fltr_lev:1, /* filter out by levels[] */
109
fltr_fac:1, /* filter out by facilities[] */
110
decode:1, /* use "facility: level: " prefix */
111
notime:1, /* don't print timestamp */
112
delta:1, /* show time deltas */
113
ctime:1; /* show human readable time */
116
struct dmesg_record {
124
const char *next; /* buffer with next unparsed record */
125
size_t next_size; /* size of the next buffer */
128
static void __attribute__((__noreturn__)) usage(FILE *out)
132
fputs(_("\nUsage:\n"), out);
134
_(" %s [options]\n"), program_invocation_short_name);
136
fputs(_("\nOptions:\n"), out);
137
fputs(_(" -C, --clear clear the kernel ring buffer\n"
138
" -c, --read-clear read and clear all messages\n"
139
" -D, --console-off disable printing messages to console\n"
140
" -d, --show-delta show time delta between printed messages\n"
141
" -E, --console-on enable printing messages to console\n"
142
" -f, --facility <list> restrict output to defined facilities\n"
143
" -h, --help display this help and exit\n"
144
" -k, --kernel display kernel messages\n"
145
" -l, --level <list> restrict output to defined levels\n"
146
" -n, --console-level <level> set level of messages printed to console\n"
147
" -r, --raw print the raw message buffer\n"
148
" -s, --buffer-size <size> buffer size to query the kernel ring buffer\n"
149
" -T, --ctime show human readable timestamp (could be \n"
150
" inaccurate if you have used SUSPEND/RESUME)\n"
151
" -t, --notime don't print messages timestamp\n"
152
" -u, --userspace display userspace messages\n"
153
" -V, --version output version information and exit\n"
154
" -x, --decode decode facility and level to readable string\n"), out);
156
fputs(_("\nSupported log facilities:\n"), out);
157
for (i = 0; i < ARRAY_SIZE(level_names); i++) {
158
fprintf(stderr, " %7s - %s\n",
159
facility_names[i].name,
160
_(facility_names[i].help));
163
fputs(_("\nSupported log levels (priorities):\n"), out);
164
for (i = 0; i < ARRAY_SIZE(level_names); i++) {
165
fprintf(stderr, " %7s - %s\n",
167
_(level_names[i].help));
172
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
176
* LEVEL ::= <number> | <name>
177
* <number> ::= number in range <0..N>, where N < ARRAY_SIZE(level_names)
178
* <name> ::= case-insensitive text
180
static int parse_level(const char *str, size_t len)
190
long x = strtol(str, &end, 10);
192
if (!errno && end && end > str && (size_t) (end - str) == len &&
193
x >= 0 && (size_t) x < ARRAY_SIZE(level_names))
198
for (i = 0; i < ARRAY_SIZE(level_names); i++) {
199
const char *n = level_names[i].name;
201
if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
207
err(EXIT_FAILURE, _("failed to parse level '%s'"), str);
209
errx(EXIT_FAILURE, _("unknown level '%s'"), str);
214
* FACILITY ::= <number> | <name>
215
* <number> ::= number in range <0..N>, where N < ARRAY_SIZE(facility_names)
216
* <name> ::= case-insensitive text
218
static int parse_facility(const char *str, size_t len)
228
long x = strtol(str, &end, 10);
230
if (!errno && end && end > str && (size_t) (end - str) == len &&
231
x >= 0 && (size_t) x < ARRAY_SIZE(facility_names))
236
for (i = 0; i < ARRAY_SIZE(facility_names); i++) {
237
const char *n = facility_names[i].name;
239
if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
245
err(EXIT_FAILURE, _("failed to parse facility '%s'"), str);
247
errx(EXIT_FAILURE, _("unknown facility '%s'"), str);
252
* Parses numerical prefix used for all messages in kernel ring buffer.
254
* Priorities/facilities are encoded into a single 32-bit quantity, where the
255
* bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
258
* Note that the number has to end with '>' char.
260
static const char *parse_faclev(const char *str, int *fac, int *lev)
269
num = strtol(str, &end, 10);
271
if (!errno && end && end > str) {
275
if (*lev < 0 || (size_t) *lev > ARRAY_SIZE(level_names))
277
if (*fac < 0 || (size_t) *fac > ARRAY_SIZE(facility_names))
279
return end + 1; /* skip '<' */
285
static const char *parse_timestamp(const char *str0, struct timeval *tv)
287
const char *str = str0;
294
tv->tv_sec = strtol(str, &end, 10);
296
if (!errno && end && *end == '.' && *(end + 1)) {
299
tv->tv_usec = strtol(str, &end, 10);
301
if (errno || !end || end == str || *end != ']')
304
return end + 1; /* skip ']' */
308
static double time_diff(struct timeval *a, struct timeval *b)
310
return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
313
static int get_buffer_size()
315
int n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
317
return n > 0 ? n : 0;
320
static time_t get_boot_time(void)
325
if (sysinfo(&info) != 0)
326
warn(_("sysinfo failed"));
327
else if (gettimeofday(&tv, NULL) != 0)
328
warn(_("gettimeofday failed"));
330
return tv.tv_sec -= info.uptime;
335
* Reads messages from kernel ring buffer
337
static int read_buffer(char **buf, size_t bufsize, int clear)
341
int cmd = clear ? SYSLOG_ACTION_READ_CLEAR :
342
SYSLOG_ACTION_READ_ALL;
346
*buf = xmalloc(sz * sizeof(char));
347
rc = klogctl(cmd, *buf, sz);
351
*buf = xmalloc(sz * sizeof(char));
352
rc = klogctl(SYSLOG_ACTION_READ_ALL, *buf, sz);
355
if ((size_t) rc != sz || sz > (1 << 28))
363
rc = klogctl(SYSLOG_ACTION_READ_CLEAR, *buf, sz);
369
static int fwrite_hex(const char *buf, size_t size, FILE *out)
373
for (i = 0; i < size; i++) {
374
int rc = fprintf(out, "\\x%02x", buf[i]);
382
* Prints to 'out' and non-printable chars are replaced with \x<hex> sequences.
384
static void safe_fwrite(const char *buf, size_t size, FILE *out)
389
memset(&s, 0, sizeof (s));
391
for (i = 0; i < size; i++) {
392
const char *p = buf + i;
397
size_t len = mbrtowc(&wc, p, size - i, &s);
399
if (len == 0) /* L'\0' */
402
if (len == (size_t)-1 || len == (size_t)-2) { /* invalid sequence */
403
memset(&s, 0, sizeof (s));
406
} else if (len > 1 && !iswprint(wc)) { /* non-printable multibyte */
411
if (!isprint((unsigned int) *p) &&
412
!isspace((unsigned int) *p)) /* non-printable */
416
rc = fwrite_hex(p, len, out);
418
rc = fwrite(p, 1, len, out) != len;
420
err(EXIT_FAILURE, _("write failed"));
424
static int get_next_record(struct dmesg_control *ctl, struct dmesg_record *rec)
427
const char *begin = NULL;
429
if (!rec->next || !rec->next_size)
439
for (i = 0; i < rec->next_size; i++) {
440
const char *p = rec->next + i;
441
const char *end = NULL;
445
if (i + 1 == rec->next_size) {
448
} else if (*p == '\n' && *(p + 1) == '<')
451
if (begin && !*begin)
452
begin = NULL; /* zero(s) at the end of the buffer? */
456
continue; /* error or empty line? */
459
if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode) {
460
begin = parse_faclev(begin + 1, &rec->facility,
463
/* ignore facility and level */
464
while (begin < end) {
466
if (*(begin - 1) == '>')
472
if (*begin == '[' && (*(begin + 1) == ' ' ||
473
isdigit(*(begin + 1)))) {
474
if (ctl->delta || ctl->ctime) {
475
begin = parse_timestamp(begin + 1, &rec->tv);
476
} else if (ctl->notime) {
477
while (begin < end) {
479
if (*(begin - 1) == ']')
483
if (begin < end && *begin == ' ')
488
rec->mesg_size = end - begin;
490
rec->next_size -= end - rec->next;
491
rec->next = rec->next_size > 0 ? end + 1 : NULL;
492
if (rec->next_size > 0)
501
static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec)
503
if (ctl->fltr_lev && (rec->facility < 0 ||
504
!isset(ctl->levels, rec->level)))
507
if (ctl->fltr_fac && (rec->facility < 0 ||
508
!isset(ctl->facilities, rec->facility)))
515
* Prints the 'buf' kernel ring buffer; the messages are filtered out according
516
* to 'levels' and 'facilities' bitarrays.
518
static void print_buffer(const char *buf, size_t size,
519
struct dmesg_control *ctl)
521
struct dmesg_record rec = { .next = buf, .next_size = size };
525
/* print whole buffer */
526
safe_fwrite(buf, size, stdout);
527
if (buf[size - 1] != '\n')
532
while (get_next_record(ctl, &rec) == 0) {
534
if (!accept_record(ctl, &rec))
537
if (!rec.mesg_size) {
542
if (ctl->decode && rec.level >= 0 && rec.facility >= 0)
543
printf("%-6s:%-6s: ", facility_names[rec.facility].name,
544
level_names[rec.level].name);
548
time_t t = ctl->boot_time + rec.tv.tv_sec;
549
if (strftime(tbuf, sizeof(tbuf), "%a %b %e %H:%M:%S %Y",
556
if (timerisset(&ctl->lasttime))
557
delta = time_diff(&rec.tv, &ctl->lasttime);
558
ctl->lasttime = rec.tv;
560
if (ctl->ctime && *tbuf)
561
printf("[%s ", tbuf);
562
else if (ctl->notime)
565
printf("[%5d.%06d ", (int) rec.tv.tv_sec,
566
(int) rec.tv.tv_usec);
567
printf("<%12.06f>] ", delta);
568
} else if (ctl->ctime && *tbuf) {
569
printf("[%s] ", tbuf);
572
safe_fwrite(rec.mesg, rec.mesg_size, stdout);
574
if (*(rec.mesg + rec.mesg_size - 1) != '\n')
579
int main(int argc, char *argv[])
59
int cmd = 3; /* Read all messages in the ring buffer */
585
int console_level = 0;
587
static struct dmesg_control ctl;
589
static const struct option longopts[] = {
590
{ "buffer-size", required_argument, NULL, 's' },
591
{ "clear", no_argument, NULL, 'C' },
592
{ "console-level", required_argument, NULL, 'n' },
593
{ "console-off", no_argument, NULL, 'D' },
594
{ "console-on", no_argument, NULL, 'E' },
595
{ "decode", no_argument, NULL, 'x' },
596
{ "facility", required_argument, NULL, 'f' },
597
{ "help", no_argument, NULL, 'h' },
598
{ "kernel", no_argument, NULL, 'k' },
599
{ "level", required_argument, NULL, 'l' },
600
{ "raw", no_argument, NULL, 'r' },
601
{ "read-clear", no_argument, NULL, 'c' },
602
{ "show-delta", no_argument, NULL, 'd' },
603
{ "ctime", no_argument, NULL, 'T' },
604
{ "notime", no_argument, NULL, 't' },
605
{ "userspace", no_argument, NULL, 'u' },
606
{ "version", no_argument, NULL, 'V' },
62
610
setlocale(LC_ALL, "");
63
611
bindtextdomain(PACKAGE, LOCALEDIR);
64
612
textdomain(PACKAGE);
67
while ((c = getopt(argc, argv, "crn:s:")) != -1) {
614
while ((c = getopt_long(argc, argv, "CcDdEf:hkl:n:rs:TtuVx",
615
longopts, NULL)) != -1) {
617
if (cmd != -1 && strchr("CcnDE", c))
618
errx(EXIT_FAILURE, _("clear, read-clear, console-level, "
619
"console-on, and console-off options are mutually "
624
cmd = SYSLOG_ACTION_CLEAR;
70
cmd = 4; /* Read and clear all messages */
627
cmd = SYSLOG_ACTION_READ_CLEAR;
630
cmd = SYSLOG_ACTION_CONSOLE_OFF;
636
cmd = SYSLOG_ACTION_CONSOLE_ON;
640
if (string_to_bitarray(optarg,
641
ctl.facilities, parse_facility) < 0)
649
setbit(ctl.facilities, FAC_BASE(LOG_KERN));
653
if (string_to_bitarray(optarg,
654
ctl.levels, parse_level) < 0)
73
cmd = 8; /* Set level of messages */
74
level = strtol_or_err(optarg, _("failed to parse level"));
658
cmd = SYSLOG_ACTION_CONSOLE_LEVEL;
659
console_level = parse_level(optarg, 0);
80
bufsize = strtol_or_err(optarg, _("failed to parse buffer size"));
665
bufsize = strtol_or_err(optarg,
666
_("failed to parse buffer size"));
81
667
if (bufsize < 4096)
671
ctl.boot_time = get_boot_time();
680
for (n = 1; (size_t) n < ARRAY_SIZE(facility_names); n++)
681
setbit(ctl.facilities, n);
684
printf(_("%s from %s\n"), program_invocation_short_name,
99
n = klogctl(cmd, NULL, level);
108
n = klogctl(10, NULL, 0); /* read ringbuffer size */
703
cmd = SYSLOG_ACTION_READ_ALL; /* default */
705
if (ctl.raw && (ctl.fltr_lev || ctl.fltr_fac || ctl.delta ||
706
ctl.notime || ctl.ctime || ctl.decode))
707
errx(EXIT_FAILURE, _("--raw can't be used together with level, "
708
"facility, decode, delta, ctime or notime options"));
710
if (ctl.notime && ctl.ctime)
711
errx(EXIT_FAILURE, _("--notime can't be used together with ctime "));
714
case SYSLOG_ACTION_READ_ALL:
715
case SYSLOG_ACTION_READ_CLEAR:
717
bufsize = get_buffer_size();
718
n = read_buffer(&buf, bufsize, cmd == SYSLOG_ACTION_READ_CLEAR);
115
buf = (char *) malloc(sz * sizeof(char));
116
n = klogctl(cmd, buf, sz);
120
buf = (char *) malloc(sz * sizeof(char));
121
n = klogctl(3, buf, sz); /* read only */
122
if (n != sz || sz > (1<<28))
128
if (n > 0 && cmd == 4)
129
n = klogctl(cmd, buf, sz); /* read and clear */
138
for (i = 0; i < n; i++) {
139
if (!raw && (i == 0 || buf[i - 1] == '\n') && buf[i] == '<') {
141
while (buf[i] >= '0' && buf[i] <= '9')
720
print_buffer(buf, n, &ctl);
723
case SYSLOG_ACTION_CLEAR:
724
case SYSLOG_ACTION_CONSOLE_OFF:
725
case SYSLOG_ACTION_CONSOLE_ON:
726
n = klogctl(cmd, NULL, 0);
728
case SYSLOG_ACTION_CONSOLE_LEVEL:
729
n = klogctl(cmd, NULL, console_level);
732
errx(EXIT_FAILURE, _("unsupported command"));
737
err(EXIT_FAILURE, _("klogctl failed"));