--- /dev/null
+/*
+ * 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>
+#include <libio.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);
+}
+
+inline 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:
+ */
--- /dev/null
+
+#include "udev-helper.h"
+#include "params.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 const char *mountpoint;
+static char partition_mntpoint[PATH_MAX];
+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 char *prepend_mountpoint(const char *path)
+{
+ char *full_path;
+
+ full_path = malloc(strlen(path) + strlen(mountpoint) + 2);
+
+ strcpy(full_path, mountpoint);
+ if (path[0] != '/')
+ strcat(full_path, "/");
+ strcat(full_path, path);
+
+ return full_path;
+}
+
+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 = prepend_mountpoint(cfgopt);
+ if (cfgopt == defimage)
+ printf("This one is default. What do we do about it?\n");
+
+ cfgopt = cfg_get_strg(label, "initrd");
+ if (cfgopt)
+ opt.initrd_file = prepend_mountpoint(cfgopt);
+
+ 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 *devicepath, const char *_mountpoint)
+{
+ char *filepath;
+ char *conf_file;
+ char *tmpstr;
+ ssize_t conf_len;
+ int fd;
+ struct stat st;
+ char *label;
+
+ mountpoint = _mountpoint;
+
+ filepath = prepend_mountpoint("/etc/yaboot.conf");
+
+ fd = open(filepath, O_RDONLY);
+ if (fd < 0) {
+ free(filepath);
+ filepath = prepend_mountpoint("/yaboot.conf");
+ 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)) {
+ printf("Error parsing yaboot.conf\n");
+ return 0;
+ }
+
+ free(filepath);
+
+ dev = malloc(sizeof(*dev));
+ memset(dev, 0, sizeof(*dev));
+ dev->id = strdup(devicepath);
+ 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()));
+
+ /* Mount the 'partition' which is what all the image filenames
+ are relative to */
+ tmpstr = cfg_get_strg(0, "partition");
+ if (tmpstr) {
+ char *endp;
+ int partnr = strtol(tmpstr, &endp, 10);
+ if (endp != tmpstr && !*endp) {
+ char *new_dev = malloc(strlen(devicepath) + strlen(tmpstr) + 1);
+ if (!new_dev)
+ return 0;
+
+ strcpy(new_dev, devicepath);
+
+ /* Strip digits (partition number) from string */
+ endp = &new_dev[strlen(devicepath) - 1];
+ while (isdigit(*endp))
+ *(endp--) = 0;
+
+ /* and add our own... */
+ sprintf(endp+1, "%d", partnr);
+
+ /* FIXME: udev may not have created the device node
+ yet. And on removal, unmount_device() only unmounts
+ it once, while in fact it may be mounted twice. */
+ if (mount_device(new_dev, partition_mntpoint)) {
+ printf("Error mounting image partition\n");
+ return 0;
+ }
+ mountpoint = partition_mntpoint;
+ dev->id = 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
+};