|
|
|
@ -666,11 +666,8 @@ static ssize_t msg_print_ext_header(char *buf, size_t size,
|
|
|
|
|
* better readable output. 'c' in the record flags mark the first
|
|
|
|
|
* fragment of a line, '+' the following.
|
|
|
|
|
*/
|
|
|
|
|
if (msg->flags & LOG_CONT && !(prev_flags & LOG_CONT))
|
|
|
|
|
cont = 'c';
|
|
|
|
|
else if ((msg->flags & LOG_CONT) ||
|
|
|
|
|
((prev_flags & LOG_CONT) && !(msg->flags & LOG_PREFIX)))
|
|
|
|
|
cont = '+';
|
|
|
|
|
if (msg->flags & LOG_CONT)
|
|
|
|
|
cont = (prev_flags & LOG_CONT) ? '+' : 'c';
|
|
|
|
|
|
|
|
|
|
return scnprintf(buf, size, "%u,%llu,%llu,%c;",
|
|
|
|
|
(msg->facility << 3) | msg->level, seq, ts_usec, cont);
|
|
|
|
@ -797,6 +794,8 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cont_flush(void);
|
|
|
|
|
|
|
|
|
|
static ssize_t devkmsg_read(struct file *file, char __user *buf,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
@ -812,6 +811,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
while (user->seq == log_next_seq) {
|
|
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
|
|
|
ret = -EAGAIN;
|
|
|
|
@ -874,6 +874,7 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
|
|
|
|
|
return -ESPIPE;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
switch (whence) {
|
|
|
|
|
case SEEK_SET:
|
|
|
|
|
/* the first record */
|
|
|
|
@ -912,6 +913,7 @@ static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
|
|
|
|
|
poll_wait(file, &log_wait, wait);
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
if (user->seq < log_next_seq) {
|
|
|
|
|
/* return error when data has vanished underneath us */
|
|
|
|
|
if (user->seq < log_first_seq)
|
|
|
|
@ -1298,6 +1300,7 @@ static int syslog_print(char __user *buf, int size)
|
|
|
|
|
size_t skip;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
if (syslog_seq < log_first_seq) {
|
|
|
|
|
/* messages are gone, move to first one */
|
|
|
|
|
syslog_seq = log_first_seq;
|
|
|
|
@ -1357,6 +1360,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
if (buf) {
|
|
|
|
|
u64 next_seq;
|
|
|
|
|
u64 seq;
|
|
|
|
@ -1518,6 +1522,7 @@ int do_syslog(int type, char __user *buf, int len, int source)
|
|
|
|
|
/* Number of chars in the log buffer */
|
|
|
|
|
case SYSLOG_ACTION_SIZE_UNREAD:
|
|
|
|
|
raw_spin_lock_irq(&logbuf_lock);
|
|
|
|
|
cont_flush();
|
|
|
|
|
if (syslog_seq < log_first_seq) {
|
|
|
|
|
/* messages are gone, move to first one */
|
|
|
|
|
syslog_seq = log_first_seq;
|
|
|
|
@ -1654,35 +1659,33 @@ static struct cont {
|
|
|
|
|
bool flushed:1; /* buffer sealed and committed */
|
|
|
|
|
} cont;
|
|
|
|
|
|
|
|
|
|
static void cont_flush(enum log_flags flags)
|
|
|
|
|
static void cont_flush(void)
|
|
|
|
|
{
|
|
|
|
|
if (cont.flushed)
|
|
|
|
|
return;
|
|
|
|
|
if (cont.len == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (cont.cons) {
|
|
|
|
|
/*
|
|
|
|
|
* If a fragment of this line was directly flushed to the
|
|
|
|
|
* console; wait for the console to pick up the rest of the
|
|
|
|
|
* line. LOG_NOCONS suppresses a duplicated output.
|
|
|
|
|
*/
|
|
|
|
|
log_store(cont.facility, cont.level, flags | LOG_NOCONS,
|
|
|
|
|
log_store(cont.facility, cont.level, cont.flags | LOG_NOCONS,
|
|
|
|
|
cont.ts_nsec, NULL, 0, cont.buf, cont.len);
|
|
|
|
|
cont.flags = flags;
|
|
|
|
|
cont.flushed = true;
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* If no fragment of this line ever reached the console,
|
|
|
|
|
* just submit it to the store and free the buffer.
|
|
|
|
|
*/
|
|
|
|
|
log_store(cont.facility, cont.level, flags, 0,
|
|
|
|
|
log_store(cont.facility, cont.level, cont.flags, 0,
|
|
|
|
|
NULL, 0, cont.buf, cont.len);
|
|
|
|
|
cont.len = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool cont_add(int facility, int level, const char *text, size_t len)
|
|
|
|
|
static bool cont_add(int facility, int level, enum log_flags flags, const char *text, size_t len)
|
|
|
|
|
{
|
|
|
|
|
if (cont.len && cont.flushed)
|
|
|
|
|
return false;
|
|
|
|
@ -1693,7 +1696,7 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
|
|
|
|
|
* the line gets too long, split it up in separate records.
|
|
|
|
|
*/
|
|
|
|
|
if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) {
|
|
|
|
|
cont_flush(LOG_CONT);
|
|
|
|
|
cont_flush();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1702,7 +1705,7 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
|
|
|
|
|
cont.level = level;
|
|
|
|
|
cont.owner = current;
|
|
|
|
|
cont.ts_nsec = local_clock();
|
|
|
|
|
cont.flags = 0;
|
|
|
|
|
cont.flags = flags;
|
|
|
|
|
cont.cons = 0;
|
|
|
|
|
cont.flushed = false;
|
|
|
|
|
}
|
|
|
|
@ -1710,8 +1713,15 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
|
|
|
|
|
memcpy(cont.buf + cont.len, text, len);
|
|
|
|
|
cont.len += len;
|
|
|
|
|
|
|
|
|
|
// The original flags come from the first line,
|
|
|
|
|
// but later continuations can add a newline.
|
|
|
|
|
if (flags & LOG_NEWLINE) {
|
|
|
|
|
cont.flags |= LOG_NEWLINE;
|
|
|
|
|
cont_flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cont.len > (sizeof(cont.buf) * 80) / 100)
|
|
|
|
|
cont_flush(LOG_CONT);
|
|
|
|
|
cont_flush();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -1744,6 +1754,31 @@ static size_t cont_print_text(char *text, size_t size)
|
|
|
|
|
return textlen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If an earlier line was buffered, and we're a continuation
|
|
|
|
|
* write from the same process, try to add it to the buffer.
|
|
|
|
|
*/
|
|
|
|
|
if (cont.len) {
|
|
|
|
|
if (cont.owner == current && (lflags & LOG_CONT)) {
|
|
|
|
|
if (cont_add(facility, level, lflags, text, text_len))
|
|
|
|
|
return text_len;
|
|
|
|
|
}
|
|
|
|
|
/* Otherwise, make sure it's flushed */
|
|
|
|
|
cont_flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If it doesn't end in a newline, try to buffer the current line */
|
|
|
|
|
if (!(lflags & LOG_NEWLINE)) {
|
|
|
|
|
if (cont_add(facility, level, lflags, text, text_len))
|
|
|
|
|
return text_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Store it in the record log */
|
|
|
|
|
return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asmlinkage int vprintk_emit(int facility, int level,
|
|
|
|
|
const char *dict, size_t dictlen,
|
|
|
|
|
const char *fmt, va_list args)
|
|
|
|
@ -1830,10 +1865,9 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
|
|
|
|
|
|
|
/* strip kernel syslog prefix and extract log level or control flags */
|
|
|
|
|
if (facility == 0) {
|
|
|
|
|
int kern_level = printk_get_level(text);
|
|
|
|
|
int kern_level;
|
|
|
|
|
|
|
|
|
|
if (kern_level) {
|
|
|
|
|
const char *end_of_header = printk_skip_level(text);
|
|
|
|
|
while ((kern_level = printk_get_level(text)) != 0) {
|
|
|
|
|
switch (kern_level) {
|
|
|
|
|
case '0' ... '7':
|
|
|
|
|
if (level == LOGLEVEL_DEFAULT)
|
|
|
|
@ -1841,14 +1875,13 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
|
|
/* fallthrough */
|
|
|
|
|
case 'd': /* KERN_DEFAULT */
|
|
|
|
|
lflags |= LOG_PREFIX;
|
|
|
|
|
break;
|
|
|
|
|
case 'c': /* KERN_CONT */
|
|
|
|
|
lflags |= LOG_CONT;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* No need to check length here because vscnprintf
|
|
|
|
|
* put '\0' at the end of the string. Only valid and
|
|
|
|
|
* newly printed level is detected.
|
|
|
|
|
*/
|
|
|
|
|
text_len -= end_of_header - text;
|
|
|
|
|
text = (char *)end_of_header;
|
|
|
|
|
|
|
|
|
|
text_len -= 2;
|
|
|
|
|
text += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1858,45 +1891,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|
|
|
|
if (dict)
|
|
|
|
|
lflags |= LOG_PREFIX|LOG_NEWLINE;
|
|
|
|
|
|
|
|
|
|
if (!(lflags & LOG_NEWLINE)) {
|
|
|
|
|
/*
|
|
|
|
|
* Flush the conflicting buffer. An earlier newline was missing,
|
|
|
|
|
* or another task also prints continuation lines.
|
|
|
|
|
*/
|
|
|
|
|
if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
|
|
|
|
|
cont_flush(LOG_NEWLINE);
|
|
|
|
|
|
|
|
|
|
/* buffer line if possible, otherwise store it right away */
|
|
|
|
|
if (cont_add(facility, level, text, text_len))
|
|
|
|
|
printed_len += text_len;
|
|
|
|
|
else
|
|
|
|
|
printed_len += log_store(facility, level,
|
|
|
|
|
lflags | LOG_CONT, 0,
|
|
|
|
|
dict, dictlen, text, text_len);
|
|
|
|
|
} else {
|
|
|
|
|
bool stored = false;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If an earlier newline was missing and it was the same task,
|
|
|
|
|
* either merge it with the current buffer and flush, or if
|
|
|
|
|
* there was a race with interrupts (prefix == true) then just
|
|
|
|
|
* flush it out and store this line separately.
|
|
|
|
|
* If the preceding printk was from a different task and missed
|
|
|
|
|
* a newline, flush and append the newline.
|
|
|
|
|
*/
|
|
|
|
|
if (cont.len) {
|
|
|
|
|
if (cont.owner == current && !(lflags & LOG_PREFIX))
|
|
|
|
|
stored = cont_add(facility, level, text,
|
|
|
|
|
text_len);
|
|
|
|
|
cont_flush(LOG_NEWLINE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stored)
|
|
|
|
|
printed_len += text_len;
|
|
|
|
|
else
|
|
|
|
|
printed_len += log_store(facility, level, lflags, 0,
|
|
|
|
|
dict, dictlen, text, text_len);
|
|
|
|
|
}
|
|
|
|
|
printed_len += log_output(facility, level, lflags, dict, dictlen, text, text_len);
|
|
|
|
|
|
|
|
|
|
logbuf_cpu = UINT_MAX;
|
|
|
|
|
raw_spin_unlock(&logbuf_lock);
|
|
|
|
@ -3040,6 +3035,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
|
|
|
|
|
dumper->active = true;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
|
|
|
|
cont_flush();
|
|
|
|
|
dumper->cur_seq = clear_seq;
|
|
|
|
|
dumper->cur_idx = clear_idx;
|
|
|
|
|
dumper->next_seq = log_next_seq;
|
|
|
|
@ -3130,6 +3126,7 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
|
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
|
|
|
|
cont_flush();
|
|
|
|
|
ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len);
|
|
|
|
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
|
|
|
|
|
|
|
@ -3172,6 +3169,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
|
|
|
|
cont_flush();
|
|
|
|
|
if (dumper->cur_seq < log_first_seq) {
|
|
|
|
|
/* messages are gone, move to first available one */
|
|
|
|
|
dumper->cur_seq = log_first_seq;
|
|
|
|
|