Add configuration infrastructure

Rather than expecting parameters on the command line, this change
implemnets a little configration infrastructure, to allow the core
code to load configuration from a file in ${sysconfdir}.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/Makefile.am b/Makefile.am
index 3d0a9b0..d7272a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,12 +5,16 @@
 
 AM_CFLAGS = -Wall -Wextra -Werror
 
-console_server_CPPFLAGS = -DLOCALSTATEDIR=\"$(localstatedir)\"
+EXTRA_DIST = openbmc-console.conf.sample
+
+console_server_CPPFLAGS = -DLOCALSTATEDIR=\"$(localstatedir)\" \
+			  -DSYSCONFDIR=\"$(sysconfdir)\"
 
 console_server_SOURCES = \
 	 console-server.c \
 	 console-server.h \
 	 util.c \
+	 config.c \
 	 log-handler.c \
 	 socket-handler.c
 
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..6665677
--- /dev/null
+++ b/config.c
@@ -0,0 +1,143 @@
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+static const char *config_default_filename = SYSCONFDIR "/openbmc-console.conf";
+
+struct config_item {
+	char			*name;
+	char			*value;
+	struct config_item	*next;
+};
+
+struct config {
+	struct config_item	*items;
+};
+
+const char *config_get_value(struct config *config, const char *name)
+{
+	struct config_item *item;
+
+	for (item = config->items; item; item = item->next)
+		if (!strcasecmp(item->name, name))
+			return item->value;
+
+	return NULL;
+}
+
+static void config_parse(struct config *config, char *buf)
+{
+	struct config_item *item;
+	char *name, *value;
+	char *p, *line;
+	int rc;
+
+	for (p = NULL, line = strtok_r(buf, "\n", &p); line;
+			line = strtok_r(NULL, "\n", &p)) {
+
+		/* trim leading space */
+		for (;*line == ' ' || *line == '\t'; line++)
+			;
+
+		/* skip comments */
+		if (*line == '#')
+			continue;
+
+		name = value = NULL;
+
+		rc = sscanf(line, "%m[^ =] = %ms ", &name, &value);
+		if (rc != 2 || !strlen(name) || !strlen(value)) {
+			free(name);
+			free(value);
+			continue;
+		}
+
+		/* create a new item and add to our list */
+		item = malloc(sizeof(*item));
+		item->name = name;
+		item->value = value;
+		item->next = config->items;
+		config->items = item;
+	}
+}
+
+struct config *config_init(const char *filename)
+{
+	struct config *config;
+	struct stat statbuf;
+	size_t len, i;
+	char *buf;
+	int fd, rc;
+
+	if (!filename)
+		filename = config_default_filename;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		warn("Can't open configuration file %s", filename);
+		return NULL;
+	}
+
+	rc = fstat(fd, &statbuf);
+	if (rc) {
+		warn("Can't stat %s", filename);
+		goto err_close;
+	}
+
+	len = statbuf.st_size;
+
+	buf = malloc(len + 1);
+	for (i = 0; i < len;) {
+		rc = read(fd, buf + i, len - i);
+		if (rc < 0) {
+			warn("Can't read configuration file %s", filename);
+			goto err_free;
+
+		} else if (!rc) {
+			break;
+		}
+		i += rc;
+
+	}
+	buf[len] = '\0';
+
+	close(fd);
+
+	config = malloc(sizeof(*config));
+	config->items = NULL;
+
+	config_parse(config, buf);
+
+	free(buf);
+
+	return config;
+
+err_free:
+	free(buf);
+err_close:
+	close(fd);
+	return NULL;
+}
+
+void config_fini(struct config *config)
+{
+	struct config_item *item, *next;
+
+	for (item = config->items; item; item = next) {
+		next = item->next;
+		free(item->name);
+		free(item->value);
+		free(item);
+	}
+
+	free(config);
+}
diff --git a/console-server.c b/console-server.c
index e0b4249..40e3a36 100644
--- a/console-server.c
+++ b/console-server.c
@@ -61,7 +61,7 @@
 "usage: %s [options]\n"
 "\n"
 "Options:\n"
-"  --device <TTY>  Use serial device TTY (eg, ttyS0)\n"
+"  --config <FILE>  Use FILE for configuration\n"
 "",
 		progname);
 }
@@ -176,6 +176,43 @@
 	return 0;
 }
 
