1/* dmesg.c - display/control kernel ring buffer.
2 *
3 * Copyright 2006, 2007 Rob Landley <rob@landley.net>
4 *
5 * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
6
7// We care that FLAG_c is 1, so keep c at the end.
8USE_DMESG(NEWTOY(dmesg, "w(follow)Ctrs#<1n#c[!tr][!Cc]", TOYFLAG_BIN))
9
10config DMESG
11  bool "dmesg"
12  default n
13  help
14    usage: dmesg [-Cc] [-r|-t] [-n LEVEL] [-s SIZE] [-w]
15
16    Print or control the kernel ring buffer.
17
18    -C	Clear ring buffer without printing
19    -c	Clear ring buffer after printing
20    -n	Set kernel logging LEVEL (1-9)
21    -r	Raw output (with <level markers>)
22    -s	Show the last SIZE many bytes
23    -t	Don't print kernel's timestamps
24    -w	Keep waiting for more output (aka --follow)
25*/
26
27#define FOR_dmesg
28#include "toys.h"
29#include <sys/klog.h>
30
31GLOBALS(
32  long level;
33  long size;
34
35  int color;
36)
37
38static int xklogctl(int type, char *buf, int len)
39{
40  int rc = klogctl(type, buf, len);
41
42  if (rc<0) perror_exit("klogctl");
43
44  return rc;
45}
46
47// Use klogctl for reading if we're on a pre-3.5 kernel.
48static void legacy_mode()
49{
50  char *data, *to, *from;
51  int size;
52
53  // Figure out how much data we need, and fetch it.
54  if (!(size = TT.size)) size = xklogctl(10, 0, 0);
55  data = to = from = xmalloc(size+1);
56  data[size = xklogctl(3 + (toys.optflags & FLAG_c), data, size)] = 0;
57
58  // Filter out level markers and optionally time markers
59  if (!(toys.optflags & FLAG_r)) while ((from - data) < size) {
60    if (from == data || from[-1] == '\n') {
61      char *to;
62
63      if (*from == '<' && (to = strchr(from, '>'))) from = ++to;
64      if ((toys.optflags&FLAG_t) && *from == '[' && (to = strchr(from, ']')))
65        from = to+1+(to[1]==' ');
66    }
67    *(to++) = *(from++);
68  } else to = data+size;
69
70  // Write result. The odds of somebody requesting a buffer of size 3 and
71  // getting "<1>" are remote, but don't segfault if they do.
72  if (to != data) {
73    xwrite(1, data, to-data);
74    if (to[-1] != '\n') xputc('\n');
75  }
76  if (CFG_TOYBOX_FREE) free(data);
77}
78
79static void color(int c)
80{
81  if (TT.color) printf("\033[%dm", c);
82}
83
84static void print_all(void)
85{
86  // http://kernel.org/doc/Documentation/ABI/testing/dev-kmsg
87
88  // Each read returns one message. By default, we block when there are no
89  // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
90  int fd = xopen("/dev/kmsg", O_RDONLY | ((toys.optflags&FLAG_w)?0:O_NONBLOCK));
91
92  // With /dev/kmsg, SYSLOG_ACTION_CLEAR (5) doesn't actually remove anything;
93  // you need to seek to the last clear point.
94  lseek(fd, 0, SEEK_DATA);
95
96  while (1) {
97    char msg[8192]; // CONSOLE_EXT_LOG_MAX.
98    unsigned long long time_us;
99    int facpri, subsystem, pos;
100    char *p, *text;
101    ssize_t len;
102
103    // kmsg fails with EPIPE if we try to read while the buffer moves under
104    // us; the next read will succeed and return the next available entry.
105    do {
106      len = read(fd, msg, sizeof(msg));
107    } while (len == -1 && errno == EPIPE);
108    // All reads from kmsg fail if you're on a pre-3.5 kernel.
109    if (len == -1 && errno == EINVAL) {
110      close(fd);
111      return legacy_mode();
112    }
113    if (len <= 0) break;
114
115    msg[len] = 0;
116
117    if (sscanf(msg, "%u,%*u,%llu,%*[^;];%n", &facpri, &time_us, &pos) != 2)
118      continue;
119
120    // Drop extras after end of message text.
121    text = msg + pos;
122    if ((p = strchr(text, '\n'))) *p = 0;
123
124    // Is there a subsystem? (The ": " is just a convention.)
125    p = strstr(text, ": ");
126    subsystem = p ? (p - text) : 0;
127
128    // "Raw" is a lie for /dev/kmsg. In practice, it just means we show the
129    // syslog facility/priority at the start of each line.
130    if (toys.optflags&FLAG_r) printf("<%d>", facpri);
131
132    if (!(toys.optflags&FLAG_t)) {
133      color(32);
134      printf("[%5lld.%06lld] ", time_us/1000000, time_us%1000000);
135      color(0);
136    }
137
138    // Errors (or worse) are shown in red, subsystems are shown in yellow.
139    if (subsystem) {
140      color(33);
141      printf("%.*s", subsystem, text);
142      text += subsystem;
143      color(0);
144    }
145    if (!((facpri&7) <= 3)) xputs(text);
146    else {
147      color(31);
148      printf("%s", text);
149      color(0);
150      xputc('\n');
151    }
152  }
153  close(fd);
154}
155
156void dmesg_main(void)
157{
158  TT.color = isatty(1);
159
160  if (!(toys.optflags & (FLAG_C|FLAG_n))) print_all();
161
162  // Set the log level?
163  if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.level);
164
165  // Clear the buffer?
166  if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
167}
168