From: David Woodhouse Date: Mon, 2 Apr 2007 06:30:49 +0000 (+1000) Subject: Add some basic yaboot.conf parsing support X-Git-Tag: v0.0.1~35 X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=commitdiff_plain;h=747a0d462cf02ec2b6649f5a3d9b759424d793f8;ds=sidebyside Add some basic yaboot.conf parsing support Signed-off-by: Benjamin Herrenschmidt --- diff --git a/Makefile b/Makefile index 64a8c46..642850d 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ TWIN_LDFLAGS=$(shell pkg-config --libs libtwin) LDFLAGS = CFLAGS = -O0 -ggdb -Wall '-DPREFIX="$(PREFIX)"' -PARSERS = native +PARSERS = native yaboot ARTWORK = background.png cdrom.png hdd.png usbpen.png cursor all: petitboot udev-helper @@ -20,7 +20,7 @@ petitboot: petitboot.o devices.o petitboot: LDFLAGS+=$(TWIN_LDFLAGS) petitboot: CFLAGS+=$(TWIN_CFLAGS) -udev-helper: devices/udev-helper.o devices/params.o \ +udev-helper: devices/udev-helper.o devices/params.o devices/yaboot-cfg.o \ $(foreach p,$(PARSERS),devices/$(p)-parser.o) $(CC) $(LDFLAGS) -o $@ $^ diff --git a/devices/native-parser.c b/devices/native-parser.c index f47fdca..bb1ca51 100644 --- a/devices/native-parser.c +++ b/devices/native-parser.c @@ -102,6 +102,7 @@ static int parameter(char *param_name, char *param_value) int parse(const char *devicepath, const char *_mountpoint) { char *filepath; + int rc; mountpoint = _mountpoint; @@ -112,7 +113,9 @@ int parse(const char *devicepath, const char *_mountpoint) memset(dev, 0, sizeof(*dev)); dev->id = strdup(devicepath); - pm_process(filepath, section, parameter); + rc = pm_process(filepath, section, parameter); + if (!rc) + return 0; if (cur_opt) { add_boot_option(cur_opt); diff --git a/devices/udev-helper.c b/devices/udev-helper.c index ade9787..a3f47a5 100644 --- a/devices/udev-helper.c +++ b/devices/udev-helper.c @@ -17,12 +17,14 @@ #include "petitboot-paths.h" extern struct parser native_parser; +extern struct parser yaboot_parser; static FILE *logf; static int sock; /* array of parsers, ordered by priority */ static struct parser *parsers[] = { &native_parser, + &yaboot_parser, NULL }; @@ -48,13 +50,14 @@ static void print_boot_option(const struct boot_option *opt) log("\tname: %s\n", opt->name); log("\tdescription: %s\n", opt->description); log("\tboot_image: %s\n", opt->boot_image_file); + log("\tinitrd: %s\n", opt->initrd_file); log("\tboot_args: %s\n", opt->boot_args); } static void print_device(const struct device *dev) { - log("\tid: %s\n", dev->name); + log("\tid: %s\n", dev->id); log("\tname: %s\n", dev->name); log("\tdescription: %s\n", dev->description); log("\tboot_image: %s\n", dev->icon_file); @@ -221,16 +224,21 @@ int connect_to_socket() #endif } -#define template "mnt-XXXXXX" - -static int mount_device(const char *dev_path, char *mount_path) +int mount_device(const char *dev_path, char *mount_path) { char *dir; + const char *basename; int pid, status, rc = -1; - /* create a unique mountpoint */ - dir = malloc(strlen(TMP_DIR) + 2 + strlen(template)); - sprintf(dir, "%s/%s", TMP_DIR, template); + basename = strrchr(dev_path, '/'); + if (basename) + basename++; + else + basename = dev_path; + + /* create a unique mountpoint */ + dir = malloc(strlen(TMP_DIR) + 13 + strlen(basename)); + sprintf(dir, "%s/mnt-%s-XXXXXX", TMP_DIR, basename); if (!mkdtemp(dir)) { log("failed to create temporary directory in %s: %s", @@ -414,7 +422,9 @@ int main(int argc, char **argv) remove_device(dev_path); - unmount_device(dev_path); + /* Unmount it repeatedly, if needs be */ + while (!unmount_device(dev_path)) + ; } else { log("invalid action '%s'\n", action); diff --git a/devices/udev-helper.h b/devices/udev-helper.h index ea5400c..10e5d2b 100644 --- a/devices/udev-helper.h +++ b/devices/udev-helper.h @@ -6,6 +6,8 @@ int add_device(const struct device *dev); int add_boot_option(const struct boot_option *opt); void free_boot_option(struct boot_option *opt); +int mount_device(const char *dev_path, char *mount_path); + struct parser { char *name; int priority; diff --git a/devices/yaboot-cfg.c b/devices/yaboot-cfg.c new file mode 100644 index 0000000..a252bdb --- /dev/null +++ b/devices/yaboot-cfg.c @@ -0,0 +1,492 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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: + */ diff --git a/devices/yaboot-cfg.h b/devices/yaboot-cfg.h new file mode 100644 index 0000000..2ab4fec --- /dev/null +++ b/devices/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/devices/yaboot-parser.c b/devices/yaboot-parser.c new file mode 100644 index 0000000..ee1b992 --- /dev/null +++ b/devices/yaboot-parser.c @@ -0,0 +1,252 @@ + +#include "udev-helper.h" +#include "params.h" +#include "yaboot-cfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +};