blob: 198f64a1ce94a7cde2c181da85aff497d83ebacf [file] [log] [blame]
/* dmesg.c - display/control kernel ring buffer.
*
* Copyright 2006, 2007 Rob Landley <rob@landley.net>
*
* 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 <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;
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);
}