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