summaryrefslogtreecommitdiffstats
path: root/discover
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2008-12-15 15:22:34 +1100
committerJeremy Kerr <jk@ozlabs.org>2008-12-15 15:22:34 +1100
commit32e6a41f33e5576716b351bd473a27939fe94fa1 (patch)
tree0d6b75ac0a02d2496416095405cb9498777c3beb /discover
parent000a92b4fa909c432732ac3ed8f28eeeaeac70ee (diff)
downloadtalos-petitboot-32e6a41f33e5576716b351bd473a27939fe94fa1.tar.gz
talos-petitboot-32e6a41f33e5576716b351bd473a27939fe94fa1.zip
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 <jk@ozlabs.org>
Diffstat (limited to 'discover')
-rw-r--r--discover/discover-server.c227
-rw-r--r--discover/discover-server.h10
-rw-r--r--discover/kboot-parser.c288
-rw-r--r--discover/log.c24
-rw-r--r--discover/log.h6
-rw-r--r--discover/message.h31
-rw-r--r--discover/native-parser.c124
-rw-r--r--discover/params.c595
-rw-r--r--discover/params.h6
-rw-r--r--discover/parser.c85
-rw-r--r--discover/parser.h46
-rw-r--r--discover/paths.c141
-rw-r--r--discover/paths.h53
-rw-r--r--discover/pb-discover.c34
-rw-r--r--discover/pb-discover.h6
-rw-r--r--discover/udev.c208
-rw-r--r--discover/udev.h26
-rw-r--r--discover/waiter.c83
-rw-r--r--discover/waiter.h23
-rw-r--r--discover/yaboot-cfg.c491
-rw-r--r--discover/yaboot-cfg.h30
-rw-r--r--discover/yaboot-parser.c235
22 files changed, 2772 insertions, 0 deletions
diff --git a/discover/discover-server.c b/discover/discover-server.c
new file mode 100644
index 0000000..8358f06
--- /dev/null
+++ b/discover/discover-server.c
@@ -0,0 +1,227 @@
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <asm/byteorder.h>
+
+#include <talloc/talloc.h>
+
+#include "ui/common/device.h"
+#include "pb-protocol/pb-protocol.h"
+#include "list/list.h"
+
+#include "log.h"
+#include "waiter.h"
+
+struct discover_server {
+ int socket;
+ struct waiter *waiter;
+ struct list clients;
+};
+
+struct client {
+ struct list_item list;
+ int fd;
+};
+
+
+static int server_destructor(void *arg)
+{
+ struct discover_server *server = arg;
+
+ if (server->waiter)
+ waiter_unregister(server->waiter);
+
+ if (server->socket >= 0)
+ close(server->socket);
+
+ return 0;
+}
+
+static int client_destructor(void *arg)
+{
+ struct client *client = arg;
+
+ if (client->fd >= 0)
+ close(client->fd);
+
+ list_remove(&client->list);
+
+ return 0;
+
+}
+
+static void print_clients(struct discover_server *server)
+ __attribute__((unused));
+
+static void print_clients(struct discover_server *server)
+{
+ struct client *client;
+
+ printf("current clients [%p,%p,%p]:\n",
+ &server->clients.head,
+ server->clients.head.prev,
+ server->clients.head.next);
+ list_for_each_entry(&server->clients, client, list)
+ printf("\t[%p,%p,%p] client: %d\n", &client->list,
+ client->list.prev, client->list.next,
+ client->fd);
+}
+
+static struct boot_option options[] = {
+ {
+ .id = "1.1",
+ .name = "meep one",
+ .description = "meep description one",
+ .icon_file = "meep.one.png",
+ .boot_args = "root=/dev/sda1",
+ },
+};
+
+static struct device device = {
+ .id = "1",
+ .name = "meep",
+ .description = "meep description",
+ .icon_file = "meep.png",
+ .n_options = 1,
+ .options = options,
+};
+
+static int client_write_message(struct discover_server *server,
+ struct client *client, struct pb_protocol_message *message)
+{
+ int rc;
+
+ rc = pb_protocol_write_message(client->fd, message);
+ if (rc)
+ talloc_free(client);
+
+ return rc;
+}
+
+static int write_add_message(struct discover_server *server,
+ struct client *client, struct device *dev)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = pb_protocol_device_len(dev);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_ADD, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_device(dev, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
+static int write_remove_message(struct discover_server *server,
+ struct client *client, char *dev_id)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = strlen(dev_id) + sizeof(uint32_t);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_REMOVE, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_string(message->payload, dev_id);
+
+ return client_write_message(server, client, message);
+}
+
+static int discover_server_process(void *arg)
+{
+ struct discover_server *server = arg;
+ struct client *client;
+ int fd;
+
+
+ len = sizeof(addr);
+
+ /* accept the incoming connection */
+ fd = accept(server->socket, NULL, 0);
+ if (!fd) {
+ pb_log("accept: %s\n", strerror(errno));
+ return 0;
+ }
+
+ /* add to our list of clients */
+ client = talloc(server, struct client);
+ list_add(&server->clients, &client->list);
+
+ talloc_set_destructor(client, client_destructor);
+
+ client->fd = fd;
+
+ /* send existing devices to client */
+ write_add_message(server, client, &device);
+
+ sleep(2);
+
+ write_remove_message(server, client, "1");
+
+ return 0;
+}
+
+struct discover_server *discover_server_init(void)
+{
+ struct discover_server *server;
+ struct sockaddr_un addr;
+
+ server = talloc(NULL, struct discover_server);
+ if (!server)
+ return NULL;
+
+ server->waiter = NULL;
+ list_init(&server->clients);
+
+ unlink(PB_SOCKET_PATH);
+
+ server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (server->socket < 0) {
+ pb_log("error creating server socket: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ talloc_set_destructor(server, server_destructor);
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, PB_SOCKET_PATH);
+
+ if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
+ pb_log("error binding server socket: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ if (listen(server->socket, 8)) {
+ pb_log("server socket listen: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ server->waiter = waiter_register(server->socket, WAIT_IN,
+ discover_server_process, server);
+
+ return server;
+
+out_err:
+ talloc_free(server);
+ return NULL;
+}
+
+void discover_server_destroy(struct discover_server *server)
+{
+ talloc_free(server);
+}
+
diff --git a/discover/discover-server.h b/discover/discover-server.h
new file mode 100644
index 0000000..ff27f15
--- /dev/null
+++ b/discover/discover-server.h
@@ -0,0 +1,10 @@
+#ifndef _DISCOVER_SERVER_H
+#define _DISCOVER_SERVER_H
+
+struct discover_server;
+
+struct discover_server *discover_server_init(void);
+
+void discover_server_destroy(struct discover_server *server);
+
+#endif /* _DISCOVER_SERVER_H */
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 <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 "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
+};
diff --git a/discover/log.c b/discover/log.c
new file mode 100644
index 0000000..189f31e
--- /dev/null
+++ b/discover/log.c
@@ -0,0 +1,24 @@
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "log.h"
+
+static FILE *logf;
+
+void pb_log(const char *fmt, ...)
+{
+ va_list ap;
+ FILE *stream;
+
+ stream = logf ? logf : stdout;
+
+ va_start(ap, fmt);
+ vfprintf(stream, fmt, ap);
+ va_end(ap);
+}
+
+void pb_log_set_stream(FILE *stream)
+{
+ logf = stream;
+}
diff --git a/discover/log.h b/discover/log.h
new file mode 100644
index 0000000..7f9b01f
--- /dev/null
+++ b/discover/log.h
@@ -0,0 +1,6 @@
+#ifndef _LOG_H
+#define _LOG_H
+
+void pb_log(const char *fmt, ...);
+
+#endif /* _LOG_H */
diff --git a/discover/message.h b/discover/message.h
new file mode 100644
index 0000000..d0b0e34
--- /dev/null
+++ b/discover/message.h
@@ -0,0 +1,31 @@
+
+#ifndef _MESSAGE_H
+#define _MESSAGE_H
+
+enum device_action {
+ DEV_ACTION_ADD_DEVICE = 0,
+ DEV_ACTION_ADD_OPTION = 1,
+ DEV_ACTION_REMOVE_DEVICE = 2,
+ DEV_ACTION_REMOVE_OPTION = 3
+};
+
+struct device {
+ char *id;
+ char *name;
+ char *description;
+ char *icon_file;
+
+ struct boot_option {
+ char *id;
+ char *name;
+ char *description;
+ char *icon_file;
+ char *boot_image_file;
+ char *initrd_file;
+ char *boot_args;
+ } *options;
+ int n_options;
+};
+
+
+#endif /* _MESSAGE_H */
diff --git a/discover/native-parser.c b/discover/native-parser.c
new file mode 100644
index 0000000..24713b1
--- /dev/null
+++ b/discover/native-parser.c
@@ -0,0 +1,124 @@
+
+#include "parser.h"
+#include "params.h"
+#include "paths.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+const char *conf_filename = "/boot/petitboot.conf";
+
+static struct boot_option *cur_opt;
+static struct device *dev;
+static const char *devpath;
+int device_added;
+
+int check_and_add_device(struct device *dev)
+{
+ if (!dev->icon_file)
+ dev->icon_file = strdup(generic_icon_file(guess_device_type()));
+
+ return !add_device(dev);
+}
+
+static int section(char *section_name)
+{
+ if (!device_added++ && !check_and_add_device(dev))
+ return 0;
+
+ if (cur_opt) {
+ add_boot_option(cur_opt);
+ free_boot_option(cur_opt);
+ }
+
+ cur_opt = malloc(sizeof(*cur_opt));
+ memset(cur_opt, 0, sizeof(*cur_opt));
+ return 1;
+}
+
+
+static void set_boot_option_parameter(struct boot_option *opt,
+ const char *name, const char *value)
+{
+ if (streq(name, "name"))
+ opt->name = strdup(value);
+
+ else if (streq(name, "description"))
+ opt->description = strdup(value);
+
+ else if (streq(name, "image"))
+ opt->boot_image_file = resolve_path(value, devpath);
+
+ else if (streq(name, "icon"))
+ opt->icon_file = resolve_path(value, devpath);
+
+ else if (streq(name, "initrd"))
+ opt->initrd_file =resolve_path(value, devpath);
+
+ else if (streq(name, "args"))
+ opt->boot_args = strdup(value);
+
+ else
+ fprintf(stderr, "Unknown parameter %s\n", name);
+}
+
+static void set_device_parameter(struct device *dev,
+ const char *name, const char *value)
+{
+ if (streq(name, "name"))
+ dev->name = strdup(value);
+
+ else if (streq(name, "description"))
+ dev->description = strdup(value);
+
+ else if (streq(name, "icon"))
+ dev->icon_file = resolve_path(value, devpath);
+}
+
+static int parameter(char *param_name, char *param_value)
+{
+ if (cur_opt)
+ set_boot_option_parameter(cur_opt, param_name, param_value);
+ else
+ set_device_parameter(dev, param_name, param_value);
+ return 1;
+}
+
+
+int parse(const char *device)
+{
+ char *filepath;
+ int rc;
+
+ filepath = resolve_path(conf_filename, device);
+
+ cur_opt = NULL;
+ dev = malloc(sizeof(*dev));
+ memset(dev, 0, sizeof(*dev));
+ dev->id = strdup(device);
+
+ rc = pm_process(filepath, section, parameter);
+ if (!rc)
+ return 0;
+
+ if (cur_opt) {
+ add_boot_option(cur_opt);
+ free_boot_option(cur_opt);
+ }
+
+ cur_opt = NULL;
+
+ free(filepath);
+
+ return 1;
+}
+
+struct parser native_parser = {
+ .name = "native petitboot parser",
+ .priority = 100,
+ .parse = parse
+};
+
+
+
diff --git a/discover/params.c b/discover/params.c
new file mode 100644
index 0000000..76a1451
--- /dev/null
+++ b/discover/params.c
@@ -0,0 +1,595 @@
+/* This modules is based on the params.c module from Samba, written by Karl Auer
+ and much modifed by Christopher Hertel. */
+
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.,
+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "params.h"
+
+#define new_array(type, num) ((type *)_new_array(sizeof(type), (num)))
+#define realloc_array(ptr, type, num) \
+ ((type *)_realloc_array((ptr), sizeof(type), (num)))
+
+#define rprintf(x, ...) do { fprintf(stderr, ##__VA_ARGS__); \
+ fprintf(stderr, "\n"); } while (0)
+#define rsyserr(x, y, ...) do { fprintf(stderr, ##__VA_ARGS__); \
+ fprintf(stderr, "\n"); } while (0)
+
+#define MALLOC_MAX 0x40000000
+#define False 0
+#define True 1
+
+void *_new_array(unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ return malloc(size * num);
+}
+
+void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ /* No realloc should need this, but just in case... */
+ if (!ptr)
+ return malloc(size * num);
+ return realloc(ptr, size * num);
+}
+
+
+/* -------------------------------------------------------------------------- **
+ *
+ * Module name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module performs lexical analysis and initial parsing of a
+ * Windows-like parameter file. It recognizes and handles four token
+ * types: section-name, parameter-name, parameter-value, and
+ * end-of-file. Comments and line continuation are handled
+ * internally.
+ *
+ * The entry point to the module is function pm_process(). This
+ * function opens the source file, calls the Parse() function to parse
+ * the input, and then closes the file when either the EOF is reached
+ * or a fatal error is encountered.
+ *
+ * A sample parameter file might look like this:
+ *
+ * [section one]
+ * parameter one = value string
+ * parameter two = another value
+ * [section two]
+ * new parameter = some value or t'other
+ *
+ * The parameter file is divided into sections by section headers:
+ * section names enclosed in square brackets (eg. [section one]).
+ * Each section contains parameter lines, each of which consist of a
+ * parameter name and value delimited by an equal sign. Roughly, the
+ * syntax is:
+ *
+ * <file> :== { <section> } EOF
+ *
+ * <section> :== <section header> { <parameter line> }
+ *
+ * <section header> :== '[' NAME ']'
+ *
+ * <parameter line> :== NAME '=' VALUE '\n'
+ *
+ * Blank lines and comment lines are ignored. Comment lines are lines
+ * beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ * All whitespace in section names and parameter names is compressed
+ * to single spaces. Leading and trailing whitespace is stipped from
+ * both names and values.
+ *
+ * Only the first equals sign in a parameter line is significant.
+ * Parameter values may contain equals signs, square brackets and
+ * semicolons. Internal whitespace is retained in parameter values,
+ * with the exception of the '\r' character, which is stripped for
+ * historic reasons. Parameter names may not start with a left square
+ * bracket, an equal sign, a pound sign, or a semicolon, because these
+ * are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * bufr - pointer to a global buffer. This is probably a kludge,
+ * but it was the nicest kludge I could think of (for now).
+ * bSize - The size of the global buffer <bufr>.
+ */
+
+static char *bufr = NULL;
+static int bSize = 0;
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+ * character, or newline, or EOF.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The next non-whitespace character in the input stream.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatWhitespace */
+
+static int EatComment( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan to the end of a comment.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The character that marks the end of the comment. Normally,
+ * this will be a newline, but it *might* be an EOF.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatComment */
+
+static int Continuation( char *line, int pos )
+ /* ------------------------------------------------------------------------ **
+ * Scan backards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ * Input: line - A pointer to a buffer containing the string to be
+ * scanned.
+ * pos - This is taken to be the offset of the end of the
+ * string. This position is *not* scanned.
+ *
+ * Output: The offset of the '\\' character if it was found, or -1 to
+ * indicate that it was not.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ pos--;
+ while( (pos >= 0) && isspace(((unsigned char *)line)[pos]) )
+ pos--;
+
+ return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+ } /* Continuation */
+
+
+static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan a section name, and pass the name to function sfunc().
+ *
+ * Input: InFile - Input source.
+ * sfunc - Pointer to the function to be called if the section
+ * name is successfully read.
+ *
+ * Output: True if the section name was read and True was returned from
+ * <sfunc>. False if <sfunc> failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ char *func = "params.c:Section() -";
+
+ i = 0; /* <i> is the offset of the next free byte in bufr[] and */
+ end = 0; /* <end> is the current "end of string" offset. In most */
+ /* cases these will be the same, but if the last */
+ /* character written to bufr[] is a space, then <end> */
+ /* will be one less than <i>. */
+
+ c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
+ /* past initial white space. */
+
+ while( (EOF != c) && (c > 0) )
+ {
+
+ /* Check that the buffer is big enough for the next character. */
+ if( i > (bSize - 2) )
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FERROR, "%s Memory re-allocation failure.", func);
+ return( False );
+ }
+ }
+
+ /* Handle a single character. */
+ switch( c )
+ {
+ case ']': /* Found the closing bracket. */
+ bufr[end] = '\0';
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FERROR, "%s Empty section name in configuration file.\n", func );
+ return( False );
+ }
+ if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
+ return( False );
+ (void)EatComment( InFile ); /* Finish off the line. */
+ return( True );
+
+ case '\n': /* Got newline before closing ']'. */
+ i = Continuation( bufr, i ); /* Check for line continuation. */
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FERROR, "%s Badly formed line in configuration file: %s\n",
+ func, bufr );
+ return( False );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Continue with next line. */
+ break;
+
+ default: /* All else are a valid name chars. */
+ if( isspace( c ) ) /* One space per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others copy verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* We arrive here if we've met the EOF before the closing bracket. */
+ rprintf(FERROR, "%s Unexpected EOF in the configuration file: %s\n", func, bufr );
+ return( False );
+ } /* Section */
+
+static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
+ /* ------------------------------------------------------------------------ **
+ * Scan a parameter name and value, and pass these two fields to pfunc().
+ *
+ * Input: InFile - The input source.
+ * pfunc - A pointer to the function that will be called to
+ * process the parameter, once it has been scanned.
+ * c - The first character of the parameter name, which
+ * would have been read by Parse(). Unlike a comment
+ * line or a section header, there is no lead-in
+ * character that can be discarded.
+ *
+ * Output: True if the parameter name and value were scanned and processed
+ * successfully, else False.
+ *
+ * Notes: This function is in two parts. The first loop scans the
+ * parameter name. Internal whitespace is compressed, and an
+ * equal sign (=) terminates the token. Leading and trailing
+ * whitespace is discarded. The second loop scans the parameter
+ * value. When both have been successfully identified, they are
+ * passed to pfunc() for processing.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i = 0; /* Position within bufr. */
+ int end = 0; /* bufr[end] is current end-of-string. */
+ int vstart = 0; /* Starting position of the parameter value. */
+ char *func = "params.c:Parameter() -";
+
+ /* Read the parameter name. */
+ while( 0 == vstart ) /* Loop until we've found the start of the value. */
+ {
+
+ if( i > (bSize - 2) ) /* Ensure there's space for next char. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FERROR, "%s Memory re-allocation failure.", func) ;
+ return( False );
+ }
+ }
+
+ switch( c )
+ {
+ case '=': /* Equal sign marks end of param name. */
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FERROR, "%s Invalid parameter name in config. file.\n", func );
+ return( False );
+ }
+ bufr[end++] = '\0'; /* Mark end of string & advance. */
+ i = end; /* New string starts here. */
+ vstart = end; /* New string is parameter value. */
+ bufr[i] = '\0'; /* New string is nul, for now. */
+ break;
+
+ case '\n': /* Find continuation char, else error. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FERROR, "%s Ignoring badly formed line in configuration file: %s\n",
+ func, bufr );
+ return( True );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Read past eoln. */
+ break;
+
+ case '\0': /* Shouldn't have EOF within param name. */
+ case EOF:
+ bufr[i] = '\0';
+ rprintf(FERROR, "%s Unexpected end-of-file at: %s\n", func, bufr );
+ return( True );
+
+ default:
+ if( isspace( c ) ) /* One ' ' per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* Now parse the value. */
+ c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */
+ while( (EOF !=c) && (c > 0) )
+ {
+
+ if( i > (bSize - 2) ) /* Make sure there's enough room. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FERROR, "%s Memory re-allocation failure.", func) ;
+ return( False );
+ }
+ }
+
+ switch( c )
+ {
+ case '\r': /* Explicitly remove '\r' because the older */
+ c = getc( InFile ); /* version called fgets_slash() which also */
+ break; /* removes them. */
+
+ case '\n': /* Marks end of value unless there's a '\'. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ c = 0;
+ else
+ {
+ for( end = i; (end >= 0) && isspace(((unsigned char *) bufr)[end]); end-- )
+ ;
+ c = getc( InFile );
+ }
+ break;
+
+ default: /* All others verbatim. Note that spaces do */
+ bufr[i++] = c; /* not advance <end>. This allows trimming */
+ if( !isspace( c ) ) /* of whitespace at the end of the line. */
+ end = i;
+ c = getc( InFile );
+ break;
+ }
+ }
+ bufr[end] = '\0'; /* End of value. */
+
+ return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
+ } /* Parameter */
+
+static BOOL Parse( FILE *InFile,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan & parse the input.
+ *
+ * Input: InFile - Input source.
+ * sfunc - Function to be called when a section name is scanned.
+ * See Section().
+ * pfunc - Function to be called when a parameter is scanned.
+ * See Parameter().
+ *
+ * Output: True if the file was successfully scanned, else False.
+ *
+ * Notes: The input can be viewed in terms of 'lines'. There are four
+ * types of lines:
+ * Blank - May contain whitespace, otherwise empty.
+ * Comment - First non-whitespace character is a ';' or '#'.
+ * The remainder of the line is ignored.
+ * Section - First non-whitespace character is a '['.
+ * Parameter - The default case.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ c = EatWhitespace( InFile );
+ while( (EOF != c) && (c > 0) )
+ {
+ switch( c )
+ {
+ case '\n': /* Blank line. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case ';': /* Comment line. */
+ case '#':
+ c = EatComment( InFile );
+ break;
+
+ case '[': /* Section Header. */
+ if (!sfunc) return True;
+ if( !Section( InFile, sfunc ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+
+ case '\\': /* Bogus backslash. */
+ c = EatWhitespace( InFile );
+ break;
+
+ default: /* Parameter line. */
+ if( !Parameter( InFile, pfunc, c ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+ }
+ }
+ return( True );
+ } /* Parse */
+
+static FILE *OpenConfFile( char *FileName )
+ /* ------------------------------------------------------------------------ **
+ * Open a configuration file.
+ *
+ * Input: FileName - The pathname of the config file to be opened.
+ *
+ * Output: A pointer of type (FILE *) to the opened file, or NULL if the
+ * file could not be opened.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ FILE *OpenedFile;
+ char *func = "params.c:OpenConfFile() -";
+
+ if( NULL == FileName || 0 == *FileName )
+ {
+ rprintf(FERROR,"%s No configuration filename specified.\n", func);
+ return( NULL );
+ }
+
+ OpenedFile = fopen( FileName, "r" );
+ if( NULL == OpenedFile )
+ {
+ rsyserr(FERROR, errno, "unable to open configuration file \"%s\"",
+ FileName);
+ }
+
+ return( OpenedFile );
+ } /* OpenConfFile */
+
+BOOL pm_process( char *FileName,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Process the named parameter file.
+ *
+ * Input: FileName - The pathname of the parameter file to be opened.
+ * sfunc - A pointer to a function that will be called when
+ * a section name is discovered.
+ * pfunc - A pointer to a function that will be called when
+ * a parameter name and value are discovered.
+ *
+ * Output: TRUE if the file was successfully parsed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int result;
+ FILE *InFile;
+ char *func = "params.c:pm_process() -";
+
+ InFile = OpenConfFile( FileName ); /* Open the config file. */
+ if( NULL == InFile )
+ return( False );
+
+ if( NULL != bufr ) /* If we already have a buffer */
+ result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
+ /* use it. */
+
+ else /* If we don't have a buffer */
+ { /* allocate one, then parse, */
+ bSize = BUFR_INC; /* then free. */
+ bufr = new_array( char, bSize );
+ if( NULL == bufr )
+ {
+ rprintf(FERROR,"%s memory allocation failure.\n", func);
+ fclose(InFile);
+ return( False );
+ }
+ result = Parse( InFile, sfunc, pfunc );
+ free( bufr );
+ bufr = NULL;
+ bSize = 0;
+ }
+
+ fclose(InFile);
+
+ if( !result ) /* Generic failure. */
+ {
+ rprintf(FERROR,"%s Failed. Error returned from params.c:parse().\n", func);
+ return( False );
+ }
+
+ return( True ); /* Generic success. */
+ } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/discover/params.h b/discover/params.h
new file mode 100644
index 0000000..02a39c9
--- /dev/null
+++ b/discover/params.h
@@ -0,0 +1,6 @@
+
+#define BOOL int
+
+BOOL pm_process( char *FileName,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) );
diff --git a/discover/parser.c b/discover/parser.c
new file mode 100644
index 0000000..5e50dcb
--- /dev/null
+++ b/discover/parser.c
@@ -0,0 +1,85 @@
+
+#include <petitboot-paths.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+extern struct parser native_parser;
+extern struct parser yaboot_parser;
+extern struct parser kboot_parser;
+
+/* array of parsers, ordered by priority */
+static struct parser *parsers[] = {
+ &native_parser,
+ &yaboot_parser,
+ &kboot_parser,
+ NULL
+};
+
+void iterate_parsers(const char *devpath, const char *mountpoint)
+{
+ int i;
+
+ pb_log("trying parsers for %s\n", devpath);
+
+ for (i = 0; parsers[i]; i++) {
+ pb_log("\ttrying parser '%s'\n", parsers[i]->name);
+ /* just use a dummy device path for now */
+ if (parsers[i]->parse(devpath))
+ return;
+ }
+ pb_log("\tno boot_options found\n");
+}
+
+/* convenience functions for parsers */
+void free_device(struct device *dev)
+{
+ if (!dev)
+ return;
+ if (dev->id)
+ free(dev->id);
+ if (dev->name)
+ free(dev->name);
+ if (dev->description)
+ free(dev->description);
+ if (dev->icon_file)
+ free(dev->icon_file);
+ free(dev);
+}
+
+void free_boot_option(struct boot_option *opt)
+{
+ if (!opt)
+ return;
+ if (opt->name)
+ free(opt->name);
+ if (opt->description)
+ free(opt->description);
+ if (opt->icon_file)
+ free(opt->icon_file);
+ if (opt->boot_image_file)
+ free(opt->boot_image_file);
+ if (opt->initrd_file)
+ free(opt->initrd_file);
+ if (opt->boot_args)
+ free(opt->boot_args);
+ free(opt);
+}
+
+const char *generic_icon_file(enum generic_icon_type type)
+{
+ switch (type) {
+ case ICON_TYPE_DISK:
+ return artwork_pathname("hdd.png");
+ case ICON_TYPE_USB:
+ return artwork_pathname("usbpen.png");
+ case ICON_TYPE_OPTICAL:
+ return artwork_pathname("cdrom.png");
+ case ICON_TYPE_NETWORK:
+ case ICON_TYPE_UNKNOWN:
+ break;
+ }
+ return artwork_pathname("hdd.png");
+}
+
diff --git a/discover/parser.h b/discover/parser.h
new file mode 100644
index 0000000..9c6fb35
--- /dev/null
+++ b/discover/parser.h
@@ -0,0 +1,46 @@
+
+#ifndef _PARSERS_H
+#define _PARSERS_H
+
+#include <stdarg.h>
+#include "message.h"
+
+struct parser {
+ char *name;
+ int priority;
+ int (*parse)(const char *device);
+ struct parser *next;
+};
+
+enum generic_icon_type {
+ ICON_TYPE_DISK,
+ ICON_TYPE_USB,
+ ICON_TYPE_OPTICAL,
+ ICON_TYPE_NETWORK,
+ ICON_TYPE_UNKNOWN
+};
+
+#define streq(a,b) (!strcasecmp((a),(b)))
+
+/* general functions provided by parsers.c */
+void iterate_parsers(const char *devpath, const char *mountpoint);
+
+void free_device(struct device *dev);
+void free_boot_option(struct boot_option *opt);
+
+const char *generic_icon_file(enum generic_icon_type type);
+
+/* functions provided by udev-helper or the test wrapper */
+void pb_log(const char *fmt, ...);
+
+int mount_device(const char *dev_path);
+
+char *resolve_path(const char *path, const char *current_dev);
+const char *mountpoint_for_device(const char *dev_path);
+
+enum generic_icon_type guess_device_type(void);
+
+int add_device(const struct device *dev);
+int add_boot_option(const struct boot_option *opt);
+
+#endif /* _PARSERS_H */
diff --git a/discover/paths.c b/discover/paths.c
new file mode 100644
index 0000000..2373c28
--- /dev/null
+++ b/discover/paths.c
@@ -0,0 +1,141 @@
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "paths.h"
+
+static char *mount_base;
+
+struct device_map {
+ char *dev, *mnt;
+};
+
+#define DEVICE_MAP_SIZE 32
+static struct device_map device_map[DEVICE_MAP_SIZE];
+
+char *encode_label(const char *label)
+{
+ char *str, *c;
+ int i;
+
+ /* the label can be expanded by up to four times */
+ str = malloc(strlen(label) * 4 + 1);
+ c = str;
+
+ for (i = 0; i < strlen(label); i++) {
+
+ if (label[i] == '/' || label[i] == '\\') {
+ sprintf(c, "\\x%02x", label[i]);
+ c += 4;
+ continue;
+ }
+
+ *(c++) = label[i];
+ }
+
+ *c = '\0';
+
+ return str;
+}
+
+char *parse_device_path(const char *dev_str, const char *cur_dev)
+{
+ char *dev, tmp[256], *enc;
+
+ if (!strncasecmp(dev_str, "uuid=", 5)) {
+ asprintf(&dev, "/dev/disk/by-uuid/%s", dev_str + 5);
+ return dev;
+ }
+
+ if (!strncasecmp(dev_str, "label=", 6)) {
+ enc = encode_label(dev_str + 6);
+ asprintf(&dev, "/dev/disk/by-label/%s", enc);
+ free(enc);
+ return dev;
+ }
+
+ /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand
+ * back before returning.
+ */
+ if (!strncmp(dev_str, "/dev/", 5))
+ dev_str += 5;
+
+ /* PS3 hack: if we're reading from a ps3dx device, and we refer to
+ * a sdx device, remap to ps3dx */
+ if (cur_dev && !strncmp(cur_dev, "/dev/ps3d", 9)
+ && !strncmp(dev_str, "sd", 2)) {
+ snprintf(tmp, 255, "ps3d%s", dev_str + 2);
+ dev_str = tmp;
+ }
+
+ return join_paths("/dev", dev_str);
+}
+
+const char *mountpoint_for_device(const char *dev)
+{
+ int i;
+
+ if (!strncmp(dev, "/dev/", 5))
+ dev += 5;
+
+ /* check existing entries in the map */
+ for (i = 0; (i < DEVICE_MAP_SIZE) && device_map[i].dev; i++)
+ if (!strcmp(device_map[i].dev, dev))
+ return device_map[i].mnt;
+
+ if (i == DEVICE_MAP_SIZE)
+ return NULL;
+
+ device_map[i].dev = strdup(dev);
+ device_map[i].mnt = join_paths(mount_base, dev);
+ return device_map[i].mnt;
+}
+
+char *resolve_path(const char *path, const char *current_dev)
+{
+ char *ret;
+ const char *devpath, *sep;
+
+ sep = strchr(path, ':');
+ if (!sep) {
+ devpath = mountpoint_for_device(current_dev);
+ ret = join_paths(devpath, path);
+ } else {
+ /* parse just the device name into dev */
+ char *tmp, *dev;
+ tmp = strndup(path, sep - path);
+ dev = parse_device_path(tmp, current_dev);
+
+ devpath = mountpoint_for_device(dev);
+ ret = join_paths(devpath, sep + 1);
+
+ free(dev);
+ free(tmp);
+ }
+
+ return ret;
+}
+
+void set_mount_base(const char *path)
+{
+ if (mount_base)
+ free(mount_base);
+ mount_base = strdup(path);
+}
+
+char *join_paths(const char *a, const char *b)
+{
+ char *full_path;
+
+ full_path = malloc(strlen(a) + strlen(b) + 2);
+
+ strcpy(full_path, a);
+ if (b[0] != '/' && a[strlen(a) - 1] != '/')
+ strcat(full_path, "/");
+ strcat(full_path, b);
+
+ return full_path;
+}
+
diff --git a/discover/paths.h b/discover/paths.h
new file mode 100644
index 0000000..26d4ce4
--- /dev/null
+++ b/discover/paths.h
@@ -0,0 +1,53 @@
+#ifndef PATHS_H
+#define PATHS_H
+
+/**
+ * Given a string (eg /dev/sda1, sda1 or UUID=B8E53381CA9EA0E3), parse the
+ * device path (eg /dev/sda1). Any device descriptions read from config files
+ * should be parsed into the path first.
+ *
+ * The cur_dev is provided for some remapping situations. If NULL is provided,
+ * no remapping will be done.
+ *
+ * Returns a newly-allocated string.
+ */
+char *parse_device_path(const char *dev_str, const char *current_device);
+
+/**
+ * Get the mountpoint for a device.
+ */
+const char *mountpoint_for_device(const char *dev);
+
+/**
+ * Resolve a path given in a config file, to a path in the local filesystem.
+ * Paths may be of the form:
+ * device:path (eg /dev/sda:/boot/vmlinux)
+ *
+ * or just a path:
+ * /boot/vmlinux
+ * - in this case, the current mountpoint is used.
+ *
+ * Returns a newly-allocated string containing a full path to the file in path
+ */
+char *resolve_path(const char *path, const char *current_device);
+
+
+/**
+ * Set the base directory for newly-created mountpoints
+ */
+void set_mount_base(const char *path);
+
+/**
+ * Utility function for joining two paths. Adds a / between a and b if
+ * required.
+ *
+ * Returns a newly-allocated string.
+ */
+char *join_paths(const char *a, const char *b);
+
+/**
+ * encode a disk label (or uuid) for use in a symlink.
+ */
+char *encode_label(const char *label);
+
+#endif /* PATHS_H */
diff --git a/discover/pb-discover.c b/discover/pb-discover.c
new file mode 100644
index 0000000..45b6ba1
--- /dev/null
+++ b/discover/pb-discover.c
@@ -0,0 +1,34 @@
+
+#include <stdlib.h>
+#include <signal.h>
+
+#include "udev.h"
+#include "discover-server.h"
+#include "waiter.h"
+#include "log.h"
+
+
+int main(void)
+{
+ struct discover_server *server;
+ struct udev *udev;
+
+ /* we look for closed sockets when we write, so ignore SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+
+ udev = udev_init();
+ if (!udev)
+ return EXIT_FAILURE;
+
+ server = discover_server_init();
+ if (!server)
+ return EXIT_FAILURE;
+
+ for (;;) {
+ if (waiter_poll())
+ return EXIT_FAILURE;
+ }
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/discover/pb-discover.h b/discover/pb-discover.h
new file mode 100644
index 0000000..a48557c
--- /dev/null
+++ b/discover/pb-discover.h
@@ -0,0 +1,6 @@
+#ifndef _PB_DISCOVER_H
+#define _PB_DISCOVER_H
+
+int register_waiter(int fd, int *callback(void *), void *arg);
+
+#endif /* _PB_DISCOVER_H */
diff --git a/discover/udev.c b/discover/udev.c
new file mode 100644
index 0000000..9c1b399
--- /dev/null
+++ b/discover/udev.c
@@ -0,0 +1,208 @@
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <talloc/talloc.h>
+
+#include "udev.h"
+#include "log.h"
+#include "waiter.h"
+#include "pb-discover.h"
+
+#define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev"
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+struct udev {
+ int socket;
+};
+
+static void parse_event_params(struct udev_event *event, char *buf, int len)
+{
+ int param_len, name_len, value_len;
+ struct param *param;
+ char *sep;
+
+ for (; len > 0; len -= param_len + 1, buf += param_len + 1) {
+
+ /* find the length of the whole parameter */
+ param_len = strnlen(buf, len);
+ if (!param_len) {
+ /* multiple NULs? skip over */
+ param_len = 1;
+ continue;
+ }
+
+ /* find the separator */
+ sep = memchr(buf, '=', param_len);
+ if (!sep)
+ continue;
+
+ name_len = sep - buf;
+ value_len = param_len - name_len - 1;
+
+ /* update the params array */
+ event->params = talloc_realloc(event, event->params,
+ struct param, ++event->n_params);
+ param = &event->params[event->n_params - 1];
+
+ param->name = talloc_strndup(event, buf, name_len);
+ param->value = talloc_strndup(event, sep + 1, value_len);
+ }
+}
+
+static const char *event_param(struct udev_event *event, const char *name)
+{
+ int i;
+
+ for (i = 0; i < event->n_params; i++)
+ if (!strcasecmp(event->params[i].name, name))
+ return event->params[i].value;
+
+ return NULL;
+}
+
+static void print_event(struct udev_event *event)
+{
+ const char *action, *params[] = {
+ "DEVNAME", "ID_TYPE", "ID_BUS", "ID_FS_UUID", "ID_FS_LABEL",
+ NULL,
+ };
+ int i;
+
+ action = event->action == UDEV_ACTION_ADD ? "add" : "remove";
+
+ pb_log("udev %s event:\n", action);
+ printf("\tdevice: %s\n", event->device);
+
+ for (i = 0; params[i]; i++)
+ printf("\t%-12s => %s\n",
+ params[i], event_param(event, params[i]));
+
+}
+
+static void handle_udev_message(struct udev *udev, char *buf, int len)
+{
+ char *sep, *device;
+ enum udev_action action;
+ struct udev_event *event;
+ int device_len;
+
+ /* we should see an <action>@<device>\0 at the head of the buffer */
+ sep = strchr(buf, '@');
+ if (!sep)
+ return;
+
+ /* terminate the action string */
+ *sep = '\0';
+ len -= sep - buf + 1;
+
+ if (!strcmp(buf, "add")) {
+ action = UDEV_ACTION_ADD;
+
+ } else if (!strcmp(buf, "remove")) {
+ action = UDEV_ACTION_REMOVE;
+
+ } else {
+ return;
+ }
+
+ /* initialise the device string */
+ device = sep + 1;
+ device_len = strnlen(device, len);
+ if (!device_len)
+ return;
+
+ /* now we have an action and a device, we can construct an event */
+ event = talloc(udev, struct udev_event);
+ event->action = action;
+ event->device = talloc_strndup(event, device, device_len);
+ event->n_params = 0;
+ event->params = NULL;
+
+ len -= device_len + 1;
+ parse_event_params(event, device + device_len + 1, len);
+
+ print_event(event);
+
+ talloc_free(event);
+
+ return;
+}
+
+static int udev_process(void *arg)
+{
+ struct udev *udev = arg;
+ char buf[4096];
+ int len;
+
+ len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL);
+
+ if (len < 0) {
+ pb_log("udev socket read failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if (len == 0)
+ return 0;
+
+ handle_udev_message(udev, buf, len);
+
+ return 0;
+}
+
+static int udev_destructor(void *p)
+{
+ struct udev *udev = p;
+
+ if (udev->socket >= 0)
+ close(udev->socket);
+
+ return 0;
+}
+
+struct udev *udev_init(void)
+{
+ struct sockaddr_un addr;
+ struct udev *udev;
+
+ unlink(PBOOT_DEVICE_SOCKET);
+
+ udev = talloc(NULL, struct udev);
+
+ udev->socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (udev->socket < 0) {
+ pb_log("Error creating udev socket: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ talloc_set_destructor(udev, udev_destructor);
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
+
+ if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
+ pb_log("Error binding udev socket: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ waiter_register(udev->socket, WAIT_IN, udev_process, udev);
+
+ return udev;
+
+out_err:
+ talloc_free(udev);
+ return NULL;
+}
+
+void udev_destroy(struct udev *udev)
+{
+ talloc_free(udev);
+}
diff --git a/discover/udev.h b/discover/udev.h
new file mode 100644
index 0000000..c30adc9
--- /dev/null
+++ b/discover/udev.h
@@ -0,0 +1,26 @@
+#ifndef _UDEV_H
+#define _UDEV_H
+
+enum udev_action {
+ UDEV_ACTION_ADD,
+ UDEV_ACTION_REMOVE,
+};
+
+struct udev_event {
+ enum udev_action action;
+ char *device;
+
+ struct param {
+ char *name;
+ char *value;
+ } *params;
+ int n_params;
+};
+
+struct udev;
+
+struct udev *udev_init(void);
+
+void udev_destroy(struct udev *udev);
+
+#endif /* _UDEV_H */
diff --git a/discover/waiter.c b/discover/waiter.c
new file mode 100644
index 0000000..21dd4a5
--- /dev/null
+++ b/discover/waiter.c
@@ -0,0 +1,83 @@
+
+#include <poll.h>
+#include <string.h>
+#include <assert.h>
+
+#include <talloc/talloc.h>
+
+#include "waiter.h"
+
+struct waiter {
+ int fd;
+ int events;
+ waiter_cb callback;
+ void *arg;
+};
+
+static struct waiter *waiters;
+static int n_waiters;
+
+struct waiter *waiter_register(int fd, int events,
+ waiter_cb callback, void *arg)
+{
+ struct waiter *waiter;
+
+ n_waiters++;
+
+ waiters = talloc_realloc(NULL, waiters, struct waiter, n_waiters);
+ waiter = &waiters[n_waiters - 1];
+
+ waiter->fd = fd;
+ waiter->events = events;
+ waiter->callback = callback;
+ waiter->arg = arg;
+
+ return 0;
+}
+
+void waiter_remove(struct waiter *waiter)
+{
+ int i;
+
+ i = waiter - waiters;
+ assert(i >= 0 && i < n_waiters);
+
+ n_waiters--;
+ memmove(&waiters[i], &waiters[i+1], n_waiters - i);
+
+ waiters = talloc_realloc(NULL, waiters, struct waiter, n_waiters);
+}
+
+int waiter_poll(void)
+{
+ static struct pollfd *pollfds;
+ static int n_pollfds;
+ int i, rc;
+
+ if (n_waiters > n_pollfds) {
+ pollfds = talloc_realloc(NULL, pollfds,
+ struct pollfd, n_waiters);
+ }
+
+ for (i = 0; i < n_waiters; i++) {
+ pollfds[i].fd = waiters[i].fd;
+ pollfds[i].events = waiters[i].events;
+ pollfds[i].revents = 0;
+ }
+
+ rc = poll(pollfds, n_waiters, -1);
+
+ if (rc <= 0)
+ return rc;
+
+ for (i = 0; i < n_waiters; i++) {
+ if (pollfds[i].revents) {
+ rc = waiters[i].callback(waiters[i].arg);
+
+ if (rc)
+ waiter_remove(&waiters[i]);
+ }
+ }
+
+ return 0;
+}
diff --git a/discover/waiter.h b/discover/waiter.h
new file mode 100644
index 0000000..ff8a5ff
--- /dev/null
+++ b/discover/waiter.h
@@ -0,0 +1,23 @@
+#ifndef _WAITER_H
+#define _WAITER_H
+
+#include <poll.h>
+
+struct waiter;
+
+enum events {
+ WAIT_IN = POLLIN,
+ WAIT_OUT = POLLOUT,
+};
+
+typedef int (*waiter_cb)(void *);
+
+struct waiter *waiter_register(int fd, int events,
+ waiter_cb callback, void *arg);
+
+void waiter_remove(struct waiter *waiter);
+
+int waiter_poll(void);
+#endif /* _WAITER_H */
+
+
diff --git a/discover/yaboot-cfg.c b/discover/yaboot-cfg.c
new file mode 100644
index 0000000..6b007e4
--- /dev/null
+++ b/discover/yaboot-cfg.c
@@ -0,0 +1,491 @@
+/*
+ * cfg.c - Handling and parsing of yaboot.conf
+ *
+ * Copyright (C) 1995 Werner Almesberger
+ * 1996 Jakub Jelinek
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define prom_printf printf
+#define prom_putchar putchar
+#define prom_vprintf vprintf
+
+/* Imported functions */
+extern int strcasecmp(const char *s1, const char *s2);
+
+typedef enum {
+ cft_strg, cft_flag, cft_end
+} CONFIG_TYPE;
+
+typedef struct {
+ CONFIG_TYPE type;
+ char *name;
+ void *data;
+} CONFIG;
+
+#define MAX_TOKEN 200
+#define MAX_VAR_NAME MAX_TOKEN
+char *cfg_get_default (void);
+
+CONFIG cf_options[] =
+{
+ {cft_strg, "device", NULL},
+ {cft_strg, "partition", NULL},
+ {cft_strg, "default", NULL},
+ {cft_strg, "timeout", NULL},
+ {cft_strg, "password", NULL},
+ {cft_flag, "restricted", NULL},
+ {cft_strg, "message", NULL},
+ {cft_strg, "root", NULL},
+ {cft_strg, "ramdisk", NULL},
+ {cft_flag, "read-only", NULL},
+ {cft_flag, "read-write", NULL},
+ {cft_strg, "append", NULL},
+ {cft_strg, "initrd", NULL},
+ {cft_flag, "initrd-prompt", NULL},
+ {cft_strg, "initrd-size", NULL},
+ {cft_flag, "pause-after", NULL},
+ {cft_strg, "pause-message", NULL},
+ {cft_strg, "init-code", NULL},
+ {cft_strg, "init-message", NULL},
+ {cft_strg, "fgcolor", NULL},
+ {cft_strg, "bgcolor", NULL},
+ {cft_strg, "ptypewarning", NULL},
+ {cft_end, NULL, NULL}};
+
+CONFIG cf_image[] =
+{
+ {cft_strg, "image", NULL},
+ {cft_strg, "label", NULL},
+ {cft_strg, "alias", NULL},
+ {cft_flag, "single-key", NULL},
+ {cft_flag, "restricted", NULL},
+ {cft_strg, "device", NULL},
+ {cft_strg, "partition", NULL},
+ {cft_strg, "root", NULL},
+ {cft_strg, "ramdisk", NULL},
+ {cft_flag, "read-only", NULL},
+ {cft_flag, "read-write", NULL},
+ {cft_strg, "append", NULL},
+ {cft_strg, "literal", NULL},
+ {cft_strg, "initrd", NULL},
+ {cft_flag, "initrd-prompt", NULL},
+ {cft_strg, "initrd-size", NULL},
+ {cft_flag, "pause-after", NULL},
+ {cft_strg, "pause-message", NULL},
+ {cft_flag, "novideo", NULL},
+ {cft_strg, "sysmap", NULL},
+ {cft_end, NULL, NULL}};
+
+static char flag_set;
+static char *last_token = NULL, *last_item = NULL, *last_value = NULL;
+static int line_num;
+static int back = 0; /* can go back by one char */
+static char *currp = NULL;
+static char *endp = NULL;
+static char *file_name = NULL;
+static CONFIG *curr_table = cf_options;
+static jmp_buf env;
+
+static struct IMAGES {
+ CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])];
+ struct IMAGES *next;
+} *images = NULL;
+
+void cfg_error (char *msg,...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ prom_printf ("Config file error: ");
+ prom_vprintf (msg, ap);
+ va_end (ap);
+ prom_printf (" near line %d in file %s\n", line_num, file_name);
+ longjmp (env, 1);
+}
+
+void cfg_warn (char *msg,...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ prom_printf ("Config file warning: ");
+ prom_vprintf (msg, ap);
+ va_end (ap);
+ prom_printf (" near line %d in file %s\n", line_num, file_name);
+}
+
+int cfg_getc ()
+{
+ if (currp == endp)
+ return EOF;
+ return *currp++;
+}
+
+#define next_raw next
+static int next (void)
+{
+ int ch;
+
+ if (!back)
+ return cfg_getc ();
+ ch = back;
+ back = 0;
+ return ch;
+}
+
+static void again (int ch)
+{
+ back = ch;
+}
+
+static char *cfg_get_token (void)
+{
+ char buf[MAX_TOKEN + 1];
+ char *here;
+ int ch, escaped;
+
+ if (last_token) {
+ here = last_token;
+ last_token = NULL;
+ return here;
+ }
+ while (1) {
+ while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+ if (ch == '\n' || ch == '\r')
+ line_num++;
+ if (ch == EOF || ch == (int)NULL)
+ return NULL;
+ if (ch != '#')
+ break;
+ while (ch = next_raw (), (ch != '\n' && ch != '\r'))
+ if (ch == EOF)
+ return NULL;
+ line_num++;
+ }
+ if (ch == '=')
+ return strdup ("=");
+ if (ch == '"') {
+ here = buf;
+ while (here - buf < MAX_TOKEN) {
+ if ((ch = next ()) == EOF)
+ cfg_error ("EOF in quoted string");
+ if (ch == '"') {
+ *here = 0;
+ return strdup (buf);
+ }
+ if (ch == '\\') {
+ ch = next ();
+ switch (ch) {
+ case '"':
+ case '\\':
+ break;
+ case '\n':
+ case '\r':
+ while ((ch = next ()), ch == ' ' || ch == '\t');
+ if (!ch)
+ continue;
+ again (ch);
+ ch = ' ';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ default:
+ cfg_error ("Bad use of \\ in quoted string");
+ }
+ } else if ((ch == '\n') || (ch == '\r'))
+ cfg_error ("newline is not allowed in quoted strings");
+ *here++ = ch;
+ }
+ cfg_error ("Quoted string is too long");
+ return 0; /* not reached */
+ }
+ here = buf;
+ escaped = 0;
+ while (here - buf < MAX_TOKEN) {
+ if (escaped) {
+ if (ch == EOF)
+ cfg_error ("\\ precedes EOF");
+ if (ch == '\n')
+ line_num++;
+ else
+ *here++ = ch == '\t' ? ' ' : ch;
+ escaped = 0;
+ } else {
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' ||
+ ch == '=' || ch == EOF) {
+ again (ch);
+ *here = 0;
+ return strdup (buf);
+ }
+ if (!(escaped = (ch == '\\')))
+ *here++ = ch;
+ }
+ ch = next ();
+ }
+ cfg_error ("Token is too long");
+ return 0; /* not reached */
+}
+
+static void cfg_return_token (char *token)
+{
+ last_token = token;
+}
+
+static int cfg_next (char **item, char **value)
+{
+ char *this;
+
+ if (last_item) {
+ *item = last_item;
+ *value = last_value;
+ last_item = NULL;
+ return 1;
+ }
+ *value = NULL;
+ if (!(*item = cfg_get_token ()))
+ return 0;
+ if (!strcmp (*item, "="))
+ cfg_error ("Syntax error");
+ if (!(this = cfg_get_token ()))
+ return 1;
+ if (strcmp (this, "=")) {
+ cfg_return_token (this);
+ return 1;
+ }
+ if (!(*value = cfg_get_token ()))
+ cfg_error ("Value expected at EOF");
+ if (!strcmp (*value, "="))
+ cfg_error ("Syntax error after %s", *item);
+ return 1;
+}
+
+#if 0
+// The one and only call to this procedure is commented out
+// below, so we don't need this unless we decide to use it again.
+static void cfg_return (char *item, char *value)
+{
+ last_item = item;
+ last_value = value;
+}
+#endif
+
+static int cfg_set (char *item, char *value)
+{
+ CONFIG *walk;
+
+ if (!strcasecmp (item, "image")) {
+ struct IMAGES **p = &images;
+
+ while (*p)
+ p = &((*p)->next);
+ *p = (struct IMAGES *)malloc (sizeof (struct IMAGES));
+ if (*p == NULL) {
+ prom_printf("malloc error in cfg_set\n");
+ return -1;
+ }
+ (*p)->next = 0;
+ curr_table = ((*p)->table);
+ memcpy (curr_table, cf_image, sizeof (cf_image));
+ }
+ for (walk = curr_table; walk->type != cft_end; walk++) {
+ if (walk->name && !strcasecmp (walk->name, item)) {
+ if (value && walk->type != cft_strg)
+ cfg_warn ("'%s' doesn't have a value", walk->name);
+ else if (!value && walk->type == cft_strg)
+ cfg_warn ("Value expected for '%s'", walk->name);
+ else {
+ if (walk->data)
+ cfg_warn ("Duplicate entry '%s'", walk->name);
+ if (walk->type == cft_flag)
+ walk->data = &flag_set;
+ else if (walk->type == cft_strg)
+ walk->data = value;
+ }
+ break;
+ }
+ }
+ if (walk->type != cft_end)
+ return 1;
+// cfg_return (item, value);
+ return 0;
+}
+
+int cfg_parse (char *cfg_file, char *buff, int len)
+{
+ char *item, *value;
+
+ file_name = cfg_file;
+ currp = buff;
+ endp = currp + len;
+
+ if (setjmp (env))
+ return -1;
+ while (1) {
+ if (!cfg_next (&item, &value))
+ return 0;
+ if (!cfg_set (item, value)) {
+#if DEBUG
+ prom_printf("Can't set item %s to value %s\n", item, value);
+#endif
+ }
+ free (item);
+ }
+}
+
+static char *cfg_get_strg_i (CONFIG * table, char *item)
+{
+ CONFIG *walk;
+
+ for (walk = table; walk->type != cft_end; walk++)
+ if (walk->name && !strcasecmp (walk->name, item))
+ return walk->data;
+ return 0;
+}
+
+char *cfg_get_strg (char *image, char *item)
+{
+ struct IMAGES *p;
+ char *label, *alias;
+ char *ret;
+
+ if (!image)
+ return cfg_get_strg_i (cf_options, item);
+ for (p = images; p; p = p->next) {
+ label = cfg_get_strg_i (p->table, "label");
+ if (!label) {
+ label = cfg_get_strg_i (p->table, "image");
+ alias = strrchr (label, '/');
+ if (alias)
+ label = alias + 1;
+ }
+ alias = cfg_get_strg_i (p->table, "alias");
+ if (!strcmp (label, image) || (alias && !strcmp (alias, image))) {
+ ret = cfg_get_strg_i (p->table, item);
+ if (!ret)
+ ret = cfg_get_strg_i (cf_options, item);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int cfg_get_flag (char *image, char *item)
+{
+ return !!cfg_get_strg (image, item);
+}
+
+static int printl_count = 0;
+static void printlabel (char *label, int defflag)
+{
+ int len = strlen (label);
+
+ if (!printl_count)
+ prom_printf ("\n");
+ prom_printf ("%s %s",defflag?"*":" ", label);
+ while (len++ < 25)
+ prom_putchar (' ');
+ printl_count++;
+ if (printl_count == 3)
+ printl_count = 0;
+}
+
+void cfg_print_images (void)
+{
+ struct IMAGES *p;
+ char *label, *alias;
+
+ char *ret = cfg_get_default();//strg_i (cf_options, "default");
+ int defflag=0;
+
+ printl_count = 0;
+ for (p = images; p; p = p->next) {
+ label = cfg_get_strg_i (p->table, "label");
+ if (!label) {
+ label = cfg_get_strg_i (p->table, "image");
+ alias = strrchr (label, '/');
+ if (alias)
+ label = alias + 1;
+ }
+ if(!strcmp(ret,label))
+ defflag=1;
+ else
+ defflag=0;
+ alias = cfg_get_strg_i (p->table, "alias");
+ printlabel (label, defflag);
+ if (alias)
+ printlabel (alias, 0);
+ }
+ prom_printf("\n");
+}
+
+char *cfg_get_default (void)
+{
+ char *label;
+ char *ret = cfg_get_strg_i (cf_options, "default");
+
+ if (ret)
+ return ret;
+ if (!images)
+ return 0;
+ ret = cfg_get_strg_i (images->table, "label");
+ if (!ret) {
+ ret = cfg_get_strg_i (images->table, "image");
+ label = strrchr (ret, '/');
+ if (label)
+ ret = label + 1;
+ }
+ return ret;
+}
+
+char *cfg_next_image(char *prev)
+{
+ struct IMAGES *p;
+ char *label, *alias;
+ int wantnext = 0;
+
+ if (!prev)
+ wantnext = 1;
+
+ for (p = images; p; p = p->next) {
+ label = cfg_get_strg_i (p->table, "label");
+ if (!label) {
+ label = cfg_get_strg_i (p->table, "image");
+ alias = strrchr (label, '/');
+ if (alias)
+ label = alias + 1;
+ }
+ if (wantnext)
+ return label;
+ if (!strcmp(prev, label))
+ wantnext = 1;
+ }
+ return NULL;
+}
+/*
+ * Local variables:
+ * c-file-style: "k&r"
+ * c-basic-offset: 5
+ * End:
+ */
diff --git a/discover/yaboot-cfg.h b/discover/yaboot-cfg.h
new file mode 100644
index 0000000..2ab4fec
--- /dev/null
+++ b/discover/yaboot-cfg.h
@@ -0,0 +1,30 @@
+/*
+ * cfg.h - config file parsing definitions
+ *
+ * Copyright (C) 1999 Benjamin Herrenschmidt
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef CFG_H
+#define CFG_H
+
+extern int cfg_parse(char *cfg_file, char *buff, int len);
+extern char* cfg_get_strg(char *image, char *item);
+extern int cfg_get_flag(char *image, char *item);
+extern void cfg_print_images(void);
+extern char* cfg_get_default(void);
+extern char* cfg_next_image(char *);
+#endif
diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c
new file mode 100644
index 0000000..27b4b78
--- /dev/null
+++ b/discover/yaboot-parser.c
@@ -0,0 +1,235 @@
+
+#include "parser.h"
+#include "params.h"
+#include "paths.h"
+#include "yaboot-cfg.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+static struct device *dev;
+static char *devpath;
+static char *defimage;
+
+char *
+make_params(char *label, char *params)
+{
+ char *p, *q;
+ static char buffer[2048];
+
+ q = buffer;
+ *q = 0;
+
+ p = cfg_get_strg(label, "literal");
+ if (p) {
+ strcpy(q, p);
+ q = strchr(q, 0);
+ if (params) {
+ if (*p)
+ *q++ = ' ';
+ strcpy(q, params);
+ }
+ return buffer;
+ }
+
+ p = cfg_get_strg(label, "root");
+ if (p) {
+ strcpy (q, "root=");
+ strcpy (q + 5, p);
+ q = strchr (q, 0);
+ *q++ = ' ';
+ }
+ if (cfg_get_flag(label, "read-only")) {
+ strcpy (q, "ro ");
+ q += 3;
+ }
+ if (cfg_get_flag(label, "read-write")) {
+ strcpy (q, "rw ");
+ q += 3;
+ }
+ p = cfg_get_strg(label, "ramdisk");
+ if (p) {
+ strcpy (q, "ramdisk=");
+ strcpy (q + 8, p);
+ q = strchr (q, 0);
+ *q++ = ' ';
+ }
+ p = cfg_get_strg(label, "initrd-size");
+ if (p) {
+ strcpy (q, "ramdisk_size=");
+ strcpy (q + 13, p);
+ q = strchr (q, 0);
+ *q++ = ' ';
+ }
+ if (cfg_get_flag(label, "novideo")) {
+ strcpy (q, "video=ofonly");
+ q = strchr (q, 0);
+ *q++ = ' ';
+ }
+ p = cfg_get_strg (label, "append");
+ if (p) {
+ strcpy (q, p);
+ q = strchr (q, 0);
+ *q++ = ' ';
+ }
+ *q = 0;
+ if (params)
+ strcpy(q, params);
+
+ return buffer;
+}
+
+static int check_and_add_device(struct device *dev)
+{
+ if (!dev->icon_file)
+ dev->icon_file = strdup(generic_icon_file(guess_device_type()));
+
+ return !add_device(dev);
+}
+
+void process_image(char *label)
+{
+ struct boot_option opt;
+ char *cfgopt;
+
+ memset(&opt, 0, sizeof(opt));
+
+ opt.name = label;
+ cfgopt = cfg_get_strg(label, "image");
+ opt.boot_image_file = resolve_path(cfgopt, devpath);
+ if (cfgopt == defimage)
+ pb_log("This one is default. What do we do about it?\n");
+
+ cfgopt = cfg_get_strg(label, "initrd");
+ if (cfgopt)
+ opt.initrd_file = resolve_path(cfgopt, devpath);
+
+ opt.boot_args = make_params(label, NULL);
+
+ add_boot_option(&opt);
+
+ if (opt.initrd_file)
+ free(opt.initrd_file);
+}
+
+static int yaboot_parse(const char *device)
+{
+ char *filepath;
+ char *conf_file;
+ char *tmpstr;
+ ssize_t conf_len;
+ int fd;
+ struct stat st;
+ char *label;
+
+ devpath = strdup(device);
+
+ filepath = resolve_path("/etc/yaboot.conf", devpath);
+
+ fd = open(filepath, O_RDONLY);
+ if (fd < 0) {
+ free(filepath);
+ filepath = resolve_path("/yaboot.conf", devpath);
+ fd = open(filepath, O_RDONLY);
+
+ if (fd < 0)
+ return 0;
+ }
+
+ if (fstat(fd, &st)) {
+ close(fd);
+ return 0;
+ }
+
+ conf_file = malloc(st.st_size+1);
+ if (!conf_file) {
+ close(fd);
+ return 0;
+ }
+
+ conf_len = read(fd, conf_file, st.st_size);
+ if (conf_len < 0) {
+ close(fd);
+ return 0;
+ }
+ conf_file[conf_len] = 0;
+
+ close(fd);
+
+ if (cfg_parse(filepath, conf_file, conf_len)) {
+ pb_log("Error parsing yaboot.conf\n");
+ return 0;
+ }
+
+ free(filepath);
+
+ dev = malloc(sizeof(*dev));
+ memset(dev, 0, sizeof(*dev));
+ dev->id = strdup(devpath);
+ if (cfg_get_strg(0, "init-message")) {
+ char *newline;
+ dev->description = strdup(cfg_get_strg(0, "init-message"));
+ newline = strchr(dev->description, '\n');
+ if (newline)
+ *newline = 0;
+ }
+ dev->icon_file = strdup(generic_icon_file(guess_device_type()));
+
+ /* If we have a 'partiton=' directive, update the default devpath
+ * to use that instead of the current device */
+ tmpstr = cfg_get_strg(0, "partition");
+ if (tmpstr) {
+ char *endp;
+ int partnr = strtol(tmpstr, &endp, 10);
+ if (endp != tmpstr && !*endp) {
+ char *new_dev, *tmp;
+
+ new_dev = malloc(strlen(devpath) + strlen(tmpstr) + 1);
+ if (!new_dev)
+ return 0;
+
+ strcpy(new_dev, devpath);
+
+ /* Strip digits (partition number) from string */
+ endp = new_dev + strlen(devpath) - 1;
+ while (isdigit(*endp))
+ *(endp--) = 0;
+
+ /* and add our own... */
+ sprintf(endp + 1, "%d", partnr);
+
+ tmp = devpath;
+ devpath = parse_device_path(new_dev, devpath);
+ free(tmp);
+ free(new_dev);
+ }
+ }
+
+ defimage = cfg_get_default();
+ if (!defimage)
+ return 0;
+ defimage = cfg_get_strg(defimage, "image");
+
+ label = cfg_next_image(NULL);
+ if (!label || !check_and_add_device(dev))
+ return 0;
+
+ do {
+ process_image(label);
+ } while ((label = cfg_next_image(label)));
+
+ return 1;
+}
+
+struct parser yaboot_parser = {
+ .name = "yaboot.conf parser",
+ .priority = 99,
+ .parse = yaboot_parse
+};
OpenPOWER on IntegriCloud