Home | History | Annotate | Download | only in pending
      1 /* dmesg.c - display/control kernel ring buffer.
      2  *
      3  * Copyright 2006, 2007 Rob Landley <rob (at) landley.net>
      4  *
      5  * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
      6 
      7 // We care that FLAG_c is 1, so keep c at the end.
      8 USE_DMESG(NEWTOY(dmesg, "w(follow)Ctrs#<1n#c[!tr][!Cc]", TOYFLAG_BIN))
      9 
     10 config DMESG
     11   bool "dmesg"
     12   default n
     13   help
     14     usage: dmesg [-Cc] [-r|-t] [-n LEVEL] [-s SIZE] [-w]
     15 
     16     Print or control the kernel ring buffer.
     17 
     18     -C	Clear ring buffer without printing
     19     -c	Clear ring buffer after printing
     20     -n	Set kernel logging LEVEL (1-9)
     21     -r	Raw output (with <level markers>)
     22     -s	Show the last SIZE many bytes
     23     -t	Don't print kernel's timestamps
     24     -w	Keep waiting for more output (aka --follow)
     25 */
     26 
     27 #define FOR_dmesg
     28 #include "toys.h"
     29 #include <sys/klog.h>
     30 
     31 GLOBALS(
     32   long level;
     33   long size;
     34 
     35   int color;
     36 )
     37 
     38 static int xklogctl(int type, char *buf, int len)
     39 {
     40   int rc = klogctl(type, buf, len);
     41 
     42   if (rc<0) perror_exit("klogctl");
     43 
     44   return rc;
     45 }
     46 
     47 // Use klogctl for reading if we're on a pre-3.5 kernel.
     48 static void legacy_mode()
     49 {
     50   char *data, *to, *from;
     51   int size;
     52 
     53   // Figure out how much data we need, and fetch it.
     54   if (!(size = TT.size)) size = xklogctl(10, 0, 0);
     55   data = to = from = xmalloc(size+1);
     56   data[size = xklogctl(3 + (toys.optflags & FLAG_c), data, size)] = 0;
     57 
     58   // Filter out level markers and optionally time markers
     59   if (!(toys.optflags & FLAG_r)) while ((from - data) < size) {
     60     if (from == data || from[-1] == '\n') {
     61       char *to;
     62 
     63       if (*from == '<' && (to = strchr(from, '>'))) from = ++to;
     64       if ((toys.optflags&FLAG_t) && *from == '[' && (to = strchr(from, ']')))
     65         from = to+1+(to[1]==' ');
     66     }
     67     *(to++) = *(from++);
     68   } else to = data+size;
     69 
     70   // Write result. The odds of somebody requesting a buffer of size 3 and
     71   // getting "<1>" are remote, but don't segfault if they do.
     72   if (to != data) {
     73     xwrite(1, data, to-data);
     74     if (to[-1] != '\n') xputc('\n');
     75   }
     76   if (CFG_TOYBOX_FREE) free(data);
     77 }
     78 
     79 static void color(int c)
     80 {
     81   if (TT.color) printf("\033[%dm", c);
     82 }
     83 
     84 static void print_all(void)
     85 {
     86   // http://kernel.org/doc/Documentation/ABI/testing/dev-kmsg
     87 
     88   // Each read returns one message. By default, we block when there are no
     89   // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
     90   int fd = xopen("/dev/kmsg", O_RDONLY | ((toys.optflags&FLAG_w)?0:O_NONBLOCK));
     91 
     92   // With /dev/kmsg, SYSLOG_ACTION_CLEAR (5) doesn't actually remove anything;
     93   // you need to seek to the last clear point.
     94   lseek(fd, 0, SEEK_DATA);
     95 
     96   while (1) {
     97     char msg[8192]; // CONSOLE_EXT_LOG_MAX.
     98     unsigned long long time_us;
     99     int facpri, subsystem, pos;
    100     char *p, *text;
    101     ssize_t len;
    102 
    103     // kmsg fails with EPIPE if we try to read while the buffer moves under
    104     // us; the next read will succeed and return the next available entry.
    105     do {
    106       len = read(fd, msg, sizeof(msg));
    107     } while (len == -1 && errno == EPIPE);
    108     // All reads from kmsg fail if you're on a pre-3.5 kernel.
    109     if (len == -1 && errno == EINVAL) {
    110       close(fd);
    111       return legacy_mode();
    112     }
    113     if (len <= 0) break;
    114 
    115     msg[len] = 0;
    116 
    117     if (sscanf(msg, "%u,%*u,%llu,%*[^;];%n", &facpri, &time_us, &pos) != 2)
    118       continue;
    119 
    120     // Drop extras after end of message text.
    121     text = msg + pos;
    122     if ((p = strchr(text, '\n'))) *p = 0;
    123 
    124     // Is there a subsystem? (The ": " is just a convention.)
    125     p = strstr(text, ": ");
    126     subsystem = p ? (p - text) : 0;
    127 
    128     // "Raw" is a lie for /dev/kmsg. In practice, it just means we show the
    129     // syslog facility/priority at the start of each line.
    130     if (toys.optflags&FLAG_r) printf("<%d>", facpri);
    131 
    132     if (!(toys.optflags&FLAG_t)) {
    133       color(32);
    134       printf("[%5lld.%06lld] ", time_us/1000000, time_us%1000000);
    135       color(0);
    136     }
    137 
    138     // Errors (or worse) are shown in red, subsystems are shown in yellow.
    139     if (subsystem) {
    140       color(33);
    141       printf("%.*s", subsystem, text);
    142       text += subsystem;
    143       color(0);
    144     }
    145     if (!((facpri&7) <= 3)) xputs(text);
    146     else {
    147       color(31);
    148       printf("%s", text);
    149       color(0);
    150       xputc('\n');
    151     }
    152   }
    153   close(fd);
    154 }
    155 
    156 void dmesg_main(void)
    157 {
    158   TT.color = isatty(1);
    159 
    160   if (!(toys.optflags & (FLAG_C|FLAG_n))) print_all();
    161 
    162   // Set the log level?
    163   if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.level);
    164 
    165   // Clear the buffer?
    166   if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
    167 }
    168