/* dmesg.c - display/control kernel ring buffer. * * Copyright 2006, 2007 Rob Landley * * 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, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc]", TOYFLAG_BIN)) config DMESG bool "dmesg" default n 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 ) -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 GLOBALS( long level; long size; int use_color; struct sysinfo info; ) 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; unsigned long long 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. 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 to emulate the // historical syslog(2) format. if (toys.optflags&FLAG_r) printf("<%d>", facpri); // Format the time. if (!(toys.optflags&FLAG_t)) { color(32); if (toys.optflags&FLAG_T) { time_t t = (time(NULL) - TT.info.uptime) + time_s; char *ts = ctime(&t); printf("[%.*s] ", (int)(strlen(ts) - 1), ts); } else { printf("[%5lld.%06lld] ", time_s, time_us); } 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)) xputs(text); else { color(31); printf("%s", text); color(0); xputc('\n'); } } static int xklogctl(int type, char *buf, int len) { int rc = klogctl(type, buf, len); if (rc<0) perror_exit("klogctl"); return rc; } // 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. if (!(size = TT.size)) size = xklogctl(10, 0, 0); data = to = from = xmalloc(size+1); data[size = xklogctl(3 + (toys.optflags & FLAG_c), data, size)] = 0; // Break into messages (one per line) and send each one to format_message. to = data + size; while (from < to) { char *msg_end = memchr(from, '\n', (to-from)); if (!msg_end) break; *msg_end = '\0'; format_message(from, 0); from = msg_end + 1; } if (CFG_TOYBOX_FREE) free(data); } static void print_all(void) { if (toys.optflags&FLAG_T) sysinfo(&TT.info); if (toys.optflags&FLAG_S) return legacy_mode(); // http://kernel.org/doc/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)); // With /dev/kmsg, SYSLOG_ACTION_CLEAR (5) doesn't actually remove anything; // you need to seek to the last clear point. lseek(fd, 0, SEEK_DATA); while (1) { char msg[8192]; // CONSOLE_EXT_LOG_MAX. 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; format_message(msg, 1); } close(fd); } void dmesg_main(void) { TT.use_color = isatty(1); if (!(toys.optflags & (FLAG_C|FLAG_n))) print_all(); // 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); }