From a583afc812cf7be74ebab72294c8df485908ff04 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 28 Oct 2016 15:56:01 -0700 Subject: Implement "dmesg -w". Annoyingly, this means writing a new dmesg implementation that uses /dev/kmsg rather than the klogctl system call. Worse, pre-3.5 kernels don't support that, so we need to keep the old implementation around as long as we still care about those kernels. Since I'm here, add the fancy colors from modern dmesg and the -C flag. Tested on Nexus 9's 3.10 kernel, an Ubuntu 3.13 kernel, and the Nexus One's 2.6.35 kernel. --- toys/lsb/dmesg.c | 148 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 32 deletions(-) (limited to 'toys/lsb') diff --git a/toys/lsb/dmesg.c b/toys/lsb/dmesg.c index d6084466..45319b60 100644 --- a/toys/lsb/dmesg.c +++ b/toys/lsb/dmesg.c @@ -5,21 +5,23 @@ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html // We care that FLAG_c is 1, so keep c at the end. -USE_DMESG(NEWTOY(dmesg, "trs#<1n#c[!tr]", TOYFLAG_BIN)) +USE_DMESG(NEWTOY(dmesg, "w(follow)Ctrs#<1n#c[!tr]", TOYFLAG_BIN)) config DMESG bool "dmesg" default y help - usage: dmesg [-c] [-r|-t] [-n LEVEL] [-s SIZE] + usage: dmesg [-Cc] [-r|-t] [-n LEVEL] [-s SIZE] [-w] Print or control the kernel ring buffer. + -C Clear the ring buffer -c Clear the ring buffer after printing -n Set kernel logging LEVEL (1-9) -r Raw output (with ) -s Show the last SIZE many bytes -t Don't print kernel's timestamps + -w Keep waiting for more output (aka --follow) */ #define FOR_dmesg @@ -29,43 +31,125 @@ config DMESG GLOBALS( long level; long size; + + int color; ) +// Use klogctl for reading if we're on a pre-3.5 kernel. +static void legacy_mode() { + char *data, *to, *from; + int size; + + // Figure out how much data we need, and fetch it. + size = TT.size; + if (!size && 1>(size = klogctl(10, 0, 0))) perror_exit("klogctl");; + data = to = from = xmalloc(size+1); + size = klogctl(3 + (toys.optflags & FLAG_c), data, size); + if (size < 0) perror_exit("klogctl"); + data[size] = 0; + + // Filter out level markers and optionally time markers + if (!(toys.optflags & FLAG_r)) while ((from - data) < size) { + if (from == data || from[-1] == '\n') { + char *to; + + if (*from == '<' && (to = strchr(from, '>'))) from = ++to; + if ((toys.optflags&FLAG_t) && *from == '[' && (to = strchr(from, ']'))) + from = to+1+(to[1]==' '); + } + *(to++) = *(from++); + } else to = data+size; + + // Write result. The odds of somebody requesting a buffer of size 3 and + // getting "<1>" are remote, but don't segfault if they do. + if (to != data) { + xwrite(1, data, to-data); + if (to[-1] != '\n') xputc('\n'); + } + if (CFG_TOYBOX_FREE) free(data); +} + +static void color(int c) { + if (TT.color) printf("\033[%dm", c); +} + void dmesg_main(void) { // For -n just tell kernel to which messages to keep. if (toys.optflags & FLAG_n) { if (klogctl(8, NULL, TT.level)) perror_exit("klogctl"); - } else { - char *data, *to, *from; - int size; - - // Figure out how much data we need, and fetch it. - size = TT.size; - if (!size && 1>(size = klogctl(10, 0, 0))) perror_exit("klogctl");; - data = to = from = xmalloc(size+1); - size = klogctl(3 + (toys.optflags & FLAG_c), data, size); - if (size < 0) perror_exit("klogctl"); - data[size] = 0; - - // Filter out level markers and optionally time markers - if (!(toys.optflags & FLAG_r)) while ((from - data) < size) { - if (from == data || from[-1] == '\n') { - char *to; - - if (*from == '<' && (to = strchr(from, '>'))) from = ++to; - if ((toys.optflags&FLAG_t) && *from == '[' && (to = strchr(from, ']'))) - from = to+1+(to[1]==' '); - } - *(to++) = *(from++); - } else to = data+size; - - // Write result. The odds of somebody requesting a buffer of size 3 and - // getting "<1>" are remote, but don't segfault if they do. - if (to != data) { - xwrite(1, data, to-data); - if (to[-1] != '\n') xputc('\n'); + return; + } + + // For -C just tell kernel to throw everything out. + if (toys.optflags & FLAG_C) { + if (klogctl(5, NULL, 0)) perror_exit("klogctl"); + return; + } + + TT.color = isatty(1); + + // http://lxr.free-electrons.com/source/Documentation/ABI/testing/dev-kmsg + + // Each read returns one message. By default, we block when there are no + // more messages (--follow); O_NONBLOCK is needed for for usual behavior. + int fd = xopen("/dev/kmsg", O_RDONLY | ((toys.optflags&FLAG_w)?0:O_NONBLOCK)); + while (1) { + char msg[8192]; // CONSOLE_EXT_LOG_MAX. + unsigned long long time_us; + int facpri, subsystem, pos; + char *p, *text; + ssize_t len; + + // kmsg fails with EPIPE if we try to read while the buffer moves under + // us; the next read will succeed and return the next available entry. + do { + len = read(fd, msg, sizeof(msg)); + } while (len == -1 && errno == EPIPE); + // All reads from kmsg fail if you're on a pre-3.5 kernel. + if (len == -1 && errno == EINVAL) { + close(fd); + return legacy_mode(); + } + if (len <= 0) break; + + msg[len] = 0; + + if (sscanf(msg, "%u,%*u,%llu,%*[^;];%n", &facpri, &time_us, &pos) != 2) + continue; + + // Drop extras after end of message text. + text = msg + pos; + if ((p = strchr(text, '\n'))) *p = 0; + + // Is there a subsystem? (The ": " is just a convention.) + p = strstr(text, ": "); + subsystem = p ? (p - text) : 0; + + // "Raw" is a lie for /dev/kmsg. In practice, it just means we show the + // syslog facility/priority at the start of each line. + if (toys.optflags&FLAG_r) printf("<%d>", facpri); + + if (!(toys.optflags&FLAG_t)) { + color(32); + printf("[%5lld.%06lld] ", time_us/1000000, time_us%1000000); + color(0); + } + + // Errors (or worse) are shown in red, subsystems are shown in yellow. + if (subsystem) { + color(33); + printf("%.*s", subsystem, text); + text += subsystem; + color(0); + } + if (!((facpri&7) <= 3)) puts(text); + else { + color(31); + printf("%s", text); + color(0); + xputc('\n'); } - if (CFG_TOYBOX_FREE) free(data); } + close(fd); } -- cgit v1.2.3