Patchwork [10/18] petitboot: Split common routines from kboot parser

login
register
mail settings
Submitter Geoff Levand
Date March 25, 2009, 10:35 p.m.
Message ID <20090325223547.488912083@am.sony.com>>
Download mbox | patch
Permalink /patch/25115/
State Accepted
Headers show

Comments

Geoff Levand - March 25, 2009, 10:35 p.m.
Pull out the common .conf file parsing logic from kboot-parser.c and
into two new files parser-conf.h and parser-conf.c, and rework
the kboot parser to use those common routines.

The new common routines are based on a .conf file parser context
struct conf_context.  The specific parsers setup the context then
call the main parsing entry routine conf_parse().  conf_parse()
uses the context info to open and read .conf files and call
parser specific callbacks to process name:value pairs and to
add boot_option instances to the discover server.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
---
 discover/kboot-parser.c |  370 ++++++++++++------------------------------------
 discover/parser-conf.c  |  277 +++++++++++++++++++++++++++++++++++
 discover/parser-conf.h  |   54 +++++++
 discover/parser-utils.h |    1 
 rules.mk                |    2 
 5 files changed, 433 insertions(+), 271 deletions(-)

Patch

--- a/discover/kboot-parser.c
+++ b/discover/kboot-parser.c
@@ -1,319 +1,149 @@ 
 #define _GNU_SOURCE
 
-#include <errno.h>
+#include <assert.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <talloc/talloc.h>
-#include <log/log.h>
 
+#include "log/log.h"
+#include "talloc/talloc.h"
 #include "pb-protocol/pb-protocol.h"
-#include "paths.h"
-#include "params.h"
+#include "parser-conf.h"
 #include "parser-utils.h"
-#include "device-handler.h"
-
-#define buf_size 1024
-
-struct kboot_context {
-	struct discover_context *discover;
-
-	char *buf;
-
-	struct global_option {
-		char *name;
-		char *value;
-	} *global_options;
-	int n_global_options;
-};
-
-static int param_is_ignored(const char *param)
-{
-	static const char *ignored_options[] =
-		{ "message", "timeout", "default", NULL };
-	const char **str;
-
-	for (str = ignored_options; *str; str++)
-		if (streq(*str, param))
-			return 1;
-	return 0;
-}
-
-/**
- * Splits a name=value pair, with value terminated by @term (or nul). if there
- * is no '=', then only the value is populated, and *name is set to NULL. The
- * string is modified in place.
- *
- * Returns the next byte to process, or null if we've hit the end of the
- * string.
- *
- * */
-static char *get_param_pair(char *str, char **name_out, char **value_out,
-		char terminator)
-{
-	char *sep, *tmp, *name, *value;
-
-	/* terminate the value */
-	tmp = strchr(str, terminator);
-	if (tmp)
-		*tmp = 0;
-	else
-		tmp = NULL;
-
-	sep = strchr(str, '=');
-	if (!sep) {
-		*name_out = NULL;
-		*value_out = str;
-		return tmp ? tmp + 1 : NULL;
-	}
-
-	/* terminate the name */
-	*sep = 0;
-
-	/* remove leading spaces */
-	for (name = str; isspace(*name); name++);
-	for (value = sep + 1; isspace(*value); value++);
-
-	/* .. and trailing ones.. */
-	for (sep--; isspace(*sep); sep--)
-		*sep = 0;
-	for (sep = value + strlen(value) - 1; isspace(*sep); sep--)
-		*sep = 0;
-
-	*name_out = name;
-	*value_out = value;
-
-	return tmp ? tmp + 1 : NULL;
-}
-
-static struct global_option global_options[] = {
-	{ .name = "root" },
-	{ .name = "initrd" },
-	{ .name = "video" },
-	{ .name = NULL }
-};
-
-/*
- * Check if an option (name=value) is a global option. If so, store it in
- * the global options table, and return 1. Otherwise, return 0.
- */
-static int check_for_global_option(struct kboot_context *ctx,
-		const char *name, const char *value)
-{
-	int i;
-
-	for (i = 0; i < ctx->n_global_options; i++) {
-		if (!strcmp(name, ctx->global_options[i].name)) {
-			global_options[i].value = strdup(value);
-			break;
-		}
-	}
-	return 0;
-}
+#include "paths.h"
 
