aboutsummaryrefslogtreecommitdiff
path: root/toys/lsb
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2017-06-09 15:01:07 -0500
committerRob Landley <rob@landley.net>2017-06-09 15:01:07 -0500
commit1f33eeb683e729681419c29ac831c6e71e6774fa (patch)
tree2a2e3e1dadbf0d2a97aeba161a17646a7d41a225 /toys/lsb
parent6fbe288e988f87b23b610da8c5046910b6171eb9 (diff)
downloadtoybox-1f33eeb683e729681419c29ac831c6e71e6774fa.tar.gz
Promote dmesg back to lsb.
Diffstat (limited to 'toys/lsb')
-rw-r--r--toys/lsb/dmesg.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/toys/lsb/dmesg.c b/toys/lsb/dmesg.c
new file mode 100644
index 00000000..30f90e3a
--- /dev/null
+++ b/toys/lsb/dmesg.c
@@ -0,0 +1,182 @@
+/* dmesg.c - display/control kernel ring buffer.
+ *
+ * Copyright 2006, 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
+ *
+ * Don't ask me why the horrible new dmesg API is still in "testing":
+ * http://kernel.org/doc/Documentation/ABI/testing/dev-kmsg
+
+// We care that FLAG_c is 1, so keep c at the end.
+USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
+
+config DMESG
+ bool "dmesg"
+ default y
+ help
+ usage: dmesg [-Cc] [-r|-t|-T] [-n LEVEL] [-s SIZE] [-w]
+
+ Print or control the kernel ring buffer.
+
+ -C Clear ring buffer without printing
+ -c Clear ring buffer after printing
+ -n Set kernel logging LEVEL (1-9)
+ -r Raw output (with <level markers>)
+ -S Use syslog(2) rather than /dev/kmsg
+ -s Show the last SIZE many bytes
+ -T Show human-readable timestamps
+ -t Don't print timestamps
+ -w Keep waiting for more output (aka --follow)
+*/
+
+#define FOR_dmesg
+#include "toys.h"
+#include <sys/klog.h>
+
+GLOBALS(
+ long level;
+ long size;
+
+ int use_color;
+ time_t tea;
+)
+
+static void color(int c)
+{
+ if (TT.use_color) printf("\033[%dm", c);
+}
+
+static void format_message(char *msg, int new)
+{
+ unsigned long long time_s, time_us;
+ int facpri, subsystem, pos;
+ char *p, *text;
+
+ // The new /dev/kmsg and the old syslog(2) formats differ slightly.
+ if (new) {
+ if (sscanf(msg, "%u,%*u,%llu,%*[^;]; %n", &facpri, &time_us, &pos) != 2)
+ return;
+
+ time_s = time_us/1000000;
+ time_us %= 1000000;
+ } else if (sscanf(msg, "<%u>[%llu.%llu] %n",
+ &facpri, &time_s, &time_us, &pos) != 3) return;
+
+ // Drop extras after end of message text.
+ if ((p = strchr(text = msg+pos, '\n'))) *p = 0;
+
+ // Is there a subsystem? (The ": " is just a convention.)
+ p = strstr(text, ": ");
+ subsystem = p ? (p-text) : 0;
+
+ // To get "raw" output for /dev/kmsg we need to add priority to each line
+ if (toys.optflags&FLAG_r) {
+ color(0);
+ printf("<%d>", facpri);
+ }
+
+ // Format the time.
+ if (!(toys.optflags&FLAG_t)) {
+ color(32);
+ if (toys.optflags&FLAG_T) {
+ time_t t = TT.tea+time_s;
+ char *ts = ctime(&t);
+
+ printf("[%.*s] ", (int)(strlen(ts)-1), ts);
+ } else printf("[%5lld.%06lld] ", time_s, time_us);
+ }
+
+ // Errors (or worse) are shown in red, subsystems are shown in yellow.
+ if (subsystem) {
+ color(33);
+ printf("%.*s", subsystem, text);
+ text += subsystem;
+ }
+ color(31*((facpri&7)<=3));
+ xputs(text);
+}
+
+static int xklogctl(int type, char *buf, int len)
+{
+ int rc = klogctl(type, buf, len);
+
+ if (rc<0) perror_exit("klogctl");
+
+ return rc;
+}
+
+static void dmesg_cleanup(void)
+{
+ color(0);
+}
+
+void dmesg_main(void)
+{
+ TT.use_color = isatty(1);
+
+ if (TT.use_color) sigatexit(dmesg_cleanup);
+ // If we're displaying output, is it klogctl or /dev/kmsg?
+ if (toys.optflags & (FLAG_C|FLAG_n)) goto no_output;
+
+ if (toys.optflags&FLAG_T) {
+ struct sysinfo info;
+
+ sysinfo(&info);
+ TT.tea = time(0)-info.uptime;
+ }
+
+ if (!(toys.optflags&FLAG_S)) {
+ char msg[8193]; // CONSOLE_EXT_LOG_MAX+1
+ ssize_t len;
+ int fd;
+
+ // Each read returns one message. By default, we block when there are no
+ // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
+ fd = open("/dev/kmsg", O_RDONLY|(O_NONBLOCK*!(toys.optflags&FLAG_w)));
+ if (fd == -1) goto klogctl_mode;
+
+ // SYSLOG_ACTION_CLEAR(5) doesn't actually remove anything from /dev/kmsg,
+ // you need to seek to the last clear point.
+ lseek(fd, 0, SEEK_DATA);
+
+ for (;;) {
+ // why does /dev/kmesg return EPIPE instead of EAGAIN if oldest message
+ // expires as we read it?
+ if (-1==(len = read(fd, msg, sizeof(msg))) && errno==EPIPE) continue;
+ // read() from kmsg always fails on a pre-3.5 kernel.
+ if (len==-1 && errno==EINVAL) goto klogctl_mode;
+ if (len<1) break;
+
+ msg[len] = 0;
+ format_message(msg, 1);
+ }
+ close(fd);
+ } else {
+ char *data, *to, *from, *end;
+ int size;
+
+klogctl_mode:
+ // Figure out how much data we need, and fetch it.
+ if (!(size = TT.size)) size = xklogctl(10, 0, 0);
+ data = from = xmalloc(size+1);
+ data[size = xklogctl(3+(toys.optflags&FLAG_c), data, size)] = 0;
+
+ // Send each line to format_message.
+ to = data + size;
+ while (from < to) {
+ if (!(end = memchr(from, '\n', to-from))) break;
+ *end = 0;
+ format_message(from, 0);
+ from = end + 1;
+ }
+
+ if (CFG_TOYBOX_FREE) free(data);
+ }
+
+no_output:
+ // Set the log level?
+ if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.level);
+
+ // Clear the buffer?
+ if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
+}