]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
dmesg: add --file option
authorKarel Zak <kzak@redhat.com>
Thu, 8 Sep 2011 17:40:34 +0000 (19:40 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 8 Sep 2011 17:40:34 +0000 (19:40 +0200)
This new option is mostly for testing (to test raw logs from another
boxes/kernels).

Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/dmesg.1
sys-utils/dmesg.c

index 3dc18cb0a9b8a282dbd568ce60195ab0cb5f12fc..9e90ca5db330fa8eaf7ded6d51ab252f8131707a 100644 (file)
@@ -39,6 +39,8 @@ Display the timestamp and time delta spent between messages. If used together
 with --notime then only the time delta without the timestamp is printed.
 .IP "\fB\-E, \-\-console-on\fP"
 Enable printing messages to the console.
+.IP "\fB\-F, \-\-file \fIfile\fP"
+Read log from file.
 .IP "\fB\-f, \-\-facility \fIlist\fP"
 Restrict output to defined (comma separated) list of facilities. For example
 .sp
index b3713a0a110e5f86e4a860fa71d4b1ac766d851c..c6e242f2e4e3ac9c3574c753756f4e50532e252e 100644 (file)
 #include <sys/sysinfo.h>
 #include <ctype.h>
 #include <time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "c.h"
 #include "nls.h"
@@ -104,6 +109,14 @@ struct dmesg_control {
        struct timeval  lasttime;       /* last printed timestamp */
        time_t          boot_time;      /* system boot time */
 
+       /*
+        * For the --file option we mmap whole file. The unnecessary (already
+        * printed) pages are always unmapped. The result is that we have in
+        * memory only the currenly used page(s).
+        */
+       char            *mmap_buff;
+       size_t          pagesize;
+
        unsigned int    raw:1,          /* raw mode */
                        fltr_lev:1,     /* filter out by levels[] */
                        fltr_fac:1,     /* filter out by facilities[] */
@@ -139,6 +152,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
                " -D, --console-off           disable printing messages to console\n"
                " -d, --show-delta            show time delta between printed messages\n"
                " -E, --console-on            enable printing messages to console\n"
+               " -F, --file <file>           use the file instead of the kernel log buffer\n"
                " -f, --facility <list>       restrict output to defined facilities\n"
                " -h, --help                  display this help and exit\n"
                " -k, --kernel                display kernel messages\n"
@@ -331,10 +345,34 @@ static time_t get_boot_time(void)
        return 0;
 }
 
+/*
+ * mmap file with the log
+ */
+static ssize_t read_file_buffer(struct dmesg_control *ctl,
+                               char **buf, const char *filename)
+{
+       struct stat st;
+       int fd = open(filename, O_RDONLY);
+
+       if (fd < 0)
+               err(EXIT_FAILURE, _("cannot open: %s"), filename);
+       if (fstat(fd, &st))
+               err(EXIT_FAILURE, _("cannot stat: %s"), filename);
+
+       *buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (*buf == MAP_FAILED)
+               err(EXIT_FAILURE, _("cannot mmap: %s"), filename);
+       ctl->mmap_buff = *buf;
+       ctl->pagesize = getpagesize();
+       close(fd);
+
+       return st.st_size;
+}
+
 /*
  * Reads messages from kernel ring buffer
  */
-static int read_buffer(char **buf, size_t bufsize, int clear)
+static int read_kernel_buffer(char **buf, size_t bufsize, int clear)
 {
        size_t sz;
        int rc = -1;
@@ -436,6 +474,16 @@ static int get_next_record(struct dmesg_control *ctl, struct dmesg_record *rec)
        rec->tv.tv_sec = 0;
        rec->tv.tv_usec = 0;
 
+       /*
+        * Unmap already printed file data from memory
+        */
+       if (ctl->mmap_buff && (size_t) (rec->next - ctl->mmap_buff) > ctl->pagesize) {
+               void *x = ctl->mmap_buff;
+
+               ctl->mmap_buff += ctl->pagesize;
+               munmap(x, ctl->pagesize);
+       }
+
        for (i = 0; i < rec->next_size; i++) {
                const char *p = rec->next + i;
                const char *end = NULL;
@@ -511,6 +559,37 @@ static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec)
        return 1;
 }
 
+static void raw_print(const char *buf, size_t size,
+                     struct dmesg_control *ctl)
+{
+       int lastc = '\n';
+
+       if (!ctl->mmap_buff) {
+               /*
+                * Print whole ring buffer
+                */
+               safe_fwrite(buf, size, stdout);
+               lastc = buf[size - 1];
+       } else {
+               /*
+                * Print file in small chunks to save memory
+                */
+               while (size) {
+                       size_t sz = size > ctl->pagesize ? ctl->pagesize : size;
+                       char *x = ctl->mmap_buff;
+
+                       safe_fwrite(x, sz, stdout);
+                       lastc = x[sz - 1];
+                       size -= sz;
+                       ctl->mmap_buff += sz;
+                       munmap(x, sz);
+               }
+       }
+
+       if (lastc != '\n')
+               putchar('\n');
+}
+
 /*
  * Prints the 'buf' kernel ring buffer; the messages are filtered out according
  * to 'levels' and 'facilities' bitarrays.
@@ -522,10 +601,7 @@ static void print_buffer(const char *buf, size_t size,
        char tbuf[256];
 
        if (ctl->raw) {
-               /* print whole buffer */
-               safe_fwrite(buf, size, stdout);
-               if (buf[size - 1] != '\n')
-                       putchar('\n');
+               raw_print(buf, size, ctl);
                return;
        }
 
