From 32e6a41f33e5576716b351bd473a27939fe94fa1 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 15 Dec 2008 15:22:34 +1100 Subject: Initial support for multiple UIs Move the device discovery code from separate udev helpers to a single process to listen on two sockets: one SOCK_DGRAM for incoming udev events, and one SOCK_STREAM for UIs to connect. Initial support for client/server infrastructure, still need to wire-up the udev messages. Signed-off-by: Jeremy Kerr --- discover/kboot-parser.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 discover/kboot-parser.c (limited to 'discover/kboot-parser.c') diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c new file mode 100644 index 0000000..df2e762 --- /dev/null +++ b/discover/kboot-parser.c @@ -0,0 +1,288 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "parser.h" +#include "params.h" + +#define buf_size 1024 + +static const char *devpath; + +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; +} + +struct global_option { + char *name; + char *value; +}; + + +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(const char *name, const char *value) +{ + int i; + + for (i = 0; global_options[i].name ;i++) { + if (!strcmp(name, global_options[i].name)) { + global_options[i].value = strdup(value); + return 1; + } + } + return 0; +} + +static char *get_global_option(const char *name) +{ + int i; + + for (i = 0; global_options[i].name ;i++) + if (!strcmp(name, global_options[i].name)) + return global_options[i].value; + + return NULL; +} + +static int parse_option(struct boot_option *opt, char *config) +{ + char *pos, *name, *value, *root, *initrd, *cmdline, *tmp; + + root = initrd = cmdline = NULL; + + /* remove quotes around the value */ + while (*config == '"' || *config == '\'') + config++; + + pos = config + strlen(config) - 1; + while (*pos == '"' || *pos == '\'') + *(pos--) = 0; + + if (!strlen(pos)) + return 0; + + pos = strchr(config, ' '); + + /* if there's no space, it's only a kernel image with no params */ + if (!pos) { + opt->boot_image_file = resolve_path(config, devpath); + opt->description = strdup(config); + return 1; + } + + *pos = 0; + opt->boot_image_file = resolve_path(config, devpath); + + cmdline = malloc(buf_size); + *cmdline = 0; + + for (pos++; pos;) { + pos = get_param_pair(pos, &name, &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); + } + } + + if (!root) + root = get_global_option("root"); + if (!initrd) + initrd = get_global_option("initrd"); + + if (initrd) { + asprintf(&tmp, "initrd=%s %s", initrd, cmdline); + free(cmdline); + cmdline = tmp; + + opt->initrd_file = resolve_path(initrd, devpath); + } + + if (root) { + asprintf(&tmp, "root=%s %s", root, cmdline); + free(cmdline); + cmdline = tmp; + + } else if (initrd) { + /* if there's an initrd but no root, fake up /dev/ram0 */ + asprintf(&tmp, "root=/dev/ram0 %s", cmdline); + free(cmdline); + cmdline = tmp; + } + + pb_log("kboot cmdline: %s\n", cmdline); + opt->boot_args = cmdline; + + asprintf(&opt->description, "%s %s", + config, opt->boot_args); + + return 1; +} + +static void parse_buf(struct device *dev, char *buf) +{ + char *pos, *name, *value; + int sent_device = 0; + + for (pos = buf; pos;) { + struct boot_option opt; + + pos = get_param_pair(pos, &name, &value, '\n'); + + pb_log("kboot param: '%s' = '%s'\n", name, value); + + if (name == NULL || param_is_ignored(name)) + continue; + + if (*name == '#') + continue; + + if (check_for_global_option(name, value)) + continue; + + memset(&opt, 0, sizeof(opt)); + opt.name = strdup(name); + + if (parse_option(&opt, value)) + if (!sent_device++) + add_device(dev); + add_boot_option(&opt); + + free(opt.name); + } +} + +static int parse(const char *device) +{ + char *filepath, *buf; + int fd, len, rc = 0; + struct stat stat; + struct device *dev; + + devpath = device; + + filepath = resolve_path("/etc/kboot.conf", devpath); + + fd = open(filepath, O_RDONLY); + if (fd < 0) + goto out_free_path; + + if (fstat(fd, &stat)) + goto out_close; + + buf = malloc(stat.st_size + 1); + if (!buf) + goto out_close;; + + len = read(fd, buf, stat.st_size); + if (len < 0) + goto out_free_buf; + buf[len] = 0; + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = strdup(device); + dev->icon_file = strdup(generic_icon_file(guess_device_type())); + + parse_buf(dev, buf); + + rc = 1; + +out_free_buf: + free(buf); +out_close: + close(fd); +out_free_path: + free(filepath); + return rc; +} + +struct parser kboot_parser = { + .name = "kboot.conf parser", + .priority = 98, + .parse = parse +}; -- cgit v1.2.1