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  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 n, s;
  38
  39  int use_color;
  40  time_t tea;
  41)
  42
  43static void color(int c)
  44{
  45  if (TT.use_color) printf("\e[%dm", c);
  46}
  47
  48static void format_message(char *msg, int new)
  49{
  50  unsigned long long time_s, time_us;
  51  int facpri, subsystem, pos;
  52  char *p, *text;
  53
  54  // The new /dev/kmsg and the old syslog(2) formats differ slightly.
  55  if (new) {
  56    if (sscanf(msg, "%u,%*u,%llu,%*[^;]; %n", &facpri, &time_us, &pos) != 2)
  57      return;
  58
  59    time_s = time_us/1000000;
  60    time_us %= 1000000;
  61  } else if (sscanf(msg, "<%u>[%llu.%llu] %n",
  62                    &facpri, &time_s, &time_us, &pos) != 3) return;
  63
  64  // Drop extras after end of message text.
  65  if ((p = strchr(text = msg+pos, '\n'))) *p = 0;
  66
  67  // Is there a subsystem? (The ": " is just a convention.)
  68  p = strstr(text, ": ");
  69  subsystem = p ? (p-text) : 0;
  70
  71  // To get "raw" output for /dev/kmsg we need to add priority to each line
  72  if (FLAG(r)) {
  73    color(0);
  74    printf("<%d>", facpri);
  75  }
  76
  77  // Format the time.
  78  if (!FLAG(t)) {
  79    color(32);
  80    if (FLAG(T)) {
  81      time_t t = TT.tea+time_s;
  82      char *ts = ctime(&t);
  83
  84      printf("[%.*s] ", (int)(strlen(ts)-1), ts);
  85    } else printf("[%5lld.%06lld] ", time_s, time_us);
  86  }
  87
  88  // Errors (or worse) are shown in red, subsystems are shown in yellow.
  89  if (subsystem) {
  90    color(33);
  91    printf("%.*s", subsystem, text);
  92    text += subsystem;
  93  }
  94  color(31*((facpri&7)<=3));
  95  xputs(text);
  96}
  97
  98static int xklogctl(int type, char *buf, int len)
  99{
 100  int rc = klogctl(type, buf, len);
 101
 102  if (rc<0) perror_exit("klogctl");
 103
 104  return rc;
 105}
 106
 107static void dmesg_cleanup(void)
 108{
 109  color(0);
 110}
 111
 112void dmesg_main(void)
 113{
 114  TT.use_color = isatty(1);
 115
 116  if (TT.use_color) sigatexit(dmesg_cleanup);
 117  // If we're displaying output, is it klogctl or /dev/kmsg?
 118  if (FLAG(C)||FLAG(n)) goto no_output;
 119
 120  if (FLAG(T)) {
 121    struct sysinfo info;
 122
 123    sysinfo(&info);
 124    TT.tea = time(0)-info.uptime;
 125  }
 126
 127  if (!FLAG(S)) {
 128    char msg[8193]; // CONSOLE_EXT_LOG_MAX+1
 129    ssize_t len;
 130    int fd;
 131
 132    // Each read returns one message. By default, we block when there are no
 133    // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
 134    fd = open("/dev/kmsg", O_RDONLY|(O_NONBLOCK*!FLAG(w)));
 135    if (fd == -1) goto klogctl_mode;
 136
 137    // SYSLOG_ACTION_CLEAR(5) doesn't actually remove anything from /dev/kmsg,
 138    // you need to seek to the last clear point.
 139    lseek(fd, 0, SEEK_DATA);
 140
 141    for (;;) {
 142      // why does /dev/kmesg return EPIPE instead of EAGAIN if oldest message
 143      // expires as we read it?
 144      if (-1==(len = read(fd, msg, sizeof(msg)-1)) && errno==EPIPE) continue;
 145      // read() from kmsg always fails on a pre-3.5 kernel.
 146      if (len==-1 && errno==EINVAL) goto klogctl_mode;
 147      if (len<1) break;
 148
 149      msg[len] = 0;
 150      format_message(msg, 1);
 151    }
 152    close(fd);
 153  } else {
 154    char *data, *to, *from, *end;
 155    int size;
 156
 157klogctl_mode:
 158    // Figure out how much data we need, and fetch it.
 159    if (!(size = TT.s)) size = xklogctl(10, 0, 0);
 160    data = from = xmalloc(size+1);
 161    data[size = xklogctl(3+FLAG(c), data, size)] = 0;
 162
 163    // Send each line to format_message.
 164    to = data + size;
 165    while (from < to) {
 166      if (!(end = memchr(from, '\n', to-from))) break;
 167      *end = 0;
 168      format_message(from, 0);
 169      from = end + 1;
 170    }
 171
 172    if (CFG_TOYBOX_FREE) free(data);
 173  }
 174
 175no_output:
 176  // Set the log level?
 177  if (FLAG(n)) xklogctl(8, 0, TT.n);
 178
 179  // Clear the buffer?
 180  if (FLAG(C)||FLAG(c)) xklogctl(5, 0, 0);
 181}
 182