@@ -579,8 +655,9 @@ static void print_buffer(const char *buf, size_t size,
 int main(int argc, char *argv[])
 {
        char *buf = NULL;
+       const char *filename = NULL;
        int  bufsize = 0;
-       int  n;
+       ssize_t  n;
        int  c;
        int  console_level = 0;
        int  cmd = -1;
@@ -593,6 +670,7 @@ int main(int argc, char *argv[])
                { "console-off",   no_argument,       NULL, 'D' },
                { "console-on",    no_argument,       NULL, 'E' },
                { "decode",        no_argument,       NULL, 'x' },
+               { "file",          required_argument, NULL, 'F' },
                { "facility",      required_argument, NULL, 'f' },
                { "help",          no_argument,       NULL, 'h' },
                { "kernel",        no_argument,       NULL, 'k' },
@@ -611,7 +689,7 @@ int main(int argc, char *argv[])
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
 
-       while ((c = getopt_long(argc, argv, "CcDdEf:hkl:n:rs:TtuVx",
+       while ((c = getopt_long(argc, argv, "CcDdEF:f:hkl:n:rs:TtuVx",
                                longopts, NULL)) != -1) {
 
                if (cmd != -1 && strchr("CcnDE", c))
@@ -635,6 +713,9 @@ int main(int argc, char *argv[])
                case 'E':
                        cmd = SYSLOG_ACTION_CONSOLE_ON;
                        break;
+               case 'F':
+                       filename = optarg;
+                       break;
                case 'f':
                        ctl.fltr_fac = 1;
                        if (string_to_bitarray(optarg,
@@ -713,12 +794,18 @@ int main(int argc, char *argv[])
        switch (cmd) {
        case SYSLOG_ACTION_READ_ALL:
        case SYSLOG_ACTION_READ_CLEAR:
-               if (!bufsize)
-                       bufsize = get_buffer_size();
-               n = read_buffer(&buf, bufsize, cmd == SYSLOG_ACTION_READ_CLEAR);
+               if (filename)
+                       n = read_file_buffer(&ctl, &buf, filename);
+               else  {
+                       if (!bufsize)
+                               bufsize = get_buffer_size();
+                       n = read_kernel_buffer(&buf, bufsize,
+                                       cmd == SYSLOG_ACTION_READ_CLEAR);
+               }
                if (n > 0)
                        print_buffer(buf, n, &ctl);
-               free(buf);
+               if (!filename)
+                       free(buf);
                break;
        case SYSLOG_ACTION_CLEAR:
        case SYSLOG_ACTION_CONSOLE_OFF:
@@ -733,7 +820,7 @@ int main(int argc, char *argv[])
                break;
        }
 
-       if (n < 0)
+       if (n < 0 && !filename)
                err(EXIT_FAILURE, _("klogctl failed"));
 
        return EXIT_SUCCESS;