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