toybox/toys/lsb/dmesg.c
<<
>>
Prefs
   1/* dmesg.c - display/control kernel ring buffer.
   2 *
   3 * Copyright 2006, 2007 Rob Landley <rob@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.
  11USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
  12
  13config 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
  36GLOBALS(
  37  long level;
  38  long size;
  39
  40  int use_color;
  41  time_t tea;
  42)
  43
  44static void color(int c)
  45{
  46  if (TT.use_color) printf("\033[%dm", c);
  47}
  48
  49static 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
  99static 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
 108static void dmesg_cleanup(void)
 109{
 110  color(0);
 111}
 112
 113void 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
 158klogctl_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
 176no_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