kmsg - export "continuation record" flag to /dev/kmsg

In some cases we are forced to store individual records for a continuation
line print.

Export a flag to allow the external re-construction of the line. The flag
allows us to apply a similar logic externally which is used internally when
the console, /proc/kmsg or the syslog() output is printed.

  $ cat /dev/kmsg
  4,165,0,-;Free swap  = 0kB
  4,166,0,-;Total swap = 0kB
  6,167,0,c;[
  4,168,0,+;0
  4,169,0,+;1
  4,170,0,+;2
  4,171,0,+;3
  4,172,0,+;]
  6,173,0,-;[0 1 2 3 ]
  6,174,0,-;Console: colour VGA+ 80x25
  6,175,0,-;console [tty0] enabled

Signed-off-by: Kay Sievers <kay@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/dev-kmsg b/Documentation/ABI/testing/dev-kmsg
index 281ecc5..7e7e07a 100644
--- a/Documentation/ABI/testing/dev-kmsg
+++ b/Documentation/ABI/testing/dev-kmsg
@@ -58,16 +58,18 @@
 
 		The output format consists of a prefix carrying the syslog
 		prefix including priority and facility, the 64 bit message
-		sequence number and the monotonic timestamp in microseconds.
-		The values are separated by a ','. Future extensions might
-		add more comma separated values before the terminating ';'.
-		Unknown values should be gracefully ignored.
+		sequence number and the monotonic timestamp in microseconds,
+		and a flag field. All fields are separated by a ','.
+
+		Future extensions might add more comma separated values before
+		the terminating ';'. Unknown fields and values should be
+		gracefully ignored.
 
 		The human readable text string starts directly after the ';'
 		and is terminated by a '\n'. Untrusted values derived from
 		hardware or other facilities are printed, therefore
-		all non-printable characters in the log message are escaped
-		by "\x00" C-style hex encoding.
+		all non-printable characters and '\' itself in the log message
+		are escaped by "\x00" C-style hex encoding.
 
 		A line starting with ' ', is a continuation line, adding
 		key/value pairs to the log message, which provide the machine
@@ -75,11 +77,11 @@
 		userspace.
 
 		Example:
-		7,160,424069;pci_root PNP0A03:00: host bridge window [io  0x0000-0x0cf7] (ignored)
+		7,160,424069,-;pci_root PNP0A03:00: host bridge window [io  0x0000-0x0cf7] (ignored)
 		 SUBSYSTEM=acpi
 		 DEVICE=+acpi:PNP0A03:00
-		6,339,5140900;NET: Registered protocol family 10
-		30,340,5690716;udevd[80]: starting version 181
+		6,339,5140900,-;NET: Registered protocol family 10
+		30,340,5690716,-;udevd[80]: starting version 181
 
 		The DEVICE= key uniquely identifies devices the following way:
 		  b12:8        - block dev_t
@@ -87,4 +89,13 @@
 		  n8           - netdev ifindex
 		  +sound:card0 - subsystem:devname
 
+		The flags field carries '-' by default. A 'c' indicates a
+		fragment of a line. All following fragments are flagged with
+		'+'. Note, that these hints about continuation lines are not
+		neccessarily correct, and the stream could be interleaved with
+		unrelated messages, but merging the lines in the output
+		usually produces better human readable results. A similar
+		logic is used internally when messages are printed to the
+		console, /proc/kmsg or the syslog() syscall.
+
 Users:		dmesg(1), userspace kernel log consumers
diff --git a/kernel/printk.c b/kernel/printk.c
index 6c3d5bf..a41106e 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -361,6 +361,7 @@
 struct devkmsg_user {
 	u64 seq;
 	u32 idx;
+	enum log_flags prev;
 	struct mutex lock;
 	char buf[8192];
 };
@@ -426,6 +427,7 @@
 	struct log *msg;
 	u64 ts_usec;
 	size_t i;
+	char cont = '-';
 	size_t len;
 	ssize_t ret;
 
@@ -463,8 +465,25 @@
 	msg = log_from_idx(user->idx);
 	ts_usec = msg->ts_nsec;
 	do_div(ts_usec, 1000);
-	len = sprintf(user->buf, "%u,%llu,%llu;",
-		      (msg->facility << 3) | msg->level, user->seq, ts_usec);
+
+	/*
+	 * If we couldn't merge continuation line fragments during the print,
+	 * export the stored flags to allow an optional external merge of the
+	 * records. Merging the records isn't always neccessarily correct, like
+	 * when we hit a race during printing. In most cases though, it produces
+	 * better readable output. 'c' in the record flags mark the first
+	 * fragment of a line, '+' the following.
+	 */
+	if (msg->flags & LOG_CONT && !(user->prev & LOG_CONT))
+		cont = 'c';
+	else if ((msg->flags & LOG_CONT) ||
+		 ((user->prev & LOG_CONT) && !(msg->flags & LOG_PREFIX)))
+		cont = '+';
+
+	len = sprintf(user->buf, "%u,%llu,%llu,%c;",
+		      (msg->facility << 3) | msg->level,
+		      user->seq, ts_usec, cont);
+	user->prev = msg->flags;
 
 	/* escape non-printable characters */
 	for (i = 0; i < msg->text_len; i++) {