Home | History | Annotate | Download | only in lsb
      1 /* dmesg.c - display/control kernel ring buffer.
      2  *
      3  * Copyright 2006, 2007 Rob Landley <rob (at) landley.net>
      4  *
      5  * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
      6  *
      7  * Don't ask me why the horrible new dmesg API is still in "testing":
      8  * http://kernel.org/doc/Documentation/ABI/testing/dev-kmsg
      9 
     10 // We care that FLAG_c is 1, so keep c at the end.
     11 USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
     12 
     13 config DMESG
     14   bool "dmesg"
     15   default y
     16   help
     17     usage: dmesg [-Cc] [-r|-t|-T] [-n LEVEL] [-s SIZE] [-w]
     18 
     19     Print or control the kernel ring buffer.
     20 
     21     -C	Clear ring buffer without printing
     22     -c	Clear ring buffer after printing
     23     -n	Set kernel logging LEVEL (1-9)
     24     -r	Raw output (with <level markers>)
     25     -S	Use syslog(2) rather than /dev/kmsg
     26     -s	Show the last SIZE many bytes
     27     -T	Show human-readable timestamps
     28     -t	Don't print timestamps
     29     -w	Keep waiting for more output (aka --follow)
     30 */
     31 
     32 #define FOR_dmesg
     33 #include "toys.h"
     34 #include <sys/klog.h>
     35 
     36 GLOBALS(
     37   long level;
     38   long size;
     39 
     40   int use_color;
     41   time_t tea;
     42 )
     43 
     44 static void color(int c)
     45 {
     46   if (TT.use_color) printf("\033[%dm", c);
     47 }
     48 
     49 static void format_message(char *msg, int new)
     50 {
     51   unsigned long long time_s, time_us;
     52   int facpri, subsystem, pos;
     53   char *p, *text;
     54 
     55   // The new /dev/kmsg and the old syslog(2) formats differ slightly.
     56   if (new) {
     57     if (sscanf(msg, "%u,%*u,%llu,%*[^;]; %n", &facpri, &time_us, &pos) != 2)
     58       return;
     59 
     60     time_s = time_us/1000000;
     61     time_us %= 1000000;
     62   } else if (sscanf(msg, "<%u>[%llu.%llu] %n",
     63                     &facpri, &time_s, &time_us, &pos) != 3) return;
     64 
     65   // Drop extras after end of message text.
     66   if ((p = strchr(text = msg+pos, '\n'))) *p = 0;
     67 
     68   // Is there a subsystem? (The ": " is just a convention.)
     69   p = strstr(text, ": ");
     70   subsystem = p ? (p-text) : 0;
     71 
     72   // To get "raw" output for /dev/kmsg we need to add priority to each line
     73   if (toys.optflags&FLAG_r) {
     74     color(0);
     75     printf("<%d>", facpri);
     76   }
     77 
     78   // Format the time.
     79   if (!(toys.optflags&FLAG_t)) {
     80     color(32);
     81     if (toys.optflags&FLAG_T) {
     82       time_t t = TT.tea+time_s;
     83       char *ts = ctime(&t);
     84 
     85       printf("[%.*s] ", (int)(strlen(ts)-1), ts);
     86     } else printf("[%5lld.%06lld] ", time_s, time_us);
     87   }
     88 
     89   // Errors (or worse) are shown in red, subsystems are shown in yellow.
     90   if (subsystem) {
     91     color(33);
     92     printf("%.*s", subsystem, text);
     93     text += subsystem;
     94   }
     95   color(31*((facpri&7)<=3));
     96   xputs(text);
     97 }
     98 
     99 static int xklogctl(int type, char *buf, int len)
    100 {
    101   int rc = klogctl(type, buf, len);
    102 
    103   if (rc<0) perror_exit("klogctl");
    104 
    105   return rc;
    106 }
    107 
    108 static void dmesg_cleanup(void)
    109 {
    110   color(0);
    111 }
    112 
    113 void dmesg_main(void)
    114 {
    115   TT.use_color = isatty(1);
    116 
    117   if (TT.use_color) sigatexit(dmesg_cleanup);
    118   // If we're displaying output, is it klogctl or /dev/kmsg?
    119   if (toys.optflags & (FLAG_C|FLAG_n)) goto no_output;
    120 
    121   if (toys.optflags&FLAG_T) {
    122     struct sysinfo info;
    123 
    124     sysinfo(&info);
    125     TT.tea = time(0)-info.uptime;
    126   }
    127 
    128   if (!(toys.optflags&FLAG_S)) {
    129     char msg[8193]; // CONSOLE_EXT_LOG_MAX+1
    130     ssize_t len;
    131     int fd;
    132 
    133     // Each read returns one message. By default, we block when there are no
    134     // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
    135     fd = open("/dev/kmsg", O_RDONLY|(O_NONBLOCK*!(toys.optflags&FLAG_w)));
    136     if (fd == -1) goto klogctl_mode;
    137 
    138     // SYSLOG_ACTION_CLEAR(5) doesn't actually remove anything from /dev/kmsg,
    139     // you need to seek to the last clear point.
    140     lseek(fd, 0, SEEK_DATA);
    141 
    142     for (;;) {
    143       // why does /dev/kmesg return EPIPE instead of EAGAIN if oldest message
    144       // expires as we read it?
    145       if (-1==(len = read(fd, msg, sizeof(msg))) && errno==EPIPE) continue;
    146       // read() from kmsg always fails on a pre-3.5 kernel.
    147       if (len==-1 && errno==EINVAL) goto klogctl_mode;
    148       if (len<1) break;
    149 
    150       msg[len] = 0;
    151       format_message(msg, 1);
    152     }
    153     close(fd);
    154   } else {
    155     char *data, *to, *from, *end;
    156     int size;
    157 
    158 klogctl_mode:
    159     // Figure out how much data we need, and fetch it.
    160     if (!(size = TT.size)) size = xklogctl(10, 0, 0);
    161     data = from = xmalloc(size+1);
    162     data[size = xklogctl(3+(toys.optflags&FLAG_c), data, size)] = 0;
    163 
    164     // Send each line to format_message.
    165     to = data + size;
    166     while (from < to) {
    167       if (!(end = memchr(from, '\n', to-from))) break;
    168       *end = 0;
    169       format_message(from, 0);
    170       from = end + 1;
    171     }
    172 
    173     if (CFG_TOYBOX_FREE) free(data);
    174   }
    175 
    176 no_output:
    177   // Set the log level?
    178   if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.level);
    179 
    180   // Clear the buffer?
    181   if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
    182 }
    183