summaryrefslogtreecommitdiffstats
path: root/sys-utils/dmesg.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys-utils/dmesg.c')
-rw-r--r--sys-utils/dmesg.c524
1 files changed, 398 insertions, 126 deletions
diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c
index 717cc58..25c674b 100644
--- a/sys-utils/dmesg.c
+++ b/sys-utils/dmesg.c
@@ -1,14 +1,21 @@
/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
* dmesg.c -- Print out the contents of the kernel ring buffer
*
* Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu>
- * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
- *
- * This program comes with ABSOLUTELY NO WARRANTY.
+ * Copyright (C) 2011-2023 Karel Zak <kzak@redhat.com>
*/
#include <stdio.h>
#include <getopt.h>
+#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/klog.h>
#include <sys/syslog.h>
#include <sys/time.h>
@@ -36,6 +43,7 @@
#include "mangle.h"
#include "pager.h"
#include "jsonwrt.h"
+#include "pathnames.h"
/* Close the log. Currently a NOP. */
#define SYSLOG_ACTION_CLOSE 0
@@ -60,6 +68,12 @@
/* Return size of the log buffer */
#define SYSLOG_ACTION_SIZE_BUFFER 10
+#define PID_CHARS_MAX sizeof(stringify_value(LONG_MAX))
+#define PID_CHARS_DEFAULT sizeof(stringify_value(INT_MAX))
+#define SYSLOG_DEFAULT_CALLER_ID_CHARS sizeof(stringify_value(SHRT_MAX))-1
+#define DMESG_CALLER_PREFIX "caller="
+#define DMESG_CALLER_PREFIXSZ (sizeof(DMESG_CALLER_PREFIX)-1)
+
/*
* Color scheme
*/
@@ -125,6 +139,7 @@ static const struct dmesg_name level_names[] =
* shifted code :-)
*/
#define FAC_BASE(f) ((f) >> 3)
+#define LOG_RAW_FAC_PRI(fac, pri) ((fac << 3) | pri)
static const struct dmesg_name facility_names[] =
{
@@ -140,6 +155,18 @@ static const struct dmesg_name facility_names[] =
[FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") },
[FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") },
[FAC_BASE(LOG_FTP)] = { "ftp", N_("FTP daemon") },
+ [FAC_BASE(LOG_FTP) + 1] = { "res0", N_("reserved 0") },
+ [FAC_BASE(LOG_FTP) + 2] = { "res1", N_("reserved 1") },
+ [FAC_BASE(LOG_FTP) + 3] = { "res2", N_("reserved 2") },
+ [FAC_BASE(LOG_FTP) + 4] = { "res3", N_("reserved 3") },
+ [FAC_BASE(LOG_LOCAL0)] = { "local0", N_("local use 0") },
+ [FAC_BASE(LOG_LOCAL1)] = { "local1", N_("local use 1") },
+ [FAC_BASE(LOG_LOCAL2)] = { "local2", N_("local use 2") },
+ [FAC_BASE(LOG_LOCAL3)] = { "local3", N_("local use 3") },
+ [FAC_BASE(LOG_LOCAL4)] = { "local4", N_("local use 4") },
+ [FAC_BASE(LOG_LOCAL5)] = { "local5", N_("local use 5") },
+ [FAC_BASE(LOG_LOCAL6)] = { "local6", N_("local use 6") },
+ [FAC_BASE(LOG_LOCAL7)] = { "local7", N_("local use 7") },
};
/* supported methods to read message buffer
@@ -158,9 +185,12 @@ enum {
DMESG_TIMEFTM_RELTIME, /* [relative] */
DMESG_TIMEFTM_TIME, /* [time] */
DMESG_TIMEFTM_TIME_DELTA, /* [time <delta>] */
- DMESG_TIMEFTM_ISO8601 /* 2013-06-13T22:11:00,123456+0100 */
+ DMESG_TIMEFTM_ISO8601, /* 2013-06-13T22:11:00,123456+0100 */
+
+ __DMESG_TIMEFTM_COUNT
};
-#define is_timefmt(c, f) ((c)->time_fmt == (DMESG_TIMEFTM_ ##f))
+
+#define DMESG_TIMEFTM_DEFAULT DMESG_TIMEFTM_TIME
struct dmesg_control {
/* bit arrays -- see include/bitops.h */
@@ -198,7 +228,8 @@ struct dmesg_control {
char *filename;
char *mmap_buff;
size_t pagesize;
- unsigned int time_fmt; /* time format */
+ size_t ntime_fmts;
+ unsigned int time_fmts[2 * __DMESG_TIMEFTM_COUNT]; /* time format */
struct ul_jsonwrt jfmt; /* -J formatting */
@@ -215,6 +246,7 @@ struct dmesg_control {
force_prefix:1; /* force timestamp and decode prefix
on each line */
int indent; /* due to timestamps if newline */
+ size_t caller_id_size; /* PRINTK_CALLERID max field size */
};
struct dmesg_record {
@@ -224,6 +256,7 @@ struct dmesg_record {
int level;
int facility;
struct timeval tv;
+ char caller_id[PID_CHARS_MAX];
const char *next; /* buffer with next unparsed record */
size_t next_size; /* size of the next buffer */
@@ -236,9 +269,11 @@ struct dmesg_record {
(_r)->level = -1; \
(_r)->tv.tv_sec = 0; \
(_r)->tv.tv_usec = 0; \
+ (_r)->caller_id[0] = 0; \
} while (0)
-static int read_kmsg(struct dmesg_control *ctl);
+static int process_kmsg(struct dmesg_control *ctl);
+static int process_kmsg_file(struct dmesg_control *ctl, char **buf);
static int set_level_color(int log_level, const char *mesg, size_t mesgsz)
{
@@ -290,6 +325,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -D, --console-off disable printing messages to console\n"), out);
fputs(_(" -E, --console-on enable printing messages to console\n"), out);
fputs(_(" -F, --file <file> use the file instead of the kernel log buffer\n"), out);
+ fputs(_(" -K, --kmsg-file <file> use the file in kmsg format\n"), out);
fputs(_(" -f, --facility <list> restrict output to defined facilities\n"), out);
fputs(_(" -H, --human human readable output\n"), out);
fputs(_(" -J, --json use JSON output format\n"), out);
@@ -315,15 +351,15 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -T, --ctime show human-readable timestamp (may be inaccurate!)\n"), out);
fputs(_(" -t, --notime don't show any timestamp with messages\n"), out);
fputs(_(" --time-format <format> show timestamp using the given format:\n"
- " [delta|reltime|ctime|notime|iso]\n"
+ " [delta|reltime|ctime|notime|iso|raw]\n"
"Suspending/resume will make ctime and iso timestamps inaccurate.\n"), out);
fputs(_(" --since <time> display the lines since the specified time\n"), out);
fputs(_(" --until <time> display the lines until the specified time\n"), out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(29));
+ fprintf(out, USAGE_HELP_OPTIONS(29));
fputs(_("\nSupported log facilities:\n"), out);
- for (i = 0; i < ARRAY_SIZE(level_names); i++)
+ for (i = 0; i < ARRAY_SIZE(facility_names); i++)
fprintf(out, " %7s - %s\n",
facility_names[i].name,
_(facility_names[i].help));
@@ -334,10 +370,58 @@ static void __attribute__((__noreturn__)) usage(void)
level_names[i].name,
_(level_names[i].help));
- printf(USAGE_MAN_TAIL("dmesg(1)"));
+ fprintf(out, USAGE_MAN_TAIL("dmesg(1)"));
exit(EXIT_SUCCESS);
}
+static void reset_time_fmts(struct dmesg_control *ctl)
+{
+ memset(ctl->time_fmts, 0,
+ __DMESG_TIMEFTM_COUNT * sizeof(*(ctl->time_fmts)));
+ ctl->time_fmts[0] = DMESG_TIMEFTM_DEFAULT;
+}
+
+static int is_time_fmt_set(struct dmesg_control *ctl, unsigned int time_format)
+{
+ size_t i;
+
+ if (ctl->ntime_fmts == 0)
+ return time_format == DMESG_TIMEFTM_DEFAULT;
+
+ for (i = 0; i < ctl->ntime_fmts; i++) {
+ if (ctl->time_fmts[i] == time_format)
+ return 1;
+ }
+ return 0;
+}
+
+static void include_time_fmt(struct dmesg_control *ctl, unsigned int time_format)
+{
+ if (ctl->ntime_fmts > 0 && is_time_fmt_set(ctl, time_format))
+ return;
+
+ if (ctl->ntime_fmts < __DMESG_TIMEFTM_COUNT)
+ ctl->time_fmts[ctl->ntime_fmts++] = time_format;
+}
+
+static void exclude_time_fmt(struct dmesg_control *ctl, unsigned int time_format)
+{
+ size_t idx = 0;
+
+ while (idx < ctl->ntime_fmts && ctl->time_fmts[idx] != time_format)
+ idx++;
+
+ if (idx < ctl->ntime_fmts && ctl->time_fmts[idx] == time_format) {
+ while (idx + 1 < ctl->ntime_fmts) {
+ ctl->time_fmts[idx] = ctl->time_fmts[idx+1];
+ idx++;
+ }
+ ctl->ntime_fmts--;
+ if (ctl->ntime_fmts == 0)
+ reset_time_fmts(ctl);
+ }
+}
+
/*
* LEVEL ::= <number> | <name>
* <number> ::= @len is set: number in range <0..N>, where N < ARRAY_SIZE(level_names)
@@ -510,18 +594,17 @@ static const char *parse_kmsg_timestamp(const char *str0, struct timeval *tv)
usec = strtoumax(str, &end, 10);
if (!errno && end && (*end == ';' || *end == ',')) {
- tv->tv_usec = usec % 1000000;
- tv->tv_sec = usec / 1000000;
+ tv->tv_usec = usec % USEC_PER_SEC;
+ tv->tv_sec = usec / USEC_PER_SEC;
} else
return str0;
return end + 1; /* skip separator */
}
-
static double time_diff(struct timeval *a, struct timeval *b)
{
- return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
+ return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / (double) USEC_PER_SEC;
}
static int get_syslog_buffer_size(void)
@@ -532,6 +615,47 @@ static int get_syslog_buffer_size(void)
}
/*
+ * Get the number of characters needed to hold the maximum number
+ * of tasks this system supports. This size of string could hold
+ * a thread id large enough for the highest thread id.
+ * This is needed to determine the number of characters reserved for
+ * the PRINTK_CALLER field if it has been configured in the Linux Kernel.
+ *
+ * The number of digits sets the max value since the value can't exceed
+ * a value of that size. The /proc field defined by _PATH_PROC_PIDMAX
+ * holds the maximum number of PID values that may be ussed by the system,
+ * so 0 to that value minus one.
+ *
+ * For determining the size of the PRINTK_CALLER field, we make the safe
+ * assumption that the number of threads >= number of cpus. This because
+ * the PRINTK_CALLER field can hold either a thread id or a CPU id value.
+ *
+ * If we can't access the pid max kernel proc entry we assign a default
+ * field size of 5 characters as that is what the old syslog interface
+ * uses as the reserved field size. This is justified because 32-bit Linux
+ * systems are limited to PID values between (0-32767).
+ *
+ */
+static size_t max_threads_id_size(void)
+{
+ char taskmax[PID_CHARS_MAX] = {'\0'};
+ ssize_t rdsize;
+ int fd;
+
+ fd = open(_PATH_PROC_PIDMAX, O_RDONLY);
+ if (fd == -1)
+ return PID_CHARS_DEFAULT;
+
+ rdsize = read(fd, taskmax, sizeof(taskmax));
+ close(fd);
+
+ if (rdsize == -1)
+ return PID_CHARS_DEFAULT;
+
+ return strnlen(taskmax, sizeof(taskmax));
+}
+
+/*
* Reads messages from regular file by mmap
*/
static ssize_t mmap_file_buffer(struct dmesg_control *ctl, char **buf)
@@ -589,9 +713,9 @@ static ssize_t read_syslog_buffer(struct dmesg_control *ctl, char **buf)
}
/*
- * Top level function to read messages
+ * Top level function to read (and print in case of kmesg) messages
*/
-static ssize_t read_buffer(struct dmesg_control *ctl, char **buf)
+static ssize_t process_buffer(struct dmesg_control *ctl, char **buf)
{
ssize_t n = -1;
@@ -604,12 +728,17 @@ static ssize_t read_buffer(struct dmesg_control *ctl, char **buf)
ctl->bufsize = get_syslog_buffer_size();
n = read_syslog_buffer(ctl, buf);
+ /* Set number of PID characters for caller_id spacing */
+ ctl->caller_id_size = SYSLOG_DEFAULT_CALLER_ID_CHARS;
break;
case DMESG_METHOD_KMSG:
- /*
- * Since kernel 3.5.0
- */
- n = read_kmsg(ctl);
+ if (ctl->filename)
+ n = process_kmsg_file(ctl, buf);
+ else
+ /*
+ * Since kernel 3.5.0
+ */
+ n = process_kmsg(ctl);
break;
default:
abort(); /* impossible method -> drop core */
@@ -706,6 +835,39 @@ static const char *skip_item(const char *begin, const char *end, const char *sep
}
/*
+ * Checks to see if the caller (caller id) field is present in the kmsg record.
+ * This is true if the PRINTK_CALLER config option has been set in the kernel.
+ *
+ * If the caller_id is found in the kmsg buffer then return the id and id type
+ * to the caller in dmesg caller_id. Returns string pointer to next value.
+ *
+ */
+static const char *parse_callerid(const char *p_str, const char *end,
+ struct dmesg_record *p_drec)
+{
+ const char *p_after;
+ const char *p_next;
+ size_t cid_size;
+ char *p_scn;
+ char *p_cid;
+
+ /* Check for PRINTK_CALLER prefix, must be before msg text */
+ p_cid = strstr(p_str, DMESG_CALLER_PREFIX);
+ p_scn = strchr(p_str, ';');
+ if (p_cid != NULL && p_drec != NULL && p_scn != NULL && p_cid < p_scn) {
+ p_next = p_cid + DMESG_CALLER_PREFIXSZ;
+ p_after = skip_item(p_next, end, ",;");
+ cid_size = p_after - p_next;
+ if (cid_size < sizeof(p_drec->caller_id))
+ xstrncpy(p_drec->caller_id, p_next, cid_size);
+ else
+ return p_str;
+ return p_after;
+ }
+ return p_str;
+}
+
+/*
* Parses one record from syslog(2) buffer
*/
static int get_next_syslog_record(struct dmesg_control *ctl,
@@ -763,7 +925,7 @@ static int get_next_syslog_record(struct dmesg_control *ctl,
if (*begin == '[' && (*(begin + 1) == ' ' ||
isdigit(*(begin + 1)))) {
- if (!is_timefmt(ctl, NONE))
+ if (!is_time_fmt_set(ctl, DMESG_TIMEFTM_NONE))
begin = parse_syslog_timestamp(begin + 1, &rec->tv);
else
begin = skip_item(begin, end, "]");
@@ -772,8 +934,26 @@ static int get_next_syslog_record(struct dmesg_control *ctl,
begin++;
}
- rec->mesg = begin;
- rec->mesg_size = end - begin;
+ if (*begin == '[' && (*(begin + 1) == ' ' ||
+ (*(begin + 1) == 'T' || *(begin + 1) == 'C'))) {
+ const char *start = begin + 1;
+ size_t id_size;
+
+ while (start < end && *start == ' ')
+ start++;
+
+ begin = skip_item(begin, end, "]");
+ id_size = begin - start;
+
+ if (id_size < sizeof(rec->caller_id))
+ xstrncpy(rec->caller_id, start, id_size);
+
+ rec->mesg = begin < end ? begin + 1 : NULL;
+ rec->mesg_size = begin < end ? end - begin - 1 : 0;
+ } else {
+ rec->mesg = begin;
+ rec->mesg_size = end - begin;
+ }
/* Don't count \n from the last message to the message size */
if (*end != '\n' && *(end - 1) == '\n')
@@ -938,13 +1118,20 @@ static void print_record(struct dmesg_control *ctl,
char buf[128];
char fpbuf[32] = "\0";
char tsbuf[64] = "\0";
+ char full_tsbuf[64 * __DMESG_TIMEFTM_COUNT] = "\0";
size_t mesg_size = rec->mesg_size;
int timebreak = 0;
char *mesg_copy = NULL;
const char *line = NULL;
+ double delta = 0;
+ size_t format_iter = 0;
- if (!accept_record(ctl, rec))
+ if (!accept_record(ctl, rec)) {
+ /* remember time of the rejected record to not affect delta for
+ * the following records */
+ ctl->lasttime = rec->tv;
return;
+ }
if (!rec->mesg_size) {
if (!ctl->json)
@@ -952,6 +1139,8 @@ static void print_record(struct dmesg_control *ctl,
return;
}
+ delta = record_count_delta(ctl, rec);
+
if (ctl->json) {
if (!ul_jsonwrt_is_ready(&ctl->jfmt)) {
ul_jsonwrt_init(&ctl->jfmt, stdout, 0);
@@ -966,9 +1155,9 @@ static void print_record(struct dmesg_control *ctl,
* backward compatibility with syslog(2) buffers only
*/
if (ctl->raw) {
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ ctl->indent = snprintf(full_tsbuf, sizeof(full_tsbuf),
"<%d>[%5ld.%06ld] ",
- LOG_MAKEPRI(rec->facility, rec->level),
+ LOG_RAW_FAC_PRI(rec->facility, rec->level),
(long) rec->tv.tv_sec,
(long) rec->tv.tv_usec);
goto full_output;
@@ -981,64 +1170,72 @@ static void print_record(struct dmesg_control *ctl,
level_names[rec->level].name);
/* Store the timestamp in a buffer */
- switch (ctl->time_fmt) {
- double delta;
- struct tm cur;
- case DMESG_TIMEFTM_NONE:
- ctl->indent = 0;
- break;
- case DMESG_TIMEFTM_CTIME:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
- record_ctime(ctl, rec, buf, sizeof(buf)));
- break;
- case DMESG_TIMEFTM_CTIME_DELTA:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s <%12.06f>] ",
- record_ctime(ctl, rec, buf, sizeof(buf)),
- record_count_delta(ctl, rec));
- break;
- case DMESG_TIMEFTM_DELTA:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[<%12.06f>] ",
- record_count_delta(ctl, rec));
- break;
- case DMESG_TIMEFTM_RELTIME:
- record_localtime(ctl, rec, &cur);
- delta = record_count_delta(ctl, rec);
- if (cur.tm_min != ctl->lasttm.tm_min ||
- cur.tm_hour != ctl->lasttm.tm_hour ||
- cur.tm_yday != ctl->lasttm.tm_yday) {
- timebreak = 1;
+ for (format_iter = 0;
+ format_iter < (ctl->ntime_fmts > 0 ? ctl->ntime_fmts : 1);
+ format_iter++) {
+ switch (ctl->time_fmts[format_iter]) {
+ struct tm cur;
+ case DMESG_TIMEFTM_NONE:
+ ctl->indent = 0;
+ break;
+ case DMESG_TIMEFTM_CTIME:
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
- short_ctime(&cur, buf,
- sizeof(buf)));
- } else {
- if (delta < 10)
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
- "[ %+8.06f] ", delta);
- else
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
- "[ %+9.06f] ", delta);
+ record_ctime(ctl, rec, buf, sizeof(buf)));
+ break;
+ case DMESG_TIMEFTM_CTIME_DELTA:
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s <%12.06f>] ",
+ record_ctime(ctl, rec, buf, sizeof(buf)),
+ delta);
+ break;
+ case DMESG_TIMEFTM_DELTA:
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[<%12.06f>] ",
+ delta);
+ break;
+ case DMESG_TIMEFTM_RELTIME:
+ record_localtime(ctl, rec, &cur);
+ if (cur.tm_min != ctl->lasttm.tm_min ||
+ cur.tm_hour != ctl->lasttm.tm_hour ||
+ cur.tm_yday != ctl->lasttm.tm_yday) {
+ timebreak = 1;
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
+ short_ctime(&cur, buf,
+ sizeof(buf)));
+ } else {
+ if (delta < 10)
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ "[ %+8.06f] ", delta);
+ else
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ "[ %+9.06f] ", delta);
+ }
+ ctl->lasttm = cur;
+ break;
+ case DMESG_TIMEFTM_TIME:
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ ctl->json ? "%5ld.%06ld" : "[%5ld.%06ld] ",
+ (long)rec->tv.tv_sec,
+ (long)rec->tv.tv_usec);
+ break;
+ case DMESG_TIMEFTM_TIME_DELTA:
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld <%12.06f>] ",
+ (long)rec->tv.tv_sec,
+ (long)rec->tv.tv_usec,
+ delta);
+ break;
+ case DMESG_TIMEFTM_ISO8601:
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "%s ",
+ iso_8601_time(ctl, rec, buf,
+ sizeof(buf)));
+ break;
+ default:
+ abort();
}
- ctl->lasttm = cur;
- break;
- case DMESG_TIMEFTM_TIME:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
- ctl->json ? "%5ld.%06ld" : "[%5ld.%06ld] ",
- (long)rec->tv.tv_sec,
- (long)rec->tv.tv_usec);
- break;
- case DMESG_TIMEFTM_TIME_DELTA:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld <%12.06f>] ",
- (long)rec->tv.tv_sec,
- (long)rec->tv.tv_usec,
- record_count_delta(ctl, rec));
- break;
- case DMESG_TIMEFTM_ISO8601:
- ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "%s ",
- iso_8601_time(ctl, rec, buf,
- sizeof(buf)));
- break;
- default:
- abort();
+
+ if (is_time_fmt_set(ctl, DMESG_TIMEFTM_NONE))
+ break;
+ else if (*tsbuf)
+ strcat(full_tsbuf, tsbuf);
+
}
ctl->indent += strlen(fpbuf);
@@ -1052,32 +1249,45 @@ full_output:
ul_jsonwrt_value_s(&ctl->jfmt, "fac", facility_names[rec->facility].name);
ul_jsonwrt_value_s(&ctl->jfmt, "pri", level_names[rec->level].name);
} else
- ul_jsonwrt_value_u64(&ctl->jfmt, "pri", LOG_MAKEPRI(rec->facility, rec->level));
+ ul_jsonwrt_value_u64(&ctl->jfmt, "pri", LOG_RAW_FAC_PRI(rec->facility, rec->level));
}
/* Output the timestamp buffer */
- if (*tsbuf) {
+ if (*full_tsbuf) {
/* Colorize the timestamp */
if (ctl->color)
dmesg_enable_color(timebreak ? DMESG_COLOR_TIMEBREAK :
DMESG_COLOR_TIME);
- if (ctl->time_fmt != DMESG_TIMEFTM_RELTIME) {
+ if (!is_time_fmt_set(ctl, DMESG_TIMEFTM_RELTIME)) {
if (ctl->json)
- ul_jsonwrt_value_raw(&ctl->jfmt, "time", tsbuf);
+ ul_jsonwrt_value_raw(&ctl->jfmt, "time", full_tsbuf);
else
- fputs(tsbuf, stdout);
+ fputs(full_tsbuf, stdout);
} else {
/*
* For relative timestamping, the first line's
* timestamp is the offset and all other lines will
* report an offset of 0.000000.
*/
- fputs(!line ? tsbuf : "[ +0.000000] ", stdout);
+ fputs(!line ? full_tsbuf : "[ +0.000000] ", stdout);
}
if (ctl->color)
color_disable();
}
+ if (*rec->caller_id) {
+ if (ctl->json) {
+ ul_jsonwrt_value_s(&ctl->jfmt, "caller", rec->caller_id);
+ } else {
+ char cidbuf[PID_CHARS_MAX+3] = {'\0'};
+
+ sprintf(cidbuf, "[%*s] ",
+ (char)ctl->caller_id_size, rec->caller_id);
+ ctl->indent += strnlen(cidbuf, sizeof(cidbuf));
+ fputs(cidbuf, stdout);
+ }
+ }
+
/*
* A kernel message may contain several lines of output, separated
* by '\n'. If the timestamp and decode outputs are forced then each
@@ -1117,7 +1327,7 @@ full_output:
color_disable();
} else {
if (ctl->json)
- ul_jsonwrt_value_s(&ctl->jfmt, "msg", line);
+ ul_jsonwrt_value_s_sized(&ctl->jfmt, "msg", line, mesg_size);
else
safe_fwrite(ctl, line, mesg_size, ctl->indent, stdout);
}
@@ -1197,7 +1407,7 @@ static int init_kmsg(struct dmesg_control *ctl)
* but read() returns -EINVAL :-(((
*
* Let's try to read the first record. The record is later processed in
- * read_kmsg().
+ * process_kmsg().
*/
ctl->kmsg_first_read = read_kmsg_one(ctl);
if (ctl->kmsg_first_read < 0) {
@@ -1253,7 +1463,7 @@ static int parse_kmsg_record(struct dmesg_control *ctl,
goto mesg;
/* C) timestamp */
- if (is_timefmt(ctl, NONE))
+ if (is_time_fmt_set(ctl, DMESG_TIMEFTM_NONE))
p = skip_item(p, end, ",;");
else
p = parse_kmsg_timestamp(p, &rec->tv);
@@ -1261,7 +1471,10 @@ static int parse_kmsg_record(struct dmesg_control *ctl,
goto mesg;
/* D) optional fields (ignore) */
- p = skip_item(p, end, ";");
+ p = skip_item(p, end, ",;");
+
+ /* Include optional PRINTK_CALLER field if it is present */
+ p = parse_callerid(p, end, rec);
mesg:
/* E) message text */
@@ -1305,7 +1518,7 @@ mesg:
*
* Returns 0 on success, -1 on error.
*/
-static int read_kmsg(struct dmesg_control *ctl)
+static int process_kmsg(struct dmesg_control *ctl)
{
struct dmesg_record rec;
ssize_t sz;
@@ -1313,6 +1526,9 @@ static int read_kmsg(struct dmesg_control *ctl)
if (ctl->method != DMESG_METHOD_KMSG || ctl->kmsg < 0)
return -1;
+ /* Determine number of PID characters for caller_id spacing */
+ ctl->caller_id_size = max_threads_id_size();
+
/*
* The very first read() call is done in kmsg_init() where we test
* /dev/kmsg usability. The return code from the initial read() is
@@ -1333,6 +1549,40 @@ static int read_kmsg(struct dmesg_control *ctl)
return 0;
}
+static int process_kmsg_file(struct dmesg_control *ctl, char **buf)
+{
+ char str[sizeof(ctl->kmsg_buf)];
+ struct dmesg_record rec;
+ ssize_t sz;
+ size_t len;
+
+ if (ctl->method != DMESG_METHOD_KMSG || !ctl->filename)
+ return -1;
+
+ sz = mmap_file_buffer(ctl, buf);
+ if (sz == -1)
+ return -1;
+
+ while (sz > 0) {
+ len = strnlen(ctl->mmap_buff, sz);
+ if (len > sizeof(str))
+ errx(EXIT_FAILURE, _("record too large"));
+
+ memcpy(str, ctl->mmap_buff, len);
+
+ if (parse_kmsg_record(ctl, &rec, str, len) == 0)
+ print_record(ctl, &rec);
+
+ if (len < (size_t)sz)
+ len++;
+
+ sz -= len;
+ ctl->mmap_buff += len;
+ }
+
+ return 0;
+}
+
static int which_time_format(const char *s)
{
if (!strcmp(s, "notime"))
@@ -1345,6 +1595,8 @@ static int which_time_format(const char *s)
return DMESG_TIMEFTM_RELTIME;
if (!strcmp(s, "iso"))
return DMESG_TIMEFTM_ISO8601;
+ if (!strcmp(s, "raw"))
+ return DMESG_TIMEFTM_TIME;
errx(EXIT_FAILURE, _("unknown time format: %s"), s);
}
@@ -1387,8 +1639,9 @@ int main(int argc, char *argv[])
.action = SYSLOG_ACTION_READ_ALL,
.method = DMESG_METHOD_KMSG,
.kmsg = -1,
- .time_fmt = DMESG_TIMEFTM_TIME,
+ .ntime_fmts = 0,
.indent = 0,
+ .caller_id_size = 0,
};
int colormode = UL_COLORMODE_UNDEF;
enum {
@@ -1414,6 +1667,7 @@ int main(int argc, char *argv[])
{ "help", no_argument, NULL, 'h' },
{ "json", no_argument, NULL, 'J' },
{ "kernel", no_argument, NULL, 'k' },
+ { "kmsg-file", required_argument, NULL, 'K' },
{ "level", required_argument, NULL, 'l' },
{ "since", required_argument, NULL, OPT_SINCE },
{ "syslog", no_argument, NULL, 'S' },
@@ -1435,6 +1689,7 @@ int main(int argc, char *argv[])
static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
{ 'C','D','E','c','n','r' }, /* clear,off,on,read-clear,level,raw*/
+ { 'F','K' }, /* file, kmsg-file */
{ 'H','r' }, /* human, raw */
{ 'L','r' }, /* color, raw */
{ 'S','w' }, /* syslog,follow */
@@ -1452,7 +1707,9 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
close_stdout_atexit();
- while ((c = getopt_long(argc, argv, "CcDdEeF:f:HhJkL::l:n:iPprSs:TtuVWwx",
+ ctl.time_fmts[0] = DMESG_TIMEFTM_DEFAULT;
+
+ while ((c = getopt_long(argc, argv, "CcDdEeF:f:HhJK:kL::l:n:iPprSs:TtuVWwx",
longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
@@ -1474,11 +1731,17 @@ int main(int argc, char *argv[])
ctl.action = SYSLOG_ACTION_CONSOLE_ON;
break;
case 'e':
- ctl.time_fmt = DMESG_TIMEFTM_RELTIME;
+ include_time_fmt(&ctl, DMESG_TIMEFTM_RELTIME);
break;
case 'F':
ctl.filename = optarg;
ctl.method = DMESG_METHOD_MMAP;
+ ctl.caller_id_size = SYSLOG_DEFAULT_CALLER_ID_CHARS;
+ break;
+ case 'K':
+ ctl.filename = optarg;
+ ctl.method = DMESG_METHOD_KMSG;
+ ctl.caller_id_size = max_threads_id_size();
break;
case 'f':
ctl.fltr_fac = 1;
@@ -1487,7 +1750,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
break;
case 'H':
- ctl.time_fmt = DMESG_TIMEFTM_RELTIME;
+ include_time_fmt(&ctl, DMESG_TIMEFTM_RELTIME);
colormode = UL_COLORMODE_AUTO;
ctl.pager = 1;
break;
@@ -1534,10 +1797,11 @@ int main(int argc, char *argv[])
ctl.bufsize = 4096;
break;
case 'T':
- ctl.time_fmt = DMESG_TIMEFTM_CTIME;
+ include_time_fmt(&ctl, DMESG_TIMEFTM_CTIME);
break;
case 't':
- ctl.time_fmt = DMESG_TIMEFTM_NONE;
+ reset_time_fmts(&ctl);
+ include_time_fmt(&ctl, DMESG_TIMEFTM_NONE);
break;
case 'u':
ctl.fltr_fac = 1;
@@ -1555,7 +1819,7 @@ int main(int argc, char *argv[])
ctl.decode = 1;
break;
case OPT_TIME_FORMAT:
- ctl.time_fmt = which_time_format(optarg);
+ include_time_fmt(&ctl, which_time_format(optarg));
break;
case OPT_NOESC:
ctl.noesc = 1;
@@ -1587,7 +1851,8 @@ int main(int argc, char *argv[])
}
if (ctl.json) {
- ctl.time_fmt = DMESG_TIMEFTM_TIME;
+ reset_time_fmts(&ctl);
+ ctl.ntime_fmts = 0;
delta = 0;
ctl.force_prefix = 0;
ctl.raw = 0;
@@ -1595,45 +1860,53 @@ int main(int argc, char *argv[])
nopager = 1;
}
- if ((is_timefmt(&ctl, RELTIME) ||
- is_timefmt(&ctl, CTIME) ||
- is_timefmt(&ctl, ISO8601)) ||
+ if ((is_time_fmt_set(&ctl, DMESG_TIMEFTM_RELTIME) ||
+ is_time_fmt_set(&ctl, DMESG_TIMEFTM_CTIME) ||
+ is_time_fmt_set(&ctl, DMESG_TIMEFTM_ISO8601)) ||
ctl.since ||
ctl.until) {
if (dmesg_get_boot_time(&ctl.boot_time) != 0)
- ctl.time_fmt = DMESG_TIMEFTM_NONE;
+ include_time_fmt(&ctl, DMESG_TIMEFTM_NONE);
else
ctl.suspended_time = dmesg_get_suspended_time();
}
- if (delta)
- switch (ctl.time_fmt) {
- case DMESG_TIMEFTM_CTIME:
- ctl.time_fmt = DMESG_TIMEFTM_CTIME_DELTA;
- break;
- case DMESG_TIMEFTM_TIME:
- ctl.time_fmt = DMESG_TIMEFTM_TIME_DELTA;
- break;
- case DMESG_TIMEFTM_ISO8601:
- warnx(_("--show-delta is ignored when used together with iso8601 time format"));
- break;
- default:
- ctl.time_fmt = DMESG_TIMEFTM_DELTA;
+ if (delta || is_time_fmt_set(&ctl, DMESG_TIMEFTM_DELTA)) {
+ if (is_time_fmt_set(&ctl, DMESG_TIMEFTM_TIME)) {
+ if (ctl.ntime_fmts == 0) {
+ ctl.time_fmts[ctl.ntime_fmts++] = DMESG_TIMEFTM_TIME_DELTA;
+ } else {
+ exclude_time_fmt(&ctl, DMESG_TIMEFTM_DELTA);
+ for (n = 0; (size_t) n < ctl.ntime_fmts; n++) {
+ if (ctl.time_fmts[n] == DMESG_TIMEFTM_TIME) {
+ ctl.time_fmts[n] = DMESG_TIMEFTM_TIME_DELTA;
+ break;
+ }
+ }
+ }
+ } else if (is_time_fmt_set(&ctl, DMESG_TIMEFTM_CTIME)) {
+ exclude_time_fmt(&ctl, DMESG_TIMEFTM_DELTA);
+ for (n = 0; (size_t) n < ctl.ntime_fmts; n++) {
+ if (ctl.time_fmts[n] == DMESG_TIMEFTM_CTIME) {
+ ctl.time_fmts[n] = DMESG_TIMEFTM_CTIME_DELTA;
+ break;
+ }
+ }
+ } else {
+ include_time_fmt(&ctl, DMESG_TIMEFTM_DELTA);
}
-
+ }
if (!ctl.json)
ctl.color = colors_init(colormode, "dmesg") ? 1 : 0;
if (ctl.follow)
nopager = 1;
ctl.pager = nopager ? 0 : ctl.pager;
- if (ctl.pager)
- pager_redirect();
switch (ctl.action) {
case SYSLOG_ACTION_READ_ALL:
case SYSLOG_ACTION_READ_CLEAR:
- if (ctl.method == DMESG_METHOD_KMSG && init_kmsg(&ctl) != 0)
+ if (ctl.method == DMESG_METHOD_KMSG && !ctl.filename && init_kmsg(&ctl) != 0)
ctl.method = DMESG_METHOD_SYSLOG;
if (ctl.raw
@@ -1642,12 +1915,11 @@ int main(int argc, char *argv[])
errx(EXIT_FAILURE, _("--raw can be used together with --level or "
"--facility only when reading messages from /dev/kmsg"));
- /* only kmsg supports multi-line messages */
if (ctl.force_prefix && ctl.method != DMESG_METHOD_KMSG)
- ctl.force_prefix = 0;
+ errx(EXIT_FAILURE, _("only kmsg supports multi-line messages"));
if (ctl.pager)
pager_redirect();
- n = read_buffer(&ctl, &buf);
+ n = process_buffer(&ctl, &buf);
if (n > 0)
print_buffer(&ctl, buf, n);
if (!ctl.mmap_buff)