*
* This program comes with ABSOLUTELY NO WARRANTY.
*/
-#include <linux/unistd.h>
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
/*
* sys/syslog.h uses (f << 3) for all facility codes.
- * We want to use the codes as array idexes, so shift back...
+ * We want to use the codes as array indexes, so shift back...
*
* Note that libc LOG_FAC() macro returns the base codes, not the
* shifted code :-)
fltr_fac:1, /* filter out by facilities[] */
decode:1, /* use "facility: level: " prefix */
pager:1, /* pipe output into a pager */
- color:1; /* colorize messages */
+ color:1, /* colorize messages */
+ force_prefix:1; /* force timestamp and decode prefix
+ on each line */
int indent; /* due to timestamps if newline */
};
break;
}
- /* well, sometimes the messges contains important keywords, but in
+ /* well, sometimes the messages contains important keywords, but in
* non-warning/error messages
*/
if (id < 0 && memmem(mesg, mesgsz, "segfault at", 11))
return id >= 0 ? 0 : -1;
}
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
size_t i;
fputs(USAGE_HEADER, out);
fputs(_(" -l, --level <list> restrict output to defined levels\n"), out);
fputs(_(" -n, --console-level <level> set level of messages printed to console\n"), out);
fputs(_(" -P, --nopager do not pipe output into a pager\n"), out);
+ fputs(_(" -p, --force-prefix force timestamp output on each line of multi-line messages\n"), out);
fputs(_(" -r, --raw print the raw message buffer\n"), out);
fputs(_(" -S, --syslog force to use syslog(2) rather than /dev/kmsg\n"), out);
fputs(_(" -s, --buffer-size <size> buffer size to query the kernel ring buffer\n"), out);
" [delta|reltime|ctime|notime|iso]\n"
"Suspending/resume will make ctime and iso timestamps inaccurate.\n"), out);
fputs(USAGE_SEPARATOR, out);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
+ printf(USAGE_HELP_OPTIONS(29));
fputs(_("\nSupported log facilities:\n"), out);
for (i = 0; i < ARRAY_SIZE(level_names); i++)
fprintf(out, " %7s - %s\n",
fprintf(out, " %7s - %s\n",
level_names[i].name,
_(level_names[i].help));
- fputs(USAGE_SEPARATOR, out);
- fprintf(out, USAGE_MAN_TAIL("dmesg(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+
+ printf(USAGE_MAN_TAIL("dmesg(1)"));
+ exit(EXIT_SUCCESS);
}
/*
* <name> ::= case-insensitive text
*
* Note that @len argument is not set when parsing "-n <level>" command line
- * option. The console_level is intepreted as "log level less than the value".
+ * option. The console_level is interpreted as "log level less than the value".
*
* For example "dmesg -n 8" or "dmesg -n debug" enables debug console log
* level by klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, 8). The @str argument
if (n == 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
n = klogctl(SYSLOG_ACTION_CLEAR, NULL, 0);
break;
+ default:
+ abort(); /* impossible method -> drop core */
}
return n;
size_t i;
for (i = 0; i < size; i++) {
- int rc = fprintf(out, "\\x%02x", buf[i]);
+ int rc = fprintf(out, "\\x%02hhx", buf[i]);
if (rc < 0)
return rc;
}
i += len - 1;
#else
len = 1;
- if (!isprint((unsigned int) *p) &&
- !isspace((unsigned int) *p)) /* non-printable */
+ if (!isprint((unsigned char) *p) &&
+ !isspace((unsigned char) *p)) /* non-printable */
hex = 1;
#endif
if (hex)
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')
+ rec->mesg_size--;
+
rec->next_size -= end - rec->next;
rec->next = rec->next_size > 0 ? end + 1 : NULL;
if (rec->next_size > 0)
.tv_usec = rec->tv.tv_usec
};
- if (strtimeval_iso(&tv, ISO_8601_DATE|ISO_8601_TIME|ISO_8601_COMMAUSEC|
- ISO_8601_TIMEZONE,
- buf, bufsz) != 0)
+ if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_T, buf, bufsz) != 0)
return NULL;
return buf;
const char *d = strnchr(p, sz, ':');
if (!d)
return NULL;
- sz -= d - p;
+ sz -= d - p + 1;
if (sz) {
if (isblank(*(d + 1)))
return d;
static void print_record(struct dmesg_control *ctl,
struct dmesg_record *rec)
{
- char buf[256];
- int has_color = 0;
- const char *mesg;
- size_t mesg_size;
- int indent = 0;
+ char buf[128];
+ char fpbuf[32] = "\0";
+ char tsbuf[64] = "\0";
+ size_t mesg_size = rec->mesg_size;
+ int timebreak = 0;
+ char *mesg_copy = NULL;
+ const char *line = NULL;
if (!accept_record(ctl, rec))
return;
}
/*
- * compose syslog(2) compatible raw output -- used for /dev/kmsg for
+ * Compose syslog(2) compatible raw output -- used for /dev/kmsg for
* backward compatibility with syslog(2) buffers only
*/
if (ctl->raw) {
- ctl->indent = printf("<%d>[%5ld.%06ld] ",
- LOG_MAKEPRI(rec->facility, rec->level),
- (long) rec->tv.tv_sec,
- (long) rec->tv.tv_usec);
-
- goto mesg;
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ "<%d>[%5ld.%06ld] ",
+ LOG_MAKEPRI(rec->facility, rec->level),
+ (long) rec->tv.tv_sec,
+ (long) rec->tv.tv_usec);
+ goto full_output;
}
- /*
- * facility : priority :
- */
+ /* Store decode information (facility & priority level) in a buffer */
if (ctl->decode &&
- -1 < rec->level && rec->level < (int) ARRAY_SIZE(level_names) &&
- -1 < rec->facility && rec->facility < (int) ARRAY_SIZE(facility_names))
- indent = printf("%-6s:%-6s: ", facility_names[rec->facility].name,
- level_names[rec->level].name);
-
- if (ctl->color)
- dmesg_enable_color(DMESG_COLOR_TIME);
-
+ (rec->level > -1) && (rec->level < (int) ARRAY_SIZE(level_names)) &&
+ (rec->facility > -1) &&
+ (rec->facility < (int) ARRAY_SIZE(facility_names)))
+ snprintf(fpbuf, sizeof(fpbuf), "%-6s:%-6s: ",
+ facility_names[rec->facility].name,
+ level_names[rec->level].name);
+
+ /* Store the timestamp in a buffer */
switch (ctl->time_fmt) {
double delta;
struct tm cur;
ctl->indent = 0;
break;
case DMESG_TIMEFTM_CTIME:
- ctl->indent = printf("[%s] ", record_ctime(ctl, rec, buf, sizeof(buf)));
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
+ record_ctime(ctl, rec, buf, sizeof(buf)));
break;
case DMESG_TIMEFTM_CTIME_DELTA:
- ctl->indent = printf("[%s <%12.06f>] ",
- record_ctime(ctl, rec, buf, sizeof(buf)),
- record_count_delta(ctl, rec));
+ 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 = printf("[<%12.06f>] ", record_count_delta(ctl, rec));
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[<%12.06f>] ",
+ record_count_delta(ctl, rec));
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) {
- dmesg_enable_color(DMESG_COLOR_TIMEBREAK);
- ctl->indent = printf("[%s] ", short_ctime(&cur, buf, sizeof(buf)));
+ timebreak = 1;
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
+ short_ctime(&cur, buf,
+ sizeof(buf)));
} else {
if (delta < 10)
- ctl->indent = printf("[ %+8.06f] ", delta);
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ "[ %+8.06f] ", delta);
else
- ctl->indent = printf("[ %+9.06f] ", delta);
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
+ "[ %+9.06f] ", delta);
}
ctl->lasttm = cur;
break;
case DMESG_TIMEFTM_TIME:
- ctl->indent = printf("[%5ld.%06ld] ",
- (long)rec->tv.tv_sec, (long)rec->tv.tv_usec);
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld] ",
+ (long)rec->tv.tv_sec,
+ (long)rec->tv.tv_usec);
break;
case DMESG_TIMEFTM_TIME_DELTA:
- ctl->indent = printf("[%5ld.%06ld <%12.06f>] ", (long)rec->tv.tv_sec,
- (long)rec->tv.tv_usec, record_count_delta(ctl, rec));
+ 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 = printf("%s ", iso_8601_time(ctl, rec, buf, sizeof(buf)));
+ ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "%s ",
+ iso_8601_time(ctl, rec, buf,
+ sizeof(buf)));
break;
default:
abort();
}
- ctl->indent += indent;
-
- if (ctl->color)
- color_disable();
+ ctl->indent += strlen(fpbuf);
+
+full_output:
+ /* Output the decode information */
+ if (*fpbuf)
+ fputs(fpbuf, stdout);
+
+ /* Output the timestamp buffer */
+ if (*tsbuf) {
+ /* Colorize the timestamp */
+ if (ctl->color)
+ dmesg_enable_color(timebreak ? DMESG_COLOR_TIMEBREAK :
+ DMESG_COLOR_TIME);
+ if (ctl->time_fmt != DMESG_TIMEFTM_RELTIME) {
+ fputs(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.
+ */
+ if (!line)
+ fputs(tsbuf, stdout);
+ else
+ printf("[ +0.000000] ");
+ }
+ if (ctl->color)
+ color_disable();
+ }
-mesg:
- mesg = rec->mesg;
- mesg_size = rec->mesg_size;
+ /*
+ * A kernel message may contain several lines of output, separated
+ * by '\n'. If the timestamp and decode outputs are forced then each
+ * line of the message must be displayed with that information.
+ */
+ if (ctl->force_prefix) {
+ if (!line) {
+ mesg_copy = xstrdup(rec->mesg);
+ line = strtok(mesg_copy, "\n");
+ mesg_size = strlen(line);
+ }
+ } else {
+ line = rec->mesg;
+ mesg_size = rec->mesg_size;
+ }
- /* Colorize output */
+ /* Colorize kernel message output */
if (ctl->color) {
- /* subsystem prefix */
- const char *subsys = get_subsys_delimiter(mesg, mesg_size);
+ /* Subsystem prefix */
+ const char *subsys = get_subsys_delimiter(line, mesg_size);
+ int has_color = 0;
+
if (subsys) {
dmesg_enable_color(DMESG_COLOR_SUBSYS);
- safe_fwrite(mesg, subsys - mesg, ctl->indent, stdout);
+ safe_fwrite(line, subsys - line, ctl->indent, stdout);
color_disable();
- mesg_size -= subsys - mesg;
- mesg = subsys;
+ mesg_size -= subsys - line;
+ line = subsys;
}
- /* error, alert .. etc. colors */
- has_color = set_level_color(rec->level, mesg, mesg_size) == 0;
- safe_fwrite(mesg, mesg_size, ctl->indent, stdout);
+ /* Error, alert .. etc. colors */
+ has_color = set_level_color(rec->level, line, mesg_size) == 0;
+ safe_fwrite(line, mesg_size, ctl->indent, stdout);
if (has_color)
color_disable();
} else
- safe_fwrite(mesg, mesg_size, ctl->indent, stdout);
+ safe_fwrite(line, mesg_size, ctl->indent, stdout);
+
+ /* Get the next line */
+ if (ctl->force_prefix) {
+ line = strtok(NULL, "\n");
+ if (line && *line) {
+ putchar('\n');
+ mesg_size = strlen(line);
+ goto full_output;
+ }
+ free(mesg_copy);
+ }
- if (*(mesg + mesg_size - 1) != '\n')
- putchar('\n');
+ putchar('\n');
}
/*
/*
* /dev/kmsg record format:
*
- * faclev,seqnum,timestamp[optional, ...];messgage\n
+ * faclev,seqnum,timestamp[optional, ...];message\n
* TAGNAME=value
* ...
*
/* D) optional fields (ignore) */
p = skip_item(p, end, ";");
- if (LAST_KMSG_FIELD(p))
- goto mesg;
mesg:
/* E) message text */
rec->mesg = p;
p = skip_item(p, end, "\n");
-
if (!p)
return -1;
- rec->mesg_size = p - rec->mesg;
+ /* The message text is terminated by \n, but it's possible that the
+ * message contains another stuff behind this linebreak; in this case
+ * the previous skip_item() returns pointer to the stuff behind \n.
+ * Let's normalize all these situations and make sure we always point to
+ * the \n.
+ *
+ * Note that the next unhexmangle_to_buffer() will replace \n by \0.
+ */
+ if (*p && *p != '\n')
+ p--;
/*
- * Kernel escapes non-printable characters, unfortuately kernel
+ * Kernel escapes non-printable characters, unfortunately kernel
* definition of "non-printable" is too strict. On UTF8 console we can
* print many chars, so let's decode from kernel.
*/
- unhexmangle_to_buffer(rec->mesg, (char *) rec->mesg, rec->mesg_size + 1);
+ rec->mesg_size = unhexmangle_to_buffer(rec->mesg,
+ (char *) rec->mesg, p - rec->mesg + 1);
+
+ rec->mesg_size--; /* don't count \0 */
/* F) message tags (ignore) */
return 0;
}
-static int which_time_format(const char *optarg)
+static int which_time_format(const char *s)
{
- if (!strcmp(optarg, "notime"))
+ if (!strcmp(s, "notime"))
return DMESG_TIMEFTM_NONE;
- if (!strcmp(optarg, "ctime"))
+ if (!strcmp(s, "ctime"))
return DMESG_TIMEFTM_CTIME;
- if (!strcmp(optarg, "delta"))
+ if (!strcmp(s, "delta"))
return DMESG_TIMEFTM_DELTA;
- if (!strcmp(optarg, "reltime"))
+ if (!strcmp(s, "reltime"))
return DMESG_TIMEFTM_RELTIME;
- if (!strcmp(optarg, "iso"))
+ if (!strcmp(s, "iso"))
return DMESG_TIMEFTM_ISO8601;
- errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
+ errx(EXIT_FAILURE, _("unknown time format: %s"), s);
}
#ifdef TEST_DMESG
{ "userspace", no_argument, NULL, 'u' },
{ "version", no_argument, NULL, 'V' },
{ "time-format", required_argument, NULL, OPT_TIME_FORMAT },
+ { "force-prefix", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
- static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ 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*/
{ 'H','r' }, /* human, raw */
{ 'L','r' }, /* color, raw */
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
- while ((c = getopt_long(argc, argv, "CcDdEeF:f:HhkL::l:n:iPrSs:TtuVwx",
+ while ((c = getopt_long(argc, argv, "CcDdEeF:f:HhkL::l:n:iPprSs:TtuVwx",
longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
colormode = UL_COLORMODE_AUTO;
ctl.pager = 1;
break;
- case 'h':
- usage(stdout);
- break;
case 'k':
ctl.fltr_fac = 1;
setbit(ctl.facilities, FAC_BASE(LOG_KERN));
case 'P':
nopager = 1;
break;
+ case 'p':
+ ctl.force_prefix = 1;
+ break;
case 'r':
ctl.raw = 1;
break;
for (n = 1; (size_t) n < ARRAY_SIZE(facility_names); n++)
setbit(ctl.facilities, n);
break;
- case 'V':
- printf(UTIL_LINUX_VERSION);
- return EXIT_SUCCESS;
case 'w':
ctl.follow = 1;
break;
case OPT_TIME_FORMAT:
ctl.time_fmt = which_time_format(optarg);
break;
- case '?':
+
+ case 'h':
+ usage();
+ case 'V':
+ print_version(EXIT_SUCCESS);
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
- argc -= optind;
- argv += optind;
- if (argc > 1)
- usage(stderr);
-
- if (is_timefmt(&ctl, RELTIME) ||
- is_timefmt(&ctl, CTIME) ||
- is_timefmt(&ctl, ISO8601)) {
- if (dmesg_get_boot_time(&ctl.boot_time) != 0)
- ctl.time_fmt = DMESG_TIMEFTM_NONE;
+ if (argc != optind) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
}
+ if ((is_timefmt(&ctl, RELTIME) ||
+ is_timefmt(&ctl, CTIME) ||
+ is_timefmt(&ctl, ISO8601))
+ && dmesg_get_boot_time(&ctl.boot_time) != 0)
+ ctl.time_fmt = DMESG_TIMEFTM_NONE;
+
if (delta)
switch (ctl.time_fmt) {
case DMESG_TIMEFTM_CTIME:
nopager = 1;
ctl.pager = nopager ? 0 : ctl.pager;
if (ctl.pager)
- setup_pager();
+ pager_redirect();
switch (ctl.action) {
case SYSLOG_ACTION_READ_ALL:
&& (ctl.fltr_lev || ctl.fltr_fac))
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;
+
if (ctl.pager)
- setup_pager();
+ pager_redirect();
n = read_buffer(&ctl, &buf);
if (n > 0)
print_buffer(&ctl, buf, n);