log-handler: Add a config option for log size.

Allow a configuration option to specify a log size. The parameter takes and
interprets common size suffixes k/M/G. 1kB = 1024B, 1MB=1024kB, 1GB=1024MB.
Default unit is byte. If "B" is specified at the end it will be ignored.

Tested:
Tested that "logsize = 567kB" correctly sets up a 567KB log buffer.
Tested that invalid/overflow values will be ignored and 16KB will be used.
Tested that if log size smaller than pagesize is rounded up.

Change-Id: I2fb50462c6ff7873130be80f7d57ef8065acc5da
Signed-off-by: Kun Yi <kunyi731@gmail.com>
diff --git a/config.c b/config.c
index b68cfcd..f5653dc 100644
--- a/config.c
+++ b/config.c
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <err.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -212,6 +214,64 @@
 	return -1;
 }
 
+int config_parse_logsize(const char *size_str, size_t *size)
+{
+	struct size_suffix_shift {
+		/* Left shiftwidth corresponding to the suffix. */
+		size_t	shiftwidth;
+		int	unit;
+	};
+
+	const struct size_suffix_shift suffixes[] = {
+		{ 10, 'k' },
+		{ 20, 'M' },
+		{ 30, 'G' },
+	};
+	const size_t num_suffixes = sizeof(suffixes) /
+				    sizeof(struct size_suffix_shift);
+	size_t logsize;
+	char *suffix;
+	size_t i;
+
+	if (!size_str)
+		return -1;
+
+	logsize = strtoul(size_str, &suffix, 0);
+	if (logsize == 0 || logsize >= UINT32_MAX || suffix == size_str)
+		return -1;
+
+	/* Ignore spaces between number and suffix */
+	while (*suffix && isspace(*suffix))
+		suffix++;
+
+	for (i = 0; i < num_suffixes; i++) {
+		if (*suffix == suffixes[i].unit) {
+			/*
+			 * If logsize overflows, probably something was wrong.
+			 * Return instead of clamping to an arbitrary value.
+			 */
+			if (logsize > (UINT32_MAX >> suffixes[i].shiftwidth))
+				return -1;
+
+			logsize <<= suffixes[i].shiftwidth;
+			suffix++;
+			break;
+		}
+	}
+
+	/* Allow suffix like 'kB' */
+	while (*suffix && (tolower(*suffix) == 'b' || isspace(*suffix)))
+		suffix++;
+
+	if (*suffix) {
+		warn("Invalid suffix!");
+		return -1;
+	}
+
+	*size = logsize;
+	return 0;
+}
+
 #ifdef CONFIG_TEST
 int main(void)
 {
diff --git a/console-server.h b/console-server.h
index 0be567e..d2ac87f 100644
--- a/console-server.h
+++ b/console-server.h
@@ -118,6 +118,7 @@
 void config_fini(struct config *config);
 
 int config_parse_baud(speed_t *speed, const char *baud_string);
+int config_parse_logsize(const char *size_str, size_t *size);
 
 /* socket paths */
 extern const char *console_socket_path;
diff --git a/log-handler.c b/log-handler.c
index 149806c..88ea27b 100644
--- a/log-handler.c
+++ b/log-handler.c
@@ -41,8 +41,7 @@
 
 
 static const char *default_filename = LOCALSTATEDIR "/log/obmc-console.log";
-
-static const size_t logsize = 16 * 1024;
+static const size_t default_logsize = 16 * 1024;
 
 static struct log_handler *to_log_handler(struct handler *handler)
 {
@@ -129,17 +128,26 @@
 }
 
 static int log_init(struct handler *handler, struct console *console,
-		struct config *config __attribute__((unused)))
+		struct config *config)
 {
 	struct log_handler *lh = to_log_handler(handler);
-	const char *filename;
+	const char *filename, *logsize_str;
+	size_t logsize;
 	int rc;
 
 	lh->console = console;
-	lh->maxsize = logsize;
 	lh->pagesize = 4096;
 	lh->size = 0;
 
+	logsize_str = config_get_value(config, "logsize");
+	rc = config_parse_logsize(logsize_str, &logsize);
+	if (logsize_str != NULL && rc) {
+		logsize = default_logsize;
+		warn("Invalid logsize. Default to %ukB",
+                     (unsigned int)(logsize >> 10));
+	}
+	lh->maxsize = logsize <= lh->pagesize ? lh->pagesize + 1 : logsize;
+
 	filename = config_get_value(config, "logfile");
 	if (!filename)
 		filename = default_filename;
diff --git a/obmc-console.conf.sample b/obmc-console.conf.sample
index 56d69fe..a4ff6ad 100644
--- a/obmc-console.conf.sample
+++ b/obmc-console.conf.sample
@@ -7,3 +7,9 @@
 
 # To mirror to a local tty device (typically a hardware UART), set local-tty
 # local-tty = ttyS0
+
+# Specify log size. Default is 16kB if unset. Takes the following suffixes:
+#   No suffix: byte
+#   "k": kilobyte = 1024B
+#   "M": megabytes = 1024kB
+# logsize = 128k