From: Jeremy Kerr Date: Mon, 16 Sep 2013 07:12:13 +0000 (+0800) Subject: discover/grub2: Hook up flex/bison parser to discover server X-Git-Tag: v1.0.0~460 X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=commitdiff_plain;h=2ea5eb23b027519372dd20fbe8f958c06ac2aa6c discover/grub2: Hook up flex/bison parser to discover server Signed-off-by: Jeremy Kerr --- diff --git a/configure.ac.in b/configure.ac.in index be372a6..7f13cca 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -23,6 +23,8 @@ AC_PREFIX_DEFAULT([/usr/local]) AS_IF([test "x$CFLAGS" = "x"], [AC_SUBST([CFLAGS], [""])]) AC_PROG_CC +AC_PROG_LEX +AC_PROG_YACC AC_PROG_INSTALL AM_INIT_AUTOMAKE @@ -230,6 +232,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile discover/Makefile + discover/grub2/Makefile lib/Makefile man/Makefile test/Makefile diff --git a/discover/Makefile.am b/discover/Makefile.am index 1dd63f1..848816f 100644 --- a/discover/Makefile.am +++ b/discover/Makefile.am @@ -14,6 +14,8 @@ pkgsysconfdir = @sysconfdir@/@PACKAGE@ +SUBDIRS = grub2 + AM_CPPFLAGS = -I$(top_srcdir)/lib $(DEFAULT_CPPFLAGS) AM_CFLAGS = $(DEFAULT_CFLAGS) \ @@ -58,11 +60,10 @@ pb_discover_SOURCES = \ user-event.c \ user-event.h \ kboot-parser.c \ - grub2-parser.c \ yaboot-parser.c \ pxe-parser.c -pb_discover_LDADD = $(top_builddir)/lib/libpbcore.la +pb_discover_LDADD = $(top_builddir)/lib/libpbcore.la grub2/grub2-parser.ro pb_discover_LDFLAGS = -ludev diff --git a/discover/grub2-parser.c b/discover/grub2-parser.c deleted file mode 100644 index 630ea33..0000000 --- a/discover/grub2-parser.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright Geoff Levand - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if defined(HAVE_CONFIG_H) -#include "config.h" -#endif - -#define _GNU_SOURCE - -#include -#include -#include - -#include "log/log.h" -#include "talloc/talloc.h" -#include "types/types.h" -#include "parser-conf.h" -#include "parser-utils.h" -#include "paths.h" -#include "resource.h" - -struct grub2_root { - char *uuid; -}; - -struct grub2_state { - struct discover_boot_option *opt; - int default_idx; - int cur_idx; - char *desc_image; - char *desc_initrd; - const char *const *known_names; - struct grub2_root *root; -}; - -struct grub2_resource_info { - struct grub2_root *root; - char *path; -}; - -/* we use slightly different resources for grub2 */ -static struct resource *create_grub2_resource(void *ctx, - struct discover_device *orig_device, - struct grub2_root *root, const char *path) -{ - struct grub2_resource_info *info; - struct resource *res; - - res = talloc(ctx, struct resource); - - if (root) { - info = talloc(res, struct grub2_resource_info); - info->root = root; - talloc_reference(info, root); - info->path = talloc_strdup(info, path); - - res->resolved = false; - res->info = info; - - } else - resolve_resource_against_device(res, orig_device, path); - - return res; -} - -static bool resolve_grub2_resource(struct device_handler *handler, - struct resource *res) -{ - struct grub2_resource_info *info = res->info; - struct discover_device *dev; - - assert(!res->resolved); - - dev = device_lookup_by_uuid(handler, info->root->uuid); - - if (!dev) - return false; - - resolve_resource_against_device(res, dev, info->path); - talloc_free(info); - - return true; -} - -static bool current_option_is_default(struct grub2_state *state) -{ - if (state->default_idx < 0) - return false; - return state->cur_idx == state->default_idx; -} - -static void grub2_finish(struct conf_context *conf) -{ - struct device *dev = conf->dc->device->device; - struct grub2_state *state = conf->parser_info; - struct boot_option *opt; - - if (!state->desc_image) { - pb_log("%s: %s: no image found\n", __func__, dev->id); - return; - } - - assert(state->opt); - opt = state->opt->option; - - assert(opt); - assert(opt->name); - assert(opt->boot_args); - - opt->description = talloc_asprintf(opt, "%s %s %s", - state->desc_image, - (state->desc_initrd ? state->desc_initrd : ""), - opt->boot_args); - - talloc_free(state->desc_initrd); - state->desc_initrd = NULL; - - conf_strip_str(opt->boot_args); - conf_strip_str(opt->description); - - state->opt->option->is_default = current_option_is_default(state); - - discover_context_add_boot_option(conf->dc, state->opt); - - state->opt = NULL; - state->cur_idx++; -} - -static void grub2_process_pair(struct conf_context *conf, const char *name, - char *value) -{ - struct device *dev = conf->dc->device->device; - struct grub2_state *state = conf->parser_info; - struct discover_boot_option *opt = state->opt; - - if (!name || !conf_param_in_list(state->known_names, name)) - return; - - if (streq(name, "menuentry")) { - /* complete any existing option... */ - if (state->opt) - grub2_finish(conf); - - /* ... then start the new one */ - opt = discover_boot_option_create(conf->dc, conf->dc->device); - opt->option->boot_args = talloc_strdup(opt->option, ""); - - value = strtok(value, "\'{\""); - - opt->option->id = talloc_asprintf(opt->option, - "%s#%s", dev->id, value); - opt->option->name = talloc_strdup(opt->option, value); - opt->option->boot_args = talloc_strdup(opt, ""); - - state->opt = opt; - - return; - } - - if (streq(name, "linux") || streq(name, "linux16")) { - char *sep; - - sep = strchr(value, ' '); - - if (sep) - *sep = 0; - - opt->boot_image = create_grub2_resource(opt, conf->dc->device, - state->root, value); - state->desc_image = talloc_strdup(opt, value); - - if (sep) - opt->option->boot_args = talloc_strdup(opt, sep + 1); - - return; - } - - if (streq(name, "initrd")) { - opt->initrd = create_grub2_resource(opt, conf->dc->device, - state->root, value); - state->desc_initrd = talloc_asprintf(state, "initrd=%s", - value); - return; - } - - if (streq(name, "search")) { - struct grub2_root *root; - char *uuid; - - if (!strstr(value, "--set=root")) { - pb_log("%s: no root\n", __func__); - return; - } - - /* The UUID should be the last argument to the search command. - * FIXME: this is a little fragile; would be nice to have some - * parser helpers to deal with "command args" parsing - */ - uuid = strrchr(value, ' '); - if (!uuid) - return; - - uuid++; - - if (state->root) - talloc_unlink(state, state->root); - - root = talloc(state, struct grub2_root); - root->uuid = talloc_strdup(root, uuid); - state->root = root; - return; - } - - if (streq(name, "set")) { - char *sep, *var_name, *var_value; - - /* this is pretty nasty, but works until we implement a proper - * parser... */ - - sep = strchr(value, '='); - if (!sep) - return; - - *sep = '\0'; - - var_name = value; - var_value = sep + 1; - if (var_value[0] == '"' || var_value[0] == '\'') - var_value++; - - if (!strlen(var_name) || !strlen(var_value)) - return; - - if (streq(var_name, "default")) - state->default_idx = atoi(var_value); - - return; - } - - pb_log("%s: unknown name: %s\n", __func__, name); -} - -static const char *const grub2_conf_files[] = { - "/grub.cfg", - "/menu.lst", - "/grub/grub.cfg", - "/grub2/grub.cfg", - "/grub/menu.lst", - "/boot/grub/grub.cfg", - "/boot/grub2/grub.cfg", - "/boot/grub/menu.lst", - "/GRUB.CFG", - "/MENU.LST", - "/GRUB/GRUB.CFG", - "/GRUB2/GRUB.CFG", - "/GRUB/MENU.LST", - "/BOOT/GRUB/GRUB.CFG", - "/BOOT/GRUB/MENU.LST", - NULL -}; - -static const char *grub2_known_names[] = { - "menuentry", - "linux", - "linux16", - "initrd", - "search", - "set", - NULL -}; - -static int grub2_parse(struct discover_context *dc, char *buf, int len) -{ - struct conf_context *conf; - struct grub2_state *state; - - conf = talloc_zero(dc, struct conf_context); - - if (!conf) - return 0; - - conf->dc = dc; - conf_init_global_options(conf); - conf->get_pair = conf_get_pair_space; - conf->process_pair = grub2_process_pair; - conf->finish = grub2_finish; - conf->parser_info = state = talloc_zero(conf, struct grub2_state); - - state->known_names = grub2_known_names; - state->default_idx = -1; - - conf_parse_buf(conf, buf, len); - - talloc_free(conf); - return 1; -} - -static struct parser grub2_parser = { - .name = "grub2", - .method = CONF_METHOD_LOCAL_FILE, - .parse = grub2_parse, - .filenames = grub2_conf_files, - .resolve_resource = resolve_grub2_resource, -}; - -register_parser(grub2_parser); diff --git a/discover/grub2/Makefile.am b/discover/grub2/Makefile.am new file mode 100644 index 0000000..71f3282 --- /dev/null +++ b/discover/grub2/Makefile.am @@ -0,0 +1,48 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir) $(DEFAULT_CPPFLAGS) + +AM_CFLAGS = $(DEFAULT_CFLAGS) \ + -DPREFIX='"$(prefix)"' \ + -DPKG_SHARE_DIR='"$(pkgdatadir)"' \ + -DPKG_SYSCONF_DIR='"$(pkgsysconfdir)"' \ + -DLOCAL_STATE_DIR='"$(localstatedir)"' + +AM_YFLAGS = -d + +noinst_PROGRAMS = grub2-parser.ro + +grub2-parser.ro$(EXEEXT): $(grub2_parser_ro_OBJECTS) + $(LD) -r -o $@ $^ + + +grub2_parser_ro_SOURCES = \ + builtins.c \ + grub2.h \ + grub2.c \ + lexer.l \ + parser.y \ + script.c \ + parser.c + +BUILT_SOURCES = parser.h lexer.h + +# ylwrap doesn't handle flex header files well; use our own rule here. +lexer.h lexer.c: lexer.l + $(LEX) $(LFLAGS) --header-file=lexer.h -o lexer.c $^ + +lexer.o: CFLAGS+=-Wno-unused-parameter -Wno-missing-prototypes \ + -Wno-missing-declarations + diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c new file mode 100644 index 0000000..ebc6ac7 --- /dev/null +++ b/discover/grub2/grub2.c @@ -0,0 +1,103 @@ + +#include + +#include + +#include +#include +#include + +#include "grub2.h" +#include "parser.h" +#include "lexer.h" + +static const char *const grub2_conf_files[] = { + "/grub.cfg", + "/menu.lst", + "/grub/grub.cfg", + "/grub2/grub.cfg", + "/grub/menu.lst", + "/boot/grub/grub.cfg", + "/boot/grub2/grub.cfg", + "/boot/grub/menu.lst", + "/GRUB.CFG", + "/MENU.LST", + "/GRUB/GRUB.CFG", + "/GRUB2/GRUB.CFG", + "/GRUB/MENU.LST", + "/BOOT/GRUB/GRUB.CFG", + "/BOOT/GRUB/MENU.LST", + NULL +}; + +struct grub2_resource_info { + struct grub2_root *root; + char *path; +}; + +/* we use slightly different resources for grub2 */ +struct resource *create_grub2_resource(void *ctx, + struct discover_device *orig_device, + struct grub2_root *root, const char *path) +{ + struct grub2_resource_info *info; + struct resource *res; + + res = talloc(ctx, struct resource); + + if (root) { + info = talloc(res, struct grub2_resource_info); + info->root = root; + talloc_reference(info, root); + info->path = talloc_strdup(info, path); + + res->resolved = false; + res->info = info; + + } else + resolve_resource_against_device(res, orig_device, path); + + return res; +} + +bool resolve_grub2_resource(struct device_handler *handler, + struct resource *res) +{ + struct grub2_resource_info *info = res->info; + struct discover_device *dev; + + assert(!res->resolved); + + dev = device_lookup_by_uuid(handler, info->root->uuid); + + if (!dev) + return false; + + resolve_resource_against_device(res, dev, info->path); + talloc_free(info); + + return true; +} + +static int grub2_parse(struct discover_context *dc, char *buf, int len) +{ + struct grub2_parser *parser; + + parser = grub2_parser_create(dc); + + grub2_parser_parse(parser, buf, len); + + talloc_free(parser); + + return 1; +} + +static struct parser grub2_parser = { + .name = "grub2", + .method = CONF_METHOD_LOCAL_FILE, + .parse = grub2_parse, + .filenames = grub2_conf_files, + .resolve_resource = resolve_grub2_resource, +}; + +register_parser(grub2_parser); diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h index 9da5cee..149e1e1 100644 --- a/discover/grub2/grub2.h +++ b/discover/grub2/grub2.h @@ -1,6 +1,8 @@ #ifndef GRUB2_H #define GRUB2_H +#include + #include #include @@ -88,6 +90,10 @@ struct grub2_parser { struct grub2_script *script; }; +struct grub2_root { + char *uuid; +}; + struct grub2_statements *create_statements(struct grub2_parser *parser); struct grub2_statement *create_statement_simple(struct grub2_parser *parser, @@ -143,5 +149,17 @@ struct grub2_command *script_lookup_command(struct grub2_script *script, const char *name); void register_builtins(struct grub2_script *script); + +/* resources */ +struct resource *create_grub2_resource(void *ctx, + struct discover_device *orig_device, + struct grub2_root *root, const char *path); + +bool resolve_grub2_resource(struct device_handler *handler, + struct resource *res); + +/* external parser api */ +struct grub2_parser *grub2_parser_create(void *ctx); +void grub2_parser_parse(struct grub2_parser *parser, char *buf, int len); #endif /* GRUB2_H */ diff --git a/discover/grub2/lexer.l b/discover/grub2/lexer.l index ab26f4b..5a4447b 100644 --- a/discover/grub2/lexer.l +++ b/discover/grub2/lexer.l @@ -6,6 +6,7 @@ %} %option nounput noinput +%option batch never-interactive %option warn %option noyywrap %option stack noyy_top_state diff --git a/discover/grub2/parser.y b/discover/grub2/parser.y index 0d02bc1..f49cecd 100644 --- a/discover/grub2/parser.y +++ b/discover/grub2/parser.y @@ -220,3 +220,28 @@ void word_append(struct grub2_word *w1, struct grub2_word *w2) w1->last->next = w2; w1->last = w2; } + +struct grub2_parser *grub2_parser_create(void *ctx) +{ + struct grub2_parser *parser; + + parser = talloc(ctx, struct grub2_parser); + yylex_init_extra(parser, &parser->scanner); + parser->script = create_script(parser); + + return parser; +} + +void grub2_parser_parse(struct grub2_parser *parser, char *buf, int len) +{ + YY_BUFFER_STATE bufstate; + + bufstate = yy_scan_bytes(buf, len - 1, parser->scanner); + + yyparse(parser); + + yy_delete_buffer(bufstate, parser->scanner); + + script_execute(parser->script); +} + diff --git a/discover/grub2/script.c b/discover/grub2/script.c index fe65e2d..9cc0cfb 100644 --- a/discover/grub2/script.c +++ b/discover/grub2/script.c @@ -190,7 +190,7 @@ static void process_expansions(struct grub2_script *script, argv->argv[i++] = word->text; } -int statements_execute(struct grub2_script *script, +static int statements_execute(struct grub2_script *script, struct grub2_statements *stmts) { struct grub2_statement *stmt; diff --git a/discover/parser.c b/discover/parser.c index 6896075..7d9cd0d 100644 --- a/discover/parser.c +++ b/discover/parser.c @@ -125,6 +125,8 @@ void __register_parser(struct parser *parser) { struct p_item* i = talloc(NULL, struct p_item); + printf("%s: %s\n", __func__, parser->name); + i->parser = parser; list_add(&parsers, &i->list); } diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am index a46388c..1a5e43a 100644 --- a/test/parser/Makefile.am +++ b/test/parser/Makefile.am @@ -58,7 +58,6 @@ extract_config = $(srcdir)/extract-config.awk parser_objs = \ $(top_srcdir)/discover/yaboot-parser.c \ $(top_srcdir)/discover/kboot-parser.c \ - $(top_srcdir)/discover/grub2-parser.c \ $(top_srcdir)/discover/pxe-parser.c \ $(top_srcdir)/discover/resource.c \ $(top_srcdir)/discover/paths.c \ @@ -72,7 +71,10 @@ libtest_ro_SOURCES = \ parser-test.h \ $(parser_objs) -libtest.ro$(EXEEXT): $(libtest_ro_OBJECTS) +libtest_ro_LDADD = \ + ../../discover/grub2/grub2-parser.ro + +libtest.ro$(EXEEXT): $(libtest_ro_OBJECTS) $(libtest_ro_LDADD) $(LD) -o $@ -r $^ check_PROGRAMS = $(TESTS) libtest.ro