diff options
author | Elliott Hughes <enh@google.com> | 2016-10-28 15:56:01 -0700 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2016-10-30 17:38:37 -0500 |
commit | a583afc812cf7be74ebab72294c8df485908ff04 (patch) | |
tree | 3ec35e7b58e99c121193e94a3249787510323ec2 | |
parent | 10e9abcb88be40b1382fca3d9010e46d4594996c (diff) | |
download | toybox-a583afc812cf7be74ebab72294c8df485908ff04.tar.gz |
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.
-rw-r--r-- | toys/lsb/dmesg.c | 148 |
1 files changed, 116 insertions, 32 deletions
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 <level markers>) -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); } |