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