-static char *get_global_option(
-		struct kboot_context *ctx __attribute__((unused)),
-		const char *name)
+static void kboot_process_pair(struct conf_context *conf, const char *name,
+		char *value)
 {
-	int i;
+	const char *const *ignored_names = conf->parser_info;
+	char *pos;
+	char *args;
+	const char *initrd;
+	const char *root;
+	struct boot_option *opt;
 
-	for (i = 0; global_options[i].name ;i++)
-		if (!strcmp(name, global_options[i].name))
-			return global_options[i].value;
+	if (conf_param_in_list(ignored_names, name))
+		return;
 
-	return NULL;
-}
-
-static int parse_option(struct kboot_context *kboot_ctx, char *opt_name,
-		char *config)
-{
-	char *pos, *name, *value, *root, *initrd, *cmdline, *tmp;
-	struct boot_option *opt;
+	if (conf_set_global_option(conf, name, value))
+		return;
 
-	root = initrd = cmdline = NULL;
+	/* opt must be associated with dc */
 
-	/* remove quotes around the value */
-	while (*config == '"' || *config == '\'')
-		config++;
+	opt = talloc_zero(conf->dc->device, struct boot_option);
 
-	pos = config + strlen(config) - 1;
-	while (*pos == '"' || *pos == '\'')
-		*(pos--) = 0;
+	if (!opt)
+		return;
 
-	if (!strlen(pos))
-		return 0;
+	opt->id = talloc_asprintf(opt, "%s#%s", conf->dc->device->id, name);
+	opt->name = talloc_strdup(opt, name);
 
-	pos = strchr(config, ' ');
+	args = talloc_strdup(opt, "");
+	initrd = conf_get_global_option(conf, "initrd");
+	root = conf_get_global_option(conf, "root");
 
-	opt = talloc_zero(kboot_ctx, struct boot_option);
-	opt->id = talloc_asprintf(opt, "%s#%s",
-			kboot_ctx->discover->device->id, opt_name);
-	opt->name = talloc_strdup(opt, opt_name);
+	pos = strchr(value, ' ');
 
 	/* if there's no space, it's only a kernel image with no params */
-	if (!pos) {
-		opt->boot_image_file = resolve_path(opt, config,
-				kboot_ctx->discover->device_path);
-		opt->description = talloc_strdup(opt, config);
-		goto out_add;
-	}
 
+	if (!pos)
+		goto out_add;
 	*pos = 0;
-	opt->boot_image_file = resolve_path(opt, config,
-			kboot_ctx->discover->device_path);
-
-	cmdline = talloc_array(opt, char, buf_size);
-	*cmdline = 0;
 
 	for (pos++; pos;) {
-		pos = get_param_pair(pos, &name, &value, ' ');
+		char *cl_name, *cl_value;
 
-		if (!name) {
-			strcat(cmdline, " ");
-			strcat(cmdline, value);
-
-		} else if (streq(name, "initrd")) {
-			initrd = value;
-
-		} else if (streq(name, "root")) {
-			root = value;
-
-		} else {
-			strcat(cmdline, " ");
-			*(value - 1) = '=';
-			strcat(cmdline, name);
+		pos = conf_get_param_pair(pos, &cl_name, &cl_value, ' ');
+
+		if (!cl_name) {
+			args = talloc_asprintf_append(args, "%s ", cl_value);
+			continue;
 		}
-	}
 
-	if (!root)
-		root = get_global_option(kboot_ctx, "root");
-	if (!initrd)
-		initrd = get_global_option(kboot_ctx, "initrd");
+		if (streq(cl_name, "initrd")) {
+			initrd = cl_value;
+			continue;
+		}
 
-	if (initrd) {
-		tmp = talloc_asprintf(opt, "initrd=%s %s", initrd, cmdline);
-		talloc_free(cmdline);
-		cmdline = tmp;
+		if (streq(cl_name, "root")) {
+			root = cl_value;
+			continue;
+		}
 
-		opt->initrd_file = resolve_path(opt, initrd,
-				kboot_ctx->discover->device_path);
+		args = talloc_asprintf_append(args, "%s=%s ", cl_name,
+			cl_value);
 	}
 
-	if (root) {
-		tmp = talloc_asprintf(opt, "root=%s %s", root, cmdline);
-		talloc_free(cmdline);
-		cmdline = tmp;
-
-	} else if (initrd) {
-		/* if there's an initrd but no root, fake up /dev/ram0 */
-		tmp = talloc_asprintf(opt, "root=/dev/ram0 %s", cmdline);
-		talloc_free(cmdline);
-		cmdline = tmp;
-	}
-
-	opt->boot_args = cmdline;
-
-	opt->description = talloc_asprintf(opt, "%s %s",
-			config, opt->boot_args);
-
 out_add:
-	device_add_boot_option(kboot_ctx->discover->device, opt);
-	return 1;
-}
+	opt->boot_image_file = resolve_path(opt, value, conf->dc->device_path);
 
-static void parse_buf(struct kboot_context *kboot_ctx)
-{
-	char *pos, *name, *value;
+	if (root) {
+		opt->boot_args = talloc_asprintf(opt, "root=%s %s", root, args);
+		talloc_free(args);
+	} else
+		opt->boot_args = args;
 
-	for (pos = kboot_ctx->buf; pos;) {
-		pos = get_param_pair(pos, &name, &value, '\n');
+	if (initrd) {
+		opt->initrd_file = resolve_path(opt, initrd,
+				conf->dc->device_path);
 
-		if (name == NULL || param_is_ignored(name))
-			continue;
+		opt->description = talloc_asprintf(opt, "%s initrd=%s %s",
+			value, initrd, opt->boot_args);
+	} else
+		opt->description = talloc_asprintf(opt, "%s %s", value,
+			opt->boot_args);
 
-		if (*name == '#')
-			continue;
+	conf_strip_str(opt->boot_args);
+	conf_strip_str(opt->description);
 
-		if (check_for_global_option(kboot_ctx, name, value))
-			continue;
-
-		parse_option(kboot_ctx, name, value);
-	}
+	device_add_boot_option(conf->dc->device, opt);
 }
 
+static struct conf_global_option kboot_global_options[] = {
+	{ .name = "initrd" },
+	{ .name = "root" },
+	{ .name = "video" },
+	{ .name = NULL }
+};
 
-static int kboot_parse(struct discover_context *ctx)
-{
-	static const char *const conf_names[] = {
-		"/etc/kboot.conf",
-		"/etc/kboot.cnf",
-	};
-	struct kboot_context *kboot_ctx;
-	int fd, len, rc;
-	unsigned int i;
-	struct stat stat;
-
-	rc = 0;
-	fd = -1;
-
-	kboot_ctx = talloc_zero(ctx, struct kboot_context);
-	kboot_ctx->discover = ctx;
-
-	for (i = 0, len = 0; i < sizeof(conf_names) / sizeof(conf_names[0]);
-		i++) {
-		char *filepath = resolve_path(kboot_ctx, conf_names[i],
-			ctx->device_path);
-
-		pb_log("%s: try: %s\n", __func__, filepath);
-
-		fd = open(filepath, O_RDONLY);
-		if (fd < 0) {
-			pb_log("%s: open failed: %s : %s\n", __func__, filepath,
-				strerror(errno));
-			continue;
-		}
-		if (fstat(fd, &stat)) {
-			pb_log("%s: fstat failed: %s : %s\n", __func__,
-				filepath, strerror(errno));
-			continue;
-		}
-
-		kboot_ctx->buf = talloc_array(kboot_ctx, char,
-			stat.st_size + 1);
+static const char *const kboot_conf_files[] = {
+	"/kboot.conf",
+	"/kboot.cnf",
+	"/etc/kboot.conf",
+	"/etc/kboot.cnf",
+	"/KBOOT.CONF",
+	"/KBOOT.CNF",
+	"/ETC/KBOOT.CONF",
+	"/ETC/KBOOT.CNF",
+	NULL
+};
 
-		len = read(fd, kboot_ctx->buf, stat.st_size);
-		if (len < 0) {
-			pb_log("%s: read failed: %s : %s\n", __func__, filepath,
-				strerror(errno));
-			continue;
-		}
-		kboot_ctx->buf[len] = 0;
-	}
+static const char *const kboot_ignored_names[] = {
+	"default",
+	"delay",
+	"message",
+	"timeout",
+	NULL
+};
 
-	if (len <= 0)
-		goto out;
+static int kboot_parse(struct discover_context *dc)
+{
+	struct conf_context *conf;
+	int rc;
 
-	if (!ctx->device->icon_file)
-		ctx->device->icon_file = talloc_strdup(ctx,
-				generic_icon_file(guess_device_type(ctx)));
+	conf = talloc_zero(dc, struct conf_context);
 
-	parse_buf(kboot_ctx);
+	if (!conf)
+		return -1;
 
-	rc = 1;
+	conf->dc = dc;
+	conf->global_options = kboot_global_options,
+	conf->conf_files = kboot_conf_files,
+	conf->process_pair = kboot_process_pair;
+	conf->parser_info = (void *)kboot_ignored_names,
 
-out:
-	pb_log("%s: %s\n", __func__, (rc ? "ok" : "failed"));
+	rc = conf_parse(conf);
 
-	if (fd >= 0)
-		close(fd);
-	talloc_free(kboot_ctx);
+	talloc_free(conf);
 	return rc;
 }
 
--- /dev/null
+++ b/discover/parser-conf.c
@@ -0,0 +1,277 @@ 
+/*
+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
+ *  Copyright 2009 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "log/log.h"
+#include "talloc/talloc.h"
+#include "parser-conf.h"
+#include "parser-utils.h"
+#include "paths.h"
+
+/**
+ * conf_strip_str - Remove quotes and/or whitespace around a string.
+ *
+ * Returns the next byte to process, or NULL if the string is empty.
+ */
+
+char *conf_strip_str(char *s)
+{
+	char *e;
+
+	if (!s)
+		return NULL;
+
+	while (*s == '"' || *s == '\'' || isspace(*s))
+		s++;
+
+	e = s + strlen(s) - 1;
+
+	while (*e == '"' || *e == '\'' || isspace(*e))
+		*(e--) = 0;
+
+	return strlen(s) ? s : NULL;
+}
+
+/**
+ * conf_get_param_pair - Get the next 'name=value' parameter pair.
+ * @str: The string to process.
+ * @name_out: Returns a pointer to the name.
+ * @value_out: Returns a pointer to the value.
+ * @terminator: The pair separator/terminator.
+ *
+ * Parses a name=value pair returning pointers in @name_out and @value_out.
+ * The pair can be terminated by @terminator or a zero.
+ * If no '=' character is found @value_out is set and @name_out is
+ * set to NULL.
+ * If the value is empty *value_out is set to NULL.
+ * The string is modified in place.
+ *
+ * Returns the next byte to process, or NULL if we've hit the end of the
+ * string.
+ */
+
+char *conf_get_param_pair(char *str, char **name_out, char **value_out,
+		char terminator)
+{
+	char *sep, *end;
+
+	/* terminate the value */
+	end = strchr(str, terminator);
+
+	if (end)
+		*end = 0;
+
+	sep = strchr(str, '=');
+
+	if (!sep) {
+		*name_out = NULL;
+		*value_out = conf_strip_str(str);
+	} else {
+		*sep = 0;
+		*name_out = conf_strip_str(str);
+		*value_out = conf_strip_str(sep + 1);
+	}
+
+	pb_log("%s: @%s@%s@\n", __func__, *name_out, *value_out);
+
+	return end ? end + 1 : NULL;
+}
+
+/**
+ * conf_param_in_list - Search a list of strings for an entry.
+ * @list: A NULL treminated array of pointers to strings.
+ * @param: A string to search for.
+ *
+ * Retuns 1 if @param is found in @list, 0 if @param is not found.
+ */
+
+int conf_param_in_list(const char *const *list, const char *param)
+{
+	const char *const *str;
+
+	for (str = list; *str; str++)
+		if (streq(*str, param))
+			return 1;
+	return 0;
+}
+
+/**
+ * conf_set_global_option - Set a value in the global option table.
+ *
+ * Check if an option (name=value) is a global option. If so, store it in
+ * the global options table, and return 1. Otherwise, return 0.
+ */
+
+int conf_set_global_option(struct conf_context *conf, const char *name,
+	const char *value)
+{
+	int i;
+
+	for (i = 0; conf->global_options[i].name; i++) {
+		if (streq(name, conf->global_options[i].name)) {
+			conf->global_options[i].value
+				= talloc_strdup(conf, value);
+			pb_log("%s: %s:%s\n", __func__, name, value);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * conf_get_global_option - Get a value from the global option table.
+ * @conf: The parser struct conf_context.
+ * @name: The name of the (name:value) to retrieve.
+ *
+ * Returns the value if @name is found in the table, or NULL if @name
+ * is not found.
+ */
+
+const char *conf_get_global_option(struct conf_context *conf,
+	const char *name)
+{
+	int i;
+
+	for (i = 0; conf->global_options[i].name ;i++)
+		if (streq(name, conf->global_options[i].name))
+			return conf->global_options[i].value;
+
+	assert(0 && "unknown global name");
+	return NULL;
+}
+
+/**
+ * conf_parse_buf - The main parser loop.
+ *
+ * Called from conf_parse() with data read from a conf file.
+ */
+
+static void conf_parse_buf(struct conf_context *conf)
+{
+	char *pos, *name, *value;
+
+	for (pos = conf->buf; pos;) {
+		pos = conf_get_param_pair(pos, &name, &value, '\n');
+
+		if (!value)
+			continue;
+
+		if (name && *name == '#')
+			continue;
+
+		if (*value == '#')
+			continue;
+
+		value = conf_strip_str(value);
+
+		if (!value)
+			continue;
+
+		conf->process_pair(conf, name, value);
+	}
+
+	if (conf->finish)
+		conf->finish(conf);
+}
+
+/**
+ * conf_parse - The common parser entry.
+ *
+ * Called from the parser specific setup routines.  Searches for .conf
+ * files, reads data into buffers, and calls conf_parse_buf().
+ */
+
+int conf_parse(struct conf_context *conf)
+{
+	int fd, rc;
+	unsigned int i;
+	struct stat stat;
+	ssize_t len;
+
+	rc = 0;
+	fd = -1;
+	len = 0;
+
+	/* The parser is only run on the first file found. */
+	/* FIXME: Could try others on error, etc. */
+
+	for (i = 0; conf->conf_files[i]; i++) {
+		char *filepath = resolve_path(conf->dc,
+			conf->conf_files[i], conf->dc->device_path);
+
+		pb_log("%s: try: %s\n", __func__, filepath);
+
+		fd = open(filepath, O_RDONLY);
+
+		talloc_free(filepath);
+
+		if (fd < 0) {
+			pb_log("%s: open failed: %s\n", __func__,
+				strerror(errno));
+			continue;
+		}
+
+		if (fstat(fd, &stat)) {
+			pb_log("%s: fstat failed: %s\n", __func__,
+				strerror(errno));
+			continue;
+		}
+
+		conf->buf = talloc_array(conf, char, stat.st_size + 1);
+
+		len = read(fd, conf->buf, stat.st_size);
+
+		if (len < 0) {
+			pb_log("%s: read failed: %s\n", __func__,
+				strerror(errno));
+			continue;
+		}
+		conf->buf[len] = 0;
+
+		break;
+	}
+
+	if (fd >= 0)
+		close(fd);
+
+	if (len <= 0)
+		goto out;
+
+	if (!conf->dc->device->icon_file)
+		conf->dc->device->icon_file = talloc_strdup(conf->dc,
+			generic_icon_file(guess_device_type(conf->dc)));
+
+	conf_parse_buf(conf);
+
+	rc = 1;
+
+out:
+	pb_log("%s: %s\n", __func__, (rc ? "ok" : "failed"));
+	return rc;
+}
+
--- /dev/null
+++ b/discover/parser-conf.h
@@ -0,0 +1,54 @@ 
+/*
+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
+ *  Copyright 2009 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if !defined(_PB_DISCOVER_CONFIG_H)
+#define _PB_DISCOVER_CONFIG_H
+
+#include "device-handler.h"
+
+struct conf_global_option {
+	const char *name;
+	char *value;
+};
+
+struct conf_context {
+	void *parser_info;
+	struct discover_context *dc;
+	char *buf;
+	struct conf_global_option *global_options;
+	const char *const *conf_files;
+
+	void (*process_pair)(struct conf_context *conf, const char *name,
+		char *value);
+	void (*finish)(struct conf_context *conf);
+};
+
+int conf_parse(struct conf_context *conf);
+char *conf_get_param_pair(char *str, char **name_out, char **value_out,
+		char terminator);
+const char *conf_get_global_option(struct conf_context *conf,
+	const char *name);
+int conf_set_global_option(struct conf_context *conf, const char *name,
+	const char *value);
+
+/* utility routines */
+
+int conf_param_in_list(const char *const *list, const char *param);
+char *conf_strip_str(char *s);
+
+#endif
--- a/discover/parser-utils.h
+++ b/discover/parser-utils.h
@@ -1,6 +1,7 @@ 
 #ifndef PARSER_UTILS_H
 #define PARSER_UTILS_H
 
+#include "pb-protocol/pb-protocol.h"
 #include "parser.h"
 
 #define streq(a,b) (!strcasecmp((a),(b)))
--- a/rules.mk
+++ b/rules.mk
@@ -17,7 +17,6 @@  parser_test = test/parser-test
 
 # install targets and components
 daemons = $(pb_discover)
-#parsers = kboot native yaboot (todo)
 parsers = kboot
 uis = $(pb_test)
 tests = $(parser_test)
@@ -39,6 +38,7 @@  waiter_objs = lib/waiter/waiter.o
 
 # daemon objs
 parser_objs = discover/parser.o discover/parser-utils.o \
+	discover/parser-conf.o\
 	$(foreach p, $(parsers), discover/$(p)-parser.o)
 discover_objs = discover/udev.o discover/discover-server.o \
 	discover/device-handler.o discover/paths.o