Previously, `dmesg -c` would clear the logbuffer except for the last 10 lines. This comes with a fair amount of surprise given linux `dmesg -c` clears the whole logbuffer.
Clear the complete logbuffer given `dmesg -c` but with an optional argument `dmesg -c [<num>]` to keep <num> lines. While at it, move the deleteion of log lines to the log_print function to prevent a corner case of lost log message if they arrive between the log_print() and log_clean() in dmesg. If loglevels are selected using -l or -p in addition to cleaning being selected using -c, only clean those messages shown by the loglevel selection. Signed-off-by: Jonas Rebmann <[email protected]> --- commands/dmesg.c | 17 ++++++++++------- common/console_common.c | 20 +++++++++++++++++--- include/linux/printk.h | 2 +- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/commands/dmesg.c b/commands/dmesg.c index a93ad5b359..6853f3afc9 100644 --- a/commands/dmesg.c +++ b/commands/dmesg.c @@ -77,14 +77,20 @@ static unsigned dmesg_get_levels(const char *__args) static int do_dmesg(int argc, char *argv[]) { int opt, ret, i; - int delete_buf = 0, emit = 0; + int delete_buf = -1, emit = 0; unsigned flags = 0, levels = 0; char *set = NULL; - while ((opt = getopt(argc, argv, "ctderl:p:n:")) > 0) { + while ((opt = getopt(argc, argv, "c::tderl:p:n:")) > 0) { switch (opt) { case 'c': - delete_buf = 1; + if (optarg) { + ret = kstrtoint(optarg, 10, &delete_buf); + if (ret || delete_buf < 0) + return COMMAND_ERROR_USAGE; + } else { + delete_buf = 0; + } break; case 't': flags |= BAREBOX_LOG_PRINT_TIME; @@ -155,13 +161,10 @@ static int do_dmesg(int argc, char *argv[]) return 0; } - ret = log_print(flags, levels); + ret = log_print(flags, levels, delete_buf); if (ret) return 1; - if (delete_buf) - log_clean(10); - return 0; } diff --git a/common/console_common.c b/common/console_common.c index 5b7a64c99c..23646d8f81 100644 --- a/common/console_common.c +++ b/common/console_common.c @@ -210,12 +210,23 @@ int log_writefile(const char *filepath) return ret < 0 ? ret : nbytes; } -int log_print(unsigned flags, unsigned levels) +/** + * log_print - print and optionally clear the log buffer + * + * @flags: Flags selecting output formatting + * @levels: bitmask of loglevels to print, 0 for all + * @delete_buf: Number of printed lines to keep in the buffer, + * -1 to keep all. + * + * This function deletes all messages in the logbuf exceeding + * the limit. + */ +int log_print(unsigned flags, unsigned levels, int delete_buf) { - struct log_entry *log; + struct log_entry *log, *tmp; unsigned long last = 0; - list_for_each_entry(log, &barebox_logbuf, list) { + list_for_each_entry_safe(log, tmp, &barebox_logbuf, list) { uint64_t time_ns = log->timestamp; unsigned long time; @@ -250,6 +261,9 @@ int log_print(unsigned flags, unsigned levels) printf("] "); printf("%s", log->msg); + + if (delete_buf >= 0 && barebox_logbuf_num_messages > delete_buf) + log_del(log); } return 0; diff --git a/include/linux/printk.h b/include/linux/printk.h index 0d7180f0eb..78ab3e6d16 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -192,7 +192,7 @@ extern void log_clean(unsigned int limit); #define BAREBOX_LOG_PRINT_TIME BIT(0) int log_writefile(const char *filepath); -int log_print(unsigned flags, unsigned levels); +int log_print(unsigned flags, unsigned levels, int delete_buf); struct va_format { const char *fmt; --- base-commit: 8defba1d0ab1aef9dd5d57710e18d0d02e2c48e2 change-id: 20251017-dmesg-05b9e462e8a1 Best regards, -- Jonas Rebmann <[email protected]>
