X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=sys-utils%2Fdmesg.c;h=f96e5bdf7e52a1c2c26a086d87adb415f91f3ac0;hb=2c308875a7fa1aaa44892c368f6b37bcfcb8879a;hp=b351595fca6883ca6a15db9b58e9de4d4828b6cf;hpb=54d172327b9080edf2b712cc0b33352032eb8f5c;p=thirdparty%2Futil-linux.git diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c index b351595fca..f96e5bdf7e 100644 --- a/sys-utils/dmesg.c +++ b/sys-utils/dmesg.c @@ -6,7 +6,6 @@ * * This program comes with ABSOLUTELY NO WARRANTY. */ -#include #include #include #include @@ -119,7 +118,7 @@ static const struct dmesg_name level_names[] = /* * 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 :-) @@ -196,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 */ }; @@ -244,7 +245,7 @@ static int set_level_color(int log_level, const char *mesg, size_t mesgsz) 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)) @@ -256,8 +257,9 @@ static int set_level_color(int log_level, const char *mesg, size_t mesgsz) 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); @@ -281,6 +283,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) fputs(_(" -l, --level restrict output to defined levels\n"), out); fputs(_(" -n, --console-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 buffer size to query the kernel ring buffer\n"), out); @@ -295,8 +298,7 @@ static void __attribute__((__noreturn__)) usage(FILE *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", @@ -308,9 +310,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out) 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); } /* @@ -320,7 +322,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) * ::= case-insensitive text * * Note that @len argument is not set when parsing "-n " 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 @@ -591,6 +593,8 @@ static ssize_t read_buffer(struct dmesg_control *ctl, char **buf) 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; @@ -601,7 +605,7 @@ static int fwrite_hex(const char *buf, size_t size, FILE *out) 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; } @@ -639,8 +643,8 @@ static void safe_fwrite(const char *buf, size_t size, int indent, FILE *out) 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) @@ -742,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) @@ -832,9 +840,7 @@ static char *iso_8601_time(struct dmesg_control *ctl, struct dmesg_record *rec, .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; @@ -861,7 +867,7 @@ static const char *get_subsys_delimiter(const char *mesg, size_t mesg_size) 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; @@ -874,11 +880,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; - 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; @@ -889,30 +897,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 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; @@ -920,15 +926,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); @@ -936,62 +944,120 @@ 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; + /* + * 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'); } /* @@ -1066,7 +1132,7 @@ static int init_kmsg(struct dmesg_control *ctl) /* * /dev/kmsg record format: * - * faclev,seqnum,timestamp[optional, ...];messgage\n + * faclev,seqnum,timestamp[optional, ...];message\n * TAGNAME=value * ... * @@ -1116,25 +1182,34 @@ static int parse_kmsg_record(struct dmesg_control *ctl, /* 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) */ @@ -1178,19 +1253,19 @@ static int read_kmsg(struct dmesg_control *ctl) 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 @@ -1258,10 +1333,11 @@ 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 } }; - 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 */ @@ -1278,9 +1354,9 @@ int main(int argc, char *argv[]) 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); @@ -1319,9 +1395,6 @@ int main(int argc, char *argv[]) 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)); @@ -1345,6 +1418,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; @@ -1368,9 +1444,6 @@ int main(int argc, char *argv[]) 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; @@ -1380,24 +1453,27 @@ int main(int argc, char *argv[]) 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: @@ -1419,7 +1495,7 @@ int main(int argc, char *argv[]) nopager = 1; ctl.pager = nopager ? 0 : ctl.pager; if (ctl.pager) - setup_pager(); + pager_redirect(); switch (ctl.action) { case SYSLOG_ACTION_READ_ALL: @@ -1432,8 +1508,13 @@ 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) - setup_pager(); + pager_redirect(); n = read_buffer(&ctl, &buf); if (n > 0) print_buffer(&ctl, buf, n);