You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
4.4 KiB
182 lines
4.4 KiB
/* 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 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 n, s;
|
|
|
|
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 (FLAG(r)) {
|
|
color(0);
|
|
printf("<%d>", facpri);
|
|
}
|
|
|
|
// Format the time.
|
|
if (!FLAG(t)) {
|
|
color(32);
|
|
if (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 (FLAG(C)||FLAG(n)) goto no_output;
|
|
|
|
if (FLAG(T)) {
|
|
struct sysinfo info;
|
|
|
|
sysinfo(&info);
|
|
TT.tea = time(0)-info.uptime;
|
|
}
|
|
|
|
if (!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*!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.s)) size = xklogctl(10, 0, 0);
|
|
data = from = xmalloc(size+1);
|
|
data[size = xklogctl(3+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 (FLAG(n)) xklogctl(8, 0, TT.n);
|
|
|
|
// Clear the buffer?
|
|
if (FLAG(C)||FLAG(c)) xklogctl(5, 0, 0);
|
|
}
|