+static int tty_init(struct console *console, struct config *config)
+{
+	const char *val;
+	char *endp;
+	int rc;
+
+	console->tty_kname = config_get_value(config, "device");
+
+	val = config_get_value(config, "lpc-address");
+	if (val) {
+		console->tty_lpc_addr = strtoul(val, &endp, 0);
+		if (endp == optarg) {
+			warn("Invalid LPC address: '%s'", val);
+			return -1;
+		}
+	}
+
+	val = config_get_value(config, "sirq");
+	if (val) {
+		console->tty_sirq = strtoul(val, &endp, 0);
+		if (endp == optarg)
+			warn("Invalid sirq: '%s'", val);
+	}
+
+	if (!console->tty_kname) {
+		warnx("Error: No TTY device specified");
+		return -1;
+	}
+
+	rc = tty_find_device(console);
+	if (rc)
+		return rc;
+
+	rc = tty_init_io(console);
+	return rc;
+}
+
 
 int console_data_out(struct console *console, const uint8_t *data, size_t len)
 {
@@ -419,73 +456,51 @@
 	return rc ? -1 : 0;
 }
 static const struct option options[] = {
-	{ "device",	required_argument,	0, 'd'},
-	{ "sirq",	required_argument,	0, 's'},
-	{ "lpc-addr",	required_argument,	0, 'l'},
+	{ "config",	required_argument,	0, 'c'},
 	{ },
 };
 
 int main(int argc, char **argv)
 {
+	const char *config_filename = NULL;
 	struct console *console;
-	int rc, i;
+	struct config *config;
+	int rc;
 
-	console = malloc(sizeof(struct console));
-	memset(console, 0, sizeof(*console));
 	rc = -1;
 
 	for (;;) {
-		char *endp;
 		int c, idx;
 
-		c = getopt_long(argc, argv, "d:s:l:", options, &idx);
+		c = getopt_long(argc, argv, "c:", options, &idx);
 		if (c == -1)
 			break;
 
 		switch (c) {
-		case 'd':
-			console->tty_kname = optarg;
+		case 'c':
+			config_filename = optarg;
 			break;
-		case 'l':
-			console->tty_lpc_addr = strtoul(optarg, &endp, 0);
-			if (endp == optarg) {
-				warnx("Invalid sirq: '%s'", optarg);
-				goto out_free;
-			}
-			break;
-
-		case 's':
-			console->tty_sirq = strtoul(optarg, &endp, 0);
-			if (endp == optarg) {
-				warnx("Invalid sirq: '%s'", optarg);
-				goto out_free;
-			}
-			break;
-
 		case 'h':
 		case '?':
 			usage(argv[0]);
-			rc = 0;
-			goto out_free;
+			return EXIT_SUCCESS;
 		}
 	}
 
+	console = malloc(sizeof(struct console));
+	memset(console, 0, sizeof(*console));
 	console->pollfds = calloc(n_internal_pollfds,
 			sizeof(*console->pollfds));
 
-	if (!console->tty_kname) {
-		fprintf(stderr,
-			"Error: No TTY device specified (use --device)\n");
-		return EXIT_FAILURE;
+	config = config_init(config_filename);
+	if (!config) {
+		warnx("Can't read configuration, exiting.");
+		goto out_free;
 	}
 
-	rc = tty_find_device(console);
+	rc = tty_init(console, config);
 	if (rc)
-		return EXIT_FAILURE;
-
-	rc = tty_init_io(console);
-	if (rc)
-		return EXIT_FAILURE;
+		goto out_config_fini;
 
 	handlers_init(console);
 
@@ -493,6 +508,9 @@
 
 	handlers_fini(console);
 
+out_config_fini:
+	config_fini(config);
+
 out_free:
 	free(console->pollers);
 	free(console->pollfds);
diff --git a/console-server.h b/console-server.h
index 292b54a..dcedada 100644
--- a/console-server.h
+++ b/console-server.h
@@ -48,6 +48,12 @@
 
 void console_unregister_poller(struct console *console, struct poller *poller);
 
+/* config API */
+struct config;
+const char *config_get_value(struct config *config, const char *name);
+struct config *config_init(const char *filename);
+void config_fini(struct config *config);
+
 /* socket paths */
 const char *console_socket_path;
 const size_t console_socket_path_len;
diff --git a/openbmc-console.conf.sample b/openbmc-console.conf.sample
new file mode 100644
index 0000000..a5b8808
--- /dev/null
+++ b/openbmc-console.conf.sample
@@ -0,0 +1,6 @@
+# Device to use for our UART console
+device = ttyUSB0
+
+# For VUART devices, we can specify the LPC address and SIRQ parameters
+lpc-address = 0x3f8
+sirq = 4