• 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  * 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.
11 USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
12 
13 config 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 
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("\033[%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;
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 (toys.optflags&FLAG_r) {
73     color(0);
74     printf("<%d>", facpri);
75   }
76 
77   // Format the time.
78   if (!(toys.optflags&FLAG_t)) {
79     color(32);
80     if (toys.optflags&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 
xklogctl(int type,char * buf,int len)98 static 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 
dmesg_cleanup(void)107 static void dmesg_cleanup(void)
108 {
109   color(0);
110 }
111 
dmesg_main(void)112 void 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 (toys.optflags & (FLAG_C|FLAG_n)) goto no_output;
119 
120   if (toys.optflags&FLAG_T) {
121     struct sysinfo info;
122 
123     sysinfo(&info);
124     TT.tea = time(0)-info.uptime;
125   }
126 
127   if (!(toys.optflags&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*!(toys.optflags&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))) && 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 
157 klogctl_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+(toys.optflags&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 
175 no_output:
176   // Set the log level?
177   if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.n);
178 
179   // Clear the buffer?
180   if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
181 }
182