xref: /third_party/toybox/toys/lsb/dmesg.c (revision 0f66f451)
1/* dmesg.c - display/control kernel ring buffer.
2 *
3 * Copyright 2006, 2007 Rob Landley <rob@landley.net>
4 *
5 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
6 *
7 * Don't ask me why the horrible new dmesg API is still in "testing":
8 * http://kernel.org/doc/Documentation/ABI/testing/dev-kmsg
9
10// We care that FLAG_c is 1, so keep c at the end.
11USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
12
13config DMESG
14  bool "dmesg"
15  default y
16  help
17    usage: dmesg [-Cc] [-r|-t|-T] [-n LEVEL] [-s SIZE] [-w]
18
19    Print or control the kernel ring buffer.
20
21    -C	Clear ring buffer without printing
22    -c	Clear ring buffer after printing
23    -n	Set kernel logging LEVEL (1-9)
24    -r	Raw output (with <level markers>)
25    -S	Use syslog(2) rather than /dev/kmsg
26    -s	Show the last SIZE many bytes
27    -T	Human readable timestamps
28    -t	Don't print timestamps
29    -w	Keep waiting for more output (aka --follow)
30*/
31
32#define FOR_dmesg
33#include "toys.h"
34#include <sys/klog.h>
35
36GLOBALS(
37  long n, s;
38
39  int use_color;
40  time_t tea;
41)
42
43static void color(int c)
44{
45  if (TT.use_color) printf("\033[%dm", c);
46}
47
48static void format_message(char *msg, int new)
49{
50  unsigned long long time_s, time_us;
51  int facpri, subsystem, pos;
52  char *p, *text;
53
54  // The new /dev/kmsg and the old syslog(2) formats differ slightly.
55  if (new) {
56    if (sscanf(msg, "%u,%*u,%llu,%*[^;]; %n", &facpri, &time_us, &pos) != 2)
57      return;
58
59    time_s = time_us/1000000;
60    time_us %= 1000000;
61  } else if (sscanf(msg, "<%u>[%llu.%llu] %n",
62                    &facpri, &time_s, &time_us, &pos) != 3) return;
63
64  // Drop extras after end of message text.
65  if ((p = strchr(text = msg+pos, '\n'))) *p = 0;
66
67  // Is there a subsystem? (The ": " is just a convention.)
68  p = strstr(text, ": ");
69  subsystem = p ? (p-text) : 0;
70
71  // To get "raw" output for /dev/kmsg we need to add priority to each line
72  if (FLAG(r)) {
73    color(0);
74    printf("<%d>", facpri);
75  }
76
77  // Format the time.
78  if (!FLAG(t)) {
79    color(32);
80    if (FLAG(T)) {
81      time_t t = TT.tea+time_s;
82      char *ts = ctime(&t);
83
84      printf("[%.*s] ", (int)(strlen(ts)-1), ts);
85    } else printf("[%5lld.%06lld] ", time_s, time_us);
86  }
87
88  // Errors (or worse) are shown in red, subsystems are shown in yellow.
89  if (subsystem) {
90    color(33);
91    printf("%.*s", subsystem, text);
92    text += subsystem;
93  }
94  color(31*((facpri&7)<=3));
95  xputs(text);
96}
97
98static int xklogctl(int type, char *buf, int len)
99{
100  int rc = klogctl(type, buf, len);
101
102  if (rc<0) perror_exit("klogctl");
103
104  return rc;
105}
106
107static void dmesg_cleanup(void)
108{
109  color(0);
110}
111
112void dmesg_main(void)
113{
114  TT.use_color = isatty(1);
115
116  if (TT.use_color) sigatexit(dmesg_cleanup);
117  // If we're displaying output, is it klogctl or /dev/kmsg?
118  if (FLAG(C)||FLAG(n)) goto no_output;
119
120  if (FLAG(T)) {
121    struct sysinfo info;
122
123    sysinfo(&info);
124    TT.tea = time(0)-info.uptime;
125  }
126
127  if (!FLAG(S)) {
128    char msg[8193]; // CONSOLE_EXT_LOG_MAX+1
129    ssize_t len;
130    int fd;
131
132    // Each read returns one message. By default, we block when there are no
133    // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
134    fd = open("/dev/kmsg", O_RDONLY|(O_NONBLOCK*!FLAG(w)));
135    if (fd == -1) goto klogctl_mode;
136
137    // SYSLOG_ACTION_CLEAR(5) doesn't actually remove anything from /dev/kmsg,
138    // you need to seek to the last clear point.
139    lseek(fd, 0, SEEK_DATA);
140
141    for (;;) {
142      // why does /dev/kmesg return EPIPE instead of EAGAIN if oldest message
143      // expires as we read it?
144      if (-1==(len = read(fd, msg, sizeof(msg)-1)) && errno==EPIPE) continue;
145      // read() from kmsg always fails on a pre-3.5 kernel.
146      if (len==-1 && errno==EINVAL) goto klogctl_mode;
147      if (len<1) break;
148
149      msg[len] = 0;
150      format_message(msg, 1);
151    }
152    close(fd);
153  } else {
154    char *data, *to, *from, *end;
155    int size;
156
157klogctl_mode:
158    // Figure out how much data we need, and fetch it.
159    if (!(size = TT.s)) size = xklogctl(10, 0, 0);
160    data = from = xmalloc(size+1);
161    data[size = xklogctl(3+FLAG(c), data, size)] = 0;
162
163    // Send each line to format_message.
164    to = data + size;
165    while (from < to) {
166      if (!(end = memchr(from, '\n', to-from))) break;
167      *end = 0;
168      format_message(from, 0);
169      from = end + 1;
170    }
171
172    if (CFG_TOYBOX_FREE) free(data);
173  }
174
175no_output:
176  // Set the log level?
177  if (FLAG(n)) xklogctl(8, 0, TT.n);
178
179  // Clear the buffer?
180  if (FLAG(C)||FLAG(c)) xklogctl(5, 0, 0);
181}
182