diff options
-rw-r--r-- | sys-utils/dmesg.1 | 2 | ||||
-rw-r--r-- | sys-utils/dmesg.c | 208 |
2 files changed, 150 insertions, 60 deletions
diff --git a/sys-utils/dmesg.1 b/sys-utils/dmesg.1 index b87b2301b..1dc9994ec 100644 --- a/sys-utils/dmesg.1 +++ b/sys-utils/dmesg.1 @@ -115,6 +115,8 @@ will print or clear the kernel ring buffer. .IP "\fB\-P\fR, \fB\-\-nopager\fR" Do not pipe output into a pager. A pager is enabled by default for \fB\-\-human\fR output. +.IP "\fB\-p\fR, \fB\-\-force\-prefix\fR" +Add facility, level or timestamp information to each line of a multi-line message. .IP "\fB\-r\fR, \fB\-\-raw\fR" Print the raw message buffer, i.e. do not strip the log-level prefixes. diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c index 6e5911a76..9fdc1d8a7 100644 --- a/sys-utils/dmesg.c +++ b/sys-utils/dmesg.c @@ -195,7 +195,9 @@ struct dmesg_control { 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 */ }; @@ -281,6 +283,7 @@ static void __attribute__((__noreturn__)) usage(void) 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); @@ -743,6 +746,10 @@ static int get_next_syslog_record(struct dmesg_control *ctl, 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) @@ -875,11 +882,13 @@ static const char *get_subsys_delimiter(const char *mesg, size_t mesg_size) static void print_record(struct dmesg_control *ctl, struct dmesg_record *rec) { - char buf[256]; - int has_color = 0; - const char *mesg; + char buf[128]; + char fpbuf[32] = "\0"; + char tsbuf[64] = "\0"; size_t mesg_size; - int indent = 0; + int timebreak = 0; + char *mesg_copy = NULL; + const char *line = NULL; if (!accept_record(ctl, rec)) return; @@ -890,30 +899,28 @@ static void print_record(struct dmesg_control *ctl, } /* - * 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 mesg_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; @@ -921,15 +928,17 @@ static void print_record(struct dmesg_control *ctl, 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); @@ -937,62 +946,121 @@ static void print_record(struct dmesg_control *ctl, 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; +mesg_output: + /* + * 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 = strdup(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'); } /* @@ -1122,18 +1190,29 @@ 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 notmalize all these sitations 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, 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) */ @@ -1257,6 +1336,7 @@ int main(int argc, char *argv[]) { "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 } }; @@ -1279,7 +1359,7 @@ int main(int argc, char *argv[]) textdomain(PACKAGE); atexit(close_stdout); - 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); @@ -1344,6 +1424,9 @@ int main(int argc, char *argv[]) case 'P': nopager = 1; break; + case 'p': + ctl.force_prefix = 1; + break; case 'r': ctl.raw = 1; break; @@ -1429,6 +1512,11 @@ int main(int argc, char *argv[]) && (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) pager_redirect(); n = read_buffer(&ctl, &buf); |