|
@@ -194,8 +194,10 @@ static int console_may_schedule;
|
|
|
*/
|
|
|
|
|
|
enum log_flags {
|
|
|
- LOG_DEFAULT = 0,
|
|
|
- LOG_NOCONS = 1, /* already flushed, do not print to console */
|
|
|
+ LOG_NOCONS = 1, /* already flushed, do not print to console */
|
|
|
+ LOG_NEWLINE = 2, /* text ended with a newline */
|
|
|
+ LOG_PREFIX = 4, /* text started with a prefix */
|
|
|
+ LOG_CONT = 8, /* text is a fragment of a continuation line */
|
|
|
};
|
|
|
|
|
|
struct log {
|
|
@@ -217,6 +219,7 @@ static DEFINE_RAW_SPINLOCK(logbuf_lock);
|
|
|
/* the next printk record to read by syslog(READ) or /proc/kmsg */
|
|
|
static u64 syslog_seq;
|
|
|
static u32 syslog_idx;
|
|
|
+static enum log_flags syslog_prev;
|
|
|
static size_t syslog_partial;
|
|
|
|
|
|
/* index and sequence number of the first record stored in the buffer */
|
|
@@ -839,13 +842,26 @@ static size_t print_prefix(const struct log *msg, bool syslog, char *buf)
|
|
|
return len;
|
|
|
}
|
|
|
|
|
|
-static size_t msg_print_text(const struct log *msg, bool syslog,
|
|
|
- char *buf, size_t size)
|
|
|
+static size_t msg_print_text(const struct log *msg, enum log_flags prev,
|
|
|
+ bool syslog, char *buf, size_t size)
|
|
|
{
|
|
|
const char *text = log_text(msg);
|
|
|
size_t text_size = msg->text_len;
|
|
|
+ bool prefix = true;
|
|
|
+ bool newline = true;
|
|
|
size_t len = 0;
|
|
|
|
|
|
+ if ((prev & LOG_CONT) && !(msg->flags & LOG_PREFIX))
|
|
|
+ prefix = false;
|
|
|
+
|
|
|
+ if (msg->flags & LOG_CONT) {
|
|
|
+ if ((prev & LOG_CONT) && !(prev & LOG_NEWLINE))
|
|
|
+ prefix = false;
|
|
|
+
|
|
|
+ if (!(msg->flags & LOG_NEWLINE))
|
|
|
+ newline = false;
|
|
|
+ }
|
|
|
+
|
|
|
do {
|
|
|
const char *next = memchr(text, '\n', text_size);
|
|
|
size_t text_len;
|
|
@@ -863,16 +879,22 @@ static size_t msg_print_text(const struct log *msg, bool syslog,
|
|
|
text_len + 1>= size - len)
|
|
|
break;
|
|
|
|
|
|
- len += print_prefix(msg, syslog, buf + len);
|
|
|
+ if (prefix)
|
|
|
+ len += print_prefix(msg, syslog, buf + len);
|
|
|
memcpy(buf + len, text, text_len);
|
|
|
len += text_len;
|
|
|
- buf[len++] = '\n';
|
|
|
+ if (next || newline)
|
|
|
+ buf[len++] = '\n';
|
|
|
} else {
|
|
|
/* SYSLOG_ACTION_* buffer size only calculation */
|
|
|
- len += print_prefix(msg, syslog, NULL);
|
|
|
- len += text_len + 1;
|
|
|
+ if (prefix)
|
|
|
+ len += print_prefix(msg, syslog, NULL);
|
|
|
+ len += text_len;
|
|
|
+ if (next || newline)
|
|
|
+ len++;
|
|
|
}
|
|
|
|
|
|
+ prefix = true;
|
|
|
text = next;
|
|
|
} while (text);
|
|
|
|
|
@@ -898,6 +920,7 @@ static int syslog_print(char __user *buf, int size)
|
|
|
/* messages are gone, move to first one */
|
|
|
syslog_seq = log_first_seq;
|
|
|
syslog_idx = log_first_idx;
|
|
|
+ syslog_prev = 0;
|
|
|
syslog_partial = 0;
|
|
|
}
|
|
|
if (syslog_seq == log_next_seq) {
|
|
@@ -907,11 +930,12 @@ static int syslog_print(char __user *buf, int size)
|
|
|
|
|
|
skip = syslog_partial;
|
|
|
msg = log_from_idx(syslog_idx);
|
|
|
- n = msg_print_text(msg, true, text, LOG_LINE_MAX);
|
|
|
+ n = msg_print_text(msg, syslog_prev, true, text, LOG_LINE_MAX);
|
|
|
if (n - syslog_partial <= size) {
|
|
|
/* message fits into buffer, move forward */
|
|
|
syslog_idx = log_next(syslog_idx);
|
|
|
syslog_seq++;
|
|
|
+ syslog_prev = msg->flags;
|
|
|
n -= syslog_partial;
|
|
|
syslog_partial = 0;
|
|
|
} else if (!len){
|
|
@@ -954,6 +978,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
u64 next_seq;
|
|
|
u64 seq;
|
|
|
u32 idx;
|
|
|
+ enum log_flags prev;
|
|
|
|
|
|
if (clear_seq < log_first_seq) {
|
|
|
/* messages are gone, move to first available one */
|
|
@@ -967,10 +992,11 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
*/
|
|
|
seq = clear_seq;
|
|
|
idx = clear_idx;
|
|
|
+ prev = 0;
|
|
|
while (seq < log_next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- len += msg_print_text(msg, true, NULL, 0);
|
|
|
+ len += msg_print_text(msg, prev, true, NULL, 0);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
}
|
|
@@ -978,10 +1004,11 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
/* move first record forward until length fits into the buffer */
|
|
|
seq = clear_seq;
|
|
|
idx = clear_idx;
|
|
|
+ prev = 0;
|
|
|
while (len > size && seq < log_next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- len -= msg_print_text(msg, true, NULL, 0);
|
|
|
+ len -= msg_print_text(msg, prev, true, NULL, 0);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
}
|
|
@@ -990,17 +1017,19 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
next_seq = log_next_seq;
|
|
|
|
|
|
len = 0;
|
|
|
+ prev = 0;
|
|
|
while (len >= 0 && seq < next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
int textlen;
|
|
|
|
|
|
- textlen = msg_print_text(msg, true, text, LOG_LINE_MAX);
|
|
|
+ textlen = msg_print_text(msg, prev, true, text, LOG_LINE_MAX);
|
|
|
if (textlen < 0) {
|
|
|
len = textlen;
|
|
|
break;
|
|
|
}
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
+ prev = msg->flags;
|
|
|
|
|
|
raw_spin_unlock_irq(&logbuf_lock);
|
|
|
if (copy_to_user(buf + len, text, textlen))
|
|
@@ -1013,6 +1042,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
/* messages are gone, move to next one */
|
|
|
seq = log_first_seq;
|
|
|
idx = log_first_idx;
|
|
|
+ prev = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1117,6 +1147,7 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
|
|
|
/* messages are gone, move to first one */
|
|
|
syslog_seq = log_first_seq;
|
|
|
syslog_idx = log_first_idx;
|
|
|
+ syslog_prev = 0;
|
|
|
syslog_partial = 0;
|
|
|
}
|
|
|
if (from_file) {
|
|
@@ -1127,18 +1158,18 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
|
|
|
*/
|
|
|
error = log_next_idx - syslog_idx;
|
|
|
} else {
|
|
|
- u64 seq;
|
|
|
- u32 idx;
|
|
|
+ u64 seq = syslog_seq;
|
|
|
+ u32 idx = syslog_idx;
|
|
|
+ enum log_flags prev = syslog_prev;
|
|
|
|
|
|
error = 0;
|
|
|
- seq = syslog_seq;
|
|
|
- idx = syslog_idx;
|
|
|
while (seq < log_next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- error += msg_print_text(msg, true, NULL, 0);
|
|
|
+ error += msg_print_text(msg, prev, true, NULL, 0);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
+ prev = msg->flags;
|
|
|
}
|
|
|
error -= syslog_partial;
|
|
|
}
|
|
@@ -1408,10 +1439,9 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
static char textbuf[LOG_LINE_MAX];
|
|
|
char *text = textbuf;
|
|
|
size_t text_len;
|
|
|
+ enum log_flags lflags = 0;
|
|
|
unsigned long flags;
|
|
|
int this_cpu;
|
|
|
- bool newline = false;
|
|
|
- bool prefix = false;
|
|
|
int printed_len = 0;
|
|
|
|
|
|
boot_delay_msec();
|
|
@@ -1450,7 +1480,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
recursion_bug = 0;
|
|
|
printed_len += strlen(recursion_msg);
|
|
|
/* emit KERN_CRIT message */
|
|
|
- log_store(0, 2, LOG_DEFAULT, 0,
|
|
|
+ log_store(0, 2, LOG_PREFIX|LOG_NEWLINE, 0,
|
|
|
NULL, 0, recursion_msg, printed_len);
|
|
|
}
|
|
|
|
|
@@ -1463,7 +1493,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
/* mark and strip a trailing newline */
|
|
|
if (text_len && text[text_len-1] == '\n') {
|
|
|
text_len--;
|
|
|
- newline = true;
|
|
|
+ lflags |= LOG_NEWLINE;
|
|
|
}
|
|
|
|
|
|
/* strip syslog prefix and extract log level or control flags */
|
|
@@ -1473,7 +1503,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
if (level == -1)
|
|
|
level = text[1] - '0';
|
|
|
case 'd': /* KERN_DEFAULT */
|
|
|
- prefix = true;
|
|
|
+ lflags |= LOG_PREFIX;
|
|
|
case 'c': /* KERN_CONT */
|
|
|
text += 3;
|
|
|
text_len -= 3;
|
|
@@ -1483,22 +1513,20 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
if (level == -1)
|
|
|
level = default_message_loglevel;
|
|
|
|
|
|
- if (dict) {
|
|
|
- prefix = true;
|
|
|
- newline = true;
|
|
|
- }
|
|
|
+ if (dict)
|
|
|
+ lflags |= LOG_PREFIX|LOG_NEWLINE;
|
|
|
|
|
|
- if (!newline) {
|
|
|
+ if (!(lflags & LOG_NEWLINE)) {
|
|
|
/*
|
|
|
* Flush the conflicting buffer. An earlier newline was missing,
|
|
|
* or another task also prints continuation lines.
|
|
|
*/
|
|
|
- if (cont.len && (prefix || cont.owner != current))
|
|
|
+ if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
|
|
|
cont_flush();
|
|
|
|
|
|
/* buffer line if possible, otherwise store it right away */
|
|
|
if (!cont_add(facility, level, text, text_len))
|
|
|
- log_store(facility, level, LOG_DEFAULT, 0,
|
|
|
+ log_store(facility, level, lflags | LOG_CONT, 0,
|
|
|
dict, dictlen, text, text_len);
|
|
|
} else {
|
|
|
bool stored = false;
|
|
@@ -1510,13 +1538,13 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
* flush it out and store this line separately.
|
|
|
*/
|
|
|
if (cont.len && cont.owner == current) {
|
|
|
- if (!prefix)
|
|
|
+ if (!(lflags & LOG_PREFIX))
|
|
|
stored = cont_add(facility, level, text, text_len);
|
|
|
cont_flush();
|
|
|
}
|
|
|
|
|
|
if (!stored)
|
|
|
- log_store(facility, level, LOG_DEFAULT, 0,
|
|
|
+ log_store(facility, level, lflags, 0,
|
|
|
dict, dictlen, text, text_len);
|
|
|
}
|
|
|
printed_len += text_len;
|
|
@@ -1615,8 +1643,8 @@ static struct cont {
|
|
|
static struct log *log_from_idx(u32 idx) { return NULL; }
|
|
|
static u32 log_next(u32 idx) { return 0; }
|
|
|
static void call_console_drivers(int level, const char *text, size_t len) {}
|
|
|
-static size_t msg_print_text(const struct log *msg, bool syslog,
|
|
|
- char *buf, size_t size) { return 0; }
|
|
|
+static size_t msg_print_text(const struct log *msg, enum log_flags prev,
|
|
|
+ bool syslog, char *buf, size_t size) { return 0; }
|
|
|
static size_t cont_print_text(char *text, size_t size) { return 0; }
|
|
|
|
|
|
#endif /* CONFIG_PRINTK */
|
|
@@ -1892,6 +1920,7 @@ void wake_up_klogd(void)
|
|
|
/* the next printk record to write to the console */
|
|
|
static u64 console_seq;
|
|
|
static u32 console_idx;
|
|
|
+static enum log_flags console_prev;
|
|
|
|
|
|
/**
|
|
|
* console_unlock - unlock the console system
|
|
@@ -1952,6 +1981,7 @@ again:
|
|
|
/* messages are gone, move to first one */
|
|
|
console_seq = log_first_seq;
|
|
|
console_idx = log_first_idx;
|
|
|
+ console_prev = 0;
|
|
|
}
|
|
|
skip:
|
|
|
if (console_seq == log_next_seq)
|
|
@@ -1975,10 +2005,11 @@ skip:
|
|
|
}
|
|
|
|
|
|
level = msg->level;
|
|
|
- len = msg_print_text(msg, false, text, sizeof(text));
|
|
|
-
|
|
|
+ len = msg_print_text(msg, console_prev, false,
|
|
|
+ text, sizeof(text));
|
|
|
console_idx = log_next(console_idx);
|
|
|
console_seq++;
|
|
|
+ console_prev = msg->flags;
|
|
|
raw_spin_unlock(&logbuf_lock);
|
|
|
|
|
|
stop_critical_timings(); /* don't trace print latency */
|
|
@@ -2241,6 +2272,7 @@ void register_console(struct console *newcon)
|
|
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
|
|
console_seq = syslog_seq;
|
|
|
console_idx = syslog_idx;
|
|
|
+ console_prev = syslog_prev;
|
|
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
|
/*
|
|
|
* We're about to replay the log buffer. Only do this to the
|
|
@@ -2534,8 +2566,7 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
|
|
|
}
|
|
|
|
|
|
msg = log_from_idx(dumper->cur_idx);
|
|
|
- l = msg_print_text(msg, syslog,
|
|
|
- line, size);
|
|
|
+ l = msg_print_text(msg, 0, syslog, line, size);
|
|
|
|
|
|
dumper->cur_idx = log_next(dumper->cur_idx);
|
|
|
dumper->cur_seq++;
|
|
@@ -2575,6 +2606,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
|
|
u32 idx;
|
|
|
u64 next_seq;
|
|
|
u32 next_idx;
|
|
|
+ enum log_flags prev;
|
|
|
size_t l = 0;
|
|
|
bool ret = false;
|
|
|
|
|
@@ -2597,23 +2629,27 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
|
|
/* calculate length of entire buffer */
|
|
|
seq = dumper->cur_seq;
|
|
|
idx = dumper->cur_idx;
|
|
|
+ prev = 0;
|
|
|
while (seq < dumper->next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- l += msg_print_text(msg, true, NULL, 0);
|
|
|
+ l += msg_print_text(msg, prev, true, NULL, 0);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
+ prev = msg->flags;
|
|
|
}
|
|
|
|
|
|
/* move first record forward until length fits into the buffer */
|
|
|
seq = dumper->cur_seq;
|
|
|
idx = dumper->cur_idx;
|
|
|
+ prev = 0;
|
|
|
while (l > size && seq < dumper->next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- l -= msg_print_text(msg, true, NULL, 0);
|
|
|
+ l -= msg_print_text(msg, prev, true, NULL, 0);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
+ prev = msg->flags;
|
|
|
}
|
|
|
|
|
|
/* last message in next interation */
|
|
@@ -2621,14 +2657,14 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
|
|
next_idx = idx;
|
|
|
|
|
|
l = 0;
|
|
|
+ prev = 0;
|
|
|
while (seq < dumper->next_seq) {
|
|
|
struct log *msg = log_from_idx(idx);
|
|
|
|
|
|
- l += msg_print_text(msg, syslog,
|
|
|
- buf + l, size - l);
|
|
|
-
|
|
|
+ l += msg_print_text(msg, prev, syslog, buf + l, size - l);
|
|
|
idx = log_next(idx);
|
|
|
seq++;
|
|
|
+ prev = msg->flags;
|
|
|
}
|
|
|
|
|
|
dumper->next_seq = next_seq